LCOV - code coverage report
Current view: top level - js/public - HeapAPI.h (source / functions) Hit Total Coverage
Test: output.info Lines: 102 123 82.9 %
Date: 2017-07-14 16:53:18 Functions: 40 71 56.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #ifndef js_HeapAPI_h
       8             : #define js_HeapAPI_h
       9             : 
      10             : #include <limits.h>
      11             : 
      12             : #include "jspubtd.h"
      13             : 
      14             : #include "js/TraceKind.h"
      15             : #include "js/Utility.h"
      16             : 
      17             : /* These values are private to the JS engine. */
      18             : namespace js {
      19             : 
      20             : JS_FRIEND_API(bool)
      21             : CurrentThreadCanAccessZone(JS::Zone* zone);
      22             : 
      23             : namespace gc {
      24             : 
      25             : struct Cell;
      26             : 
      27             : const size_t ArenaShift = 12;
      28             : const size_t ArenaSize = size_t(1) << ArenaShift;
      29             : const size_t ArenaMask = ArenaSize - 1;
      30             : 
      31             : #ifdef JS_GC_SMALL_CHUNK_SIZE
      32             : const size_t ChunkShift = 18;
      33             : #else
      34             : const size_t ChunkShift = 20;
      35             : #endif
      36             : const size_t ChunkSize = size_t(1) << ChunkShift;
      37             : const size_t ChunkMask = ChunkSize - 1;
      38             : 
      39             : const size_t CellAlignShift = 3;
      40             : const size_t CellAlignBytes = size_t(1) << CellAlignShift;
      41             : const size_t CellAlignMask = CellAlignBytes - 1;
      42             : 
      43             : const size_t CellBytesPerMarkBit = CellAlignBytes;
      44             : 
      45             : /* These are magic constants derived from actual offsets in gc/Heap.h. */
      46             : #ifdef JS_GC_SMALL_CHUNK_SIZE
      47             : const size_t ChunkMarkBitmapOffset = 258104;
      48             : const size_t ChunkMarkBitmapBits = 31744;
      49             : #else
      50             : const size_t ChunkMarkBitmapOffset = 1032352;
      51             : const size_t ChunkMarkBitmapBits = 129024;
      52             : #endif
      53             : const size_t ChunkRuntimeOffset = ChunkSize - sizeof(void*);
      54             : const size_t ChunkTrailerSize = 2 * sizeof(uintptr_t) + sizeof(uint64_t);
      55             : const size_t ChunkLocationOffset = ChunkSize - ChunkTrailerSize;
      56             : const size_t ArenaZoneOffset = sizeof(size_t);
      57             : const size_t ArenaHeaderSize = sizeof(size_t) + 2 * sizeof(uintptr_t) +
      58             :                                sizeof(size_t) + sizeof(uintptr_t);
      59             : 
      60             : /*
      61             :  * Live objects are marked black or gray. Everything reachable from a JS root is
      62             :  * marked black. Objects marked gray are eligible for cycle collection.
      63             :  *
      64             :  *    BlackBit:     GrayOrBlackBit:  Color:
      65             :  *       0               0           white
      66             :  *       0               1           gray
      67             :  *       1               0           black
      68             :  *       1               1           black
      69             :  */
      70             : enum class ColorBit : uint32_t
      71             : {
      72             :     BlackBit = 0,
      73             :     GrayOrBlackBit = 1
      74             : };
      75             : 
      76             : /*
      77             :  * The "location" field in the Chunk trailer is a enum indicating various roles
      78             :  * of the chunk.
      79             :  */
      80             : enum class ChunkLocation : uint32_t
      81             : {
      82             :     Invalid = 0,
      83             :     Nursery = 1,
      84             :     TenuredHeap = 2
      85             : };
      86             : 
      87             : #ifdef JS_DEBUG
      88             : /* When downcasting, ensure we are actually the right type. */
      89             : extern JS_FRIEND_API(void)
      90             : AssertGCThingHasType(js::gc::Cell* cell, JS::TraceKind kind);
      91             : #else
      92             : inline void
      93             : AssertGCThingHasType(js::gc::Cell* cell, JS::TraceKind kind) {}
      94             : #endif
      95             : 
      96             : MOZ_ALWAYS_INLINE bool IsInsideNursery(const js::gc::Cell* cell);
      97             : 
      98             : } /* namespace gc */
      99             : } /* namespace js */
     100             : 
     101             : namespace JS {
     102             : struct Zone;
     103             : 
     104             : /* Default size for the generational nursery in bytes. */
     105             : const uint32_t DefaultNurseryBytes = 16 * js::gc::ChunkSize;
     106             : 
     107             : /* Default maximum heap size in bytes to pass to JS_NewRuntime(). */
     108             : const uint32_t DefaultHeapMaxBytes = 32 * 1024 * 1024;
     109             : 
     110             : namespace shadow {
     111             : 
     112             : struct Zone
     113             : {
     114             :     enum GCState : uint8_t {
     115             :         NoGC,
     116             :         Mark,
     117             :         MarkGray,
     118             :         Sweep,
     119             :         Finished,
     120             :         Compact
     121             :     };
     122             : 
     123             :   protected:
     124             :     JSRuntime* const runtime_;
     125             :     JSTracer* const barrierTracer_;     // A pointer to the JSRuntime's |gcMarker|.
     126             :     uint32_t needsIncrementalBarrier_;
     127             :     GCState gcState_;
     128             : 
     129          31 :     Zone(JSRuntime* runtime, JSTracer* barrierTracerArg)
     130          31 :       : runtime_(runtime),
     131             :         barrierTracer_(barrierTracerArg),
     132             :         needsIncrementalBarrier_(0),
     133          31 :         gcState_(NoGC)
     134          31 :     {}
     135             : 
     136             :   public:
     137     1436221 :     bool needsIncrementalBarrier() const {
     138     1436221 :         return needsIncrementalBarrier_;
     139             :     }
     140             : 
     141       21808 :     JSTracer* barrierTracer() {
     142       21808 :         MOZ_ASSERT(needsIncrementalBarrier_);
     143       21808 :         MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_));
     144       21808 :         return barrierTracer_;
     145             :     }
     146             : 
     147      237409 :     JSRuntime* runtimeFromActiveCooperatingThread() const {
     148      237409 :         MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_));
     149      237409 :         return runtime_;
     150             :     }
     151             : 
     152             :     // Note: Unrestricted access to the zone's runtime from an arbitrary
     153             :     // thread can easily lead to races. Use this method very carefully.
     154     5709184 :     JSRuntime* runtimeFromAnyThread() const {
     155     5709184 :         return runtime_;
     156             :     }
     157             : 
     158           0 :     GCState gcState() const { return gcState_; }
     159      407984 :     bool wasGCStarted() const { return gcState_ != NoGC; }
     160           0 :     bool isGCMarkingBlack() const { return gcState_ == Mark; }
     161           0 :     bool isGCMarkingGray() const { return gcState_ == MarkGray; }
     162      573607 :     bool isGCSweeping() const { return gcState_ == Sweep; }
     163       41601 :     bool isGCFinished() const { return gcState_ == Finished; }
     164      598153 :     bool isGCCompacting() const { return gcState_ == Compact; }
     165       44963 :     bool isGCMarking() const { return gcState_ == Mark || gcState_ == MarkGray; }
     166      102730 :     bool isGCSweepingOrCompacting() const { return gcState_ == Sweep || gcState_ == Compact; }
     167             : 
     168     1275370 :     static MOZ_ALWAYS_INLINE JS::shadow::Zone* asShadowZone(JS::Zone* zone) {
     169     1275370 :         return reinterpret_cast<JS::shadow::Zone*>(zone);
     170             :     }
     171             : };
     172             : 
     173             : } /* namespace shadow */
     174             : 
     175             : /**
     176             :  * A GC pointer, tagged with the trace kind.
     177             :  *
     178             :  * In general, a GC pointer should be stored with an exact type. This class
     179             :  * is for use when that is not possible because a single pointer must point
     180             :  * to several kinds of GC thing.
     181             :  */
     182             : class JS_FRIEND_API(GCCellPtr)
     183             : {
     184             :   public:
     185             :     // Construction from a void* and trace kind.
     186       55251 :     GCCellPtr(void* gcthing, JS::TraceKind traceKind) : ptr(checkedCast(gcthing, traceKind)) {}
     187             : 
     188             :     // Automatically construct a null GCCellPtr from nullptr.
     189           0 :     MOZ_IMPLICIT GCCellPtr(decltype(nullptr)) : ptr(checkedCast(nullptr, JS::TraceKind::Null)) {}
     190             : 
     191             :     // Construction from an explicit type.
     192             :     template <typename T>
     193      205950 :     explicit GCCellPtr(T* p) : ptr(checkedCast(p, JS::MapTypeToTraceKind<T>::kind)) { }
     194           0 :     explicit GCCellPtr(JSFunction* p) : ptr(checkedCast(p, JS::TraceKind::Object)) { }
     195             :     explicit GCCellPtr(JSFlatString* str) : ptr(checkedCast(str, JS::TraceKind::String)) { }
     196             :     explicit GCCellPtr(const Value& v);
     197             : 
     198      724368 :     JS::TraceKind kind() const {
     199      724368 :         JS::TraceKind traceKind = JS::TraceKind(ptr & OutOfLineTraceKindMask);
     200      724368 :         if (uintptr_t(traceKind) != OutOfLineTraceKindMask)
     201      722080 :             return traceKind;
     202        2288 :         return outOfLineKind();
     203             :     }
     204             : 
     205             :     // Allow GCCellPtr to be used in a boolean context.
     206      238925 :     explicit operator bool() const {
     207      238925 :         MOZ_ASSERT(bool(asCell()) == (kind() != JS::TraceKind::Null));
     208      238925 :         return asCell();
     209             :     }
     210             : 
     211             :     // Simplify checks to the kind.
     212             :     template <typename T>
     213      473894 :     bool is() const { return kind() == JS::MapTypeToTraceKind<T>::kind; }
     214             : 
     215             :     // Conversions to more specific types must match the kind. Access to
     216             :     // further refined types is not allowed directly from a GCCellPtr.
     217             :     template <typename T>
     218        4515 :     T& as() const {
     219        4515 :         MOZ_ASSERT(kind() == JS::MapTypeToTraceKind<T>::kind);
     220             :         // We can't use static_cast here, because the fact that JSObject
     221             :         // inherits from js::gc::Cell is not part of the public API.
     222        4515 :         return *reinterpret_cast<T*>(asCell());
     223             :     }
     224             : 
     225             :     // Return a pointer to the cell this |GCCellPtr| refers to, or |nullptr|.
     226             :     // (It would be more symmetrical with |to| for this to return a |Cell&|, but
     227             :     // the result can be |nullptr|, and null references are undefined behavior.)
     228     2400448 :     js::gc::Cell* asCell() const {
     229     2400448 :         return reinterpret_cast<js::gc::Cell*>(ptr & ~OutOfLineTraceKindMask);
     230             :     }
     231             : 
     232             :     // The CC's trace logger needs an identity that is XPIDL serializable.
     233           0 :     uint64_t unsafeAsInteger() const {
     234           0 :         return static_cast<uint64_t>(unsafeAsUIntPtr());
     235             :     }
     236             :     // Inline mark bitmap access requires direct pointer arithmetic.
     237      235649 :     uintptr_t unsafeAsUIntPtr() const {
     238      235649 :         MOZ_ASSERT(asCell());
     239      235649 :         MOZ_ASSERT(!js::gc::IsInsideNursery(asCell()));
     240      235649 :         return reinterpret_cast<uintptr_t>(asCell());
     241             :     }
     242             : 
     243      235651 :     MOZ_ALWAYS_INLINE bool mayBeOwnedByOtherRuntime() const {
     244      235651 :         if (is<JSString>() || is<JS::Symbol>())
     245          94 :             return mayBeOwnedByOtherRuntimeSlow();
     246      235557 :         return false;
     247             :     }
     248             : 
     249             :   private:
     250      265916 :     static uintptr_t checkedCast(void* p, JS::TraceKind traceKind) {
     251      265916 :         js::gc::Cell* cell = static_cast<js::gc::Cell*>(p);
     252      265916 :         MOZ_ASSERT((uintptr_t(p) & OutOfLineTraceKindMask) == 0);
     253      265916 :         AssertGCThingHasType(cell, traceKind);
     254             :         // Note: the OutOfLineTraceKindMask bits are set on all out-of-line kinds
     255             :         // so that we can mask instead of branching.
     256      265916 :         MOZ_ASSERT_IF(uintptr_t(traceKind) >= OutOfLineTraceKindMask,
     257             :                       (uintptr_t(traceKind) & OutOfLineTraceKindMask) == OutOfLineTraceKindMask);
     258      265916 :         return uintptr_t(p) | (uintptr_t(traceKind) & OutOfLineTraceKindMask);
     259             :     }
     260             : 
     261             :     bool mayBeOwnedByOtherRuntimeSlow() const;
     262             : 
     263             :     JS::TraceKind outOfLineKind() const;
     264             : 
     265             :     uintptr_t ptr;
     266             : };
     267             : 
     268             : inline bool
     269        5184 : operator==(const GCCellPtr& ptr1, const GCCellPtr& ptr2)
     270             : {
     271        5184 :     return ptr1.asCell() == ptr2.asCell();
     272             : }
     273             : 
     274             : inline bool
     275             : operator!=(const GCCellPtr& ptr1, const GCCellPtr& ptr2)
     276             : {
     277             :     return !(ptr1 == ptr2);
     278             : }
     279             : 
     280             : // Unwraps the given GCCellPtr and calls the given functor with a template
     281             : // argument of the actual type of the pointer.
     282             : template <typename F, typename... Args>
     283             : auto
     284        4421 : DispatchTyped(F f, GCCellPtr thing, Args&&... args)
     285             :   -> decltype(f(static_cast<JSObject*>(nullptr), mozilla::Forward<Args>(args)...))
     286             : {
     287        4421 :     switch (thing.kind()) {
     288             : #define JS_EXPAND_DEF(name, type, _) \
     289             :       case JS::TraceKind::name: \
     290             :           return f(&thing.as<type>(), mozilla::Forward<Args>(args)...);
     291           0 :       JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
     292             : #undef JS_EXPAND_DEF
     293             :       default:
     294           0 :           MOZ_CRASH("Invalid trace kind in DispatchTyped for GCCellPtr.");
     295             :     }
     296             : }
     297             : 
     298             : } /* namespace JS */
     299             : 
     300             : namespace js {
     301             : namespace gc {
     302             : namespace detail {
     303             : 
     304             : static MOZ_ALWAYS_INLINE uintptr_t*
     305     2927838 : GetGCThingMarkBitmap(const uintptr_t addr)
     306             : {
     307     2927838 :     MOZ_ASSERT(addr);
     308     2927838 :     const uintptr_t bmap_addr = (addr & ~ChunkMask) | ChunkMarkBitmapOffset;
     309     2927838 :     return reinterpret_cast<uintptr_t*>(bmap_addr);
     310             : }
     311             : 
     312             : static MOZ_ALWAYS_INLINE void
     313     2927835 : GetGCThingMarkWordAndMask(const uintptr_t addr, ColorBit colorBit,
     314             :                           uintptr_t** wordp, uintptr_t* maskp)
     315             : {
     316     2927835 :     MOZ_ASSERT(addr);
     317     2927835 :     const size_t bit = (addr & js::gc::ChunkMask) / js::gc::CellBytesPerMarkBit +
     318     2927835 :                        static_cast<uint32_t>(colorBit);
     319     2927835 :     MOZ_ASSERT(bit < js::gc::ChunkMarkBitmapBits);
     320     2927835 :     uintptr_t* bitmap = GetGCThingMarkBitmap(addr);
     321     2927848 :     const uintptr_t nbits = sizeof(*bitmap) * CHAR_BIT;
     322     2927848 :     *maskp = uintptr_t(1) << (bit % nbits);
     323     2927848 :     *wordp = &bitmap[bit / nbits];
     324     2927848 : }
     325             : 
     326             : static MOZ_ALWAYS_INLINE JS::Zone*
     327      260581 : GetGCThingZone(const uintptr_t addr)
     328             : {
     329      260581 :     MOZ_ASSERT(addr);
     330      260581 :     const uintptr_t zone_addr = (addr & ~ArenaMask) | ArenaZoneOffset;
     331      260581 :     return *reinterpret_cast<JS::Zone**>(zone_addr);
     332             : 
     333             : }
     334             : 
     335             : static MOZ_ALWAYS_INLINE bool
     336      462658 : TenuredCellIsMarkedGray(const Cell* cell)
     337             : {
     338             :     // Return true if GrayOrBlackBit is set and BlackBit is not set.
     339      462658 :     MOZ_ASSERT(cell);
     340      462658 :     MOZ_ASSERT(!js::gc::IsInsideNursery(cell));
     341             : 
     342             :     uintptr_t* grayWord, grayMask;
     343      462658 :     js::gc::detail::GetGCThingMarkWordAndMask(uintptr_t(cell), js::gc::ColorBit::GrayOrBlackBit,
     344      462658 :                                               &grayWord, &grayMask);
     345      462658 :     if (!(*grayWord & grayMask))
     346      462658 :         return false;
     347             : 
     348             :     uintptr_t* blackWord, blackMask;
     349           0 :     js::gc::detail::GetGCThingMarkWordAndMask(uintptr_t(cell), js::gc::ColorBit::BlackBit,
     350           0 :                                               &blackWord, &blackMask);
     351           0 :     return !(*blackWord & blackMask);
     352             : }
     353             : 
     354             : static MOZ_ALWAYS_INLINE bool
     355           0 : CellIsMarkedGray(const Cell* cell)
     356             : {
     357           0 :     MOZ_ASSERT(cell);
     358           0 :     if (js::gc::IsInsideNursery(cell))
     359           0 :         return false;
     360           0 :     return TenuredCellIsMarkedGray(cell);
     361             : }
     362             : 
     363             : extern JS_PUBLIC_API(bool)
     364             : CellIsMarkedGrayIfKnown(const Cell* cell);
     365             : 
     366             : #ifdef DEBUG
     367             : extern JS_PUBLIC_API(bool)
     368             : CellIsNotGray(const Cell* cell);
     369             : #endif
     370             : 
     371             : } /* namespace detail */
     372             : 
     373             : MOZ_ALWAYS_INLINE bool
     374    25978824 : IsInsideNursery(const js::gc::Cell* cell)
     375             : {
     376    25978824 :     if (!cell)
     377      365692 :         return false;
     378    25613132 :     uintptr_t addr = uintptr_t(cell);
     379    25613132 :     addr &= ~js::gc::ChunkMask;
     380    25613132 :     addr |= js::gc::ChunkLocationOffset;
     381    25613132 :     auto location = *reinterpret_cast<ChunkLocation*>(addr);
     382    25613132 :     MOZ_ASSERT(location == ChunkLocation::Nursery || location == ChunkLocation::TenuredHeap);
     383    25613132 :     return location == ChunkLocation::Nursery;
     384             : }
     385             : 
     386             : } /* namespace gc */
     387             : } /* namespace js */
     388             : 
     389             : namespace JS {
     390             : 
     391             : static MOZ_ALWAYS_INLINE Zone*
     392      232967 : GetTenuredGCThingZone(GCCellPtr thing)
     393             : {
     394      232967 :     MOZ_ASSERT(!js::gc::IsInsideNursery(thing.asCell()));
     395      232968 :     return js::gc::detail::GetGCThingZone(thing.unsafeAsUIntPtr());
     396             : }
     397             : 
     398             : static MOZ_ALWAYS_INLINE Zone*
     399           5 : GetStringZone(JSString* str)
     400             : {
     401           5 :     return js::gc::detail::GetGCThingZone(uintptr_t(str));
     402             : }
     403             : 
     404             : extern JS_PUBLIC_API(Zone*)
     405             : GetObjectZone(JSObject* obj);
     406             : 
     407             : extern JS_PUBLIC_API(Zone*)
     408             : GetValueZone(const Value& value);
     409             : 
     410             : static MOZ_ALWAYS_INLINE bool
     411           0 : GCThingIsMarkedGray(GCCellPtr thing)
     412             : {
     413           0 :     if (thing.mayBeOwnedByOtherRuntime())
     414           0 :         return false;
     415           0 :     return js::gc::detail::CellIsMarkedGrayIfKnown(thing.asCell());
     416             : }
     417             : 
     418             : extern JS_PUBLIC_API(JS::TraceKind)
     419             : GCThingTraceKind(void* thing);
     420             : 
     421             : } /* namespace JS */
     422             : 
     423             : namespace js {
     424             : namespace gc {
     425             : 
     426             : static MOZ_ALWAYS_INLINE bool
     427      232967 : IsIncrementalBarrierNeededOnTenuredGCThing(const JS::GCCellPtr thing)
     428             : {
     429      232967 :     MOZ_ASSERT(thing);
     430      232967 :     MOZ_ASSERT(!js::gc::IsInsideNursery(thing.asCell()));
     431             : 
     432             :     // TODO: I'd like to assert !CurrentThreadIsHeapBusy() here but this gets
     433             :     // called while we are tracing the heap, e.g. during memory reporting
     434             :     // (see bug 1313318).
     435      232967 :     MOZ_ASSERT(!JS::CurrentThreadIsHeapCollecting());
     436             : 
     437      232967 :     JS::Zone* zone = JS::GetTenuredGCThingZone(thing);
     438      232968 :     return JS::shadow::Zone::asShadowZone(zone)->needsIncrementalBarrier();
     439             : }
     440             : 
     441             : /**
     442             :  * Create an object providing access to the garbage collector's internal notion
     443             :  * of the current state of memory (both GC heap memory and GCthing-controlled
     444             :  * malloc memory.
     445             :  */
     446             : extern JS_PUBLIC_API(JSObject*)
     447             : NewMemoryInfoObject(JSContext* cx);
     448             : 
     449             : } /* namespace gc */
     450             : } /* namespace js */
     451             : 
     452             : #endif /* js_HeapAPI_h */

Generated by: LCOV version 1.13