123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #ifndef js_HeapAPI_h
- #define js_HeapAPI_h
- #include <limits.h>
- #include "jspubtd.h"
- #include "js/TraceKind.h"
- #include "js/Utility.h"
- /* These values are private to the JS engine. */
- namespace js {
- JS_FRIEND_API(bool)
- CurrentThreadCanAccessZone(JS::Zone* zone);
- namespace gc {
- struct Cell;
- const size_t ArenaShift = 12;
- const size_t ArenaSize = size_t(1) << ArenaShift;
- const size_t ArenaMask = ArenaSize - 1;
- #ifdef JS_GC_SMALL_CHUNK_SIZE
- const size_t ChunkShift = 18;
- #else
- const size_t ChunkShift = 20;
- #endif
- const size_t ChunkSize = size_t(1) << ChunkShift;
- const size_t ChunkMask = ChunkSize - 1;
- const size_t CellShift = 3;
- const size_t CellSize = size_t(1) << CellShift;
- const size_t CellMask = CellSize - 1;
- /* These are magic constants derived from actual offsets in gc/Heap.h. */
- #ifdef JS_GC_SMALL_CHUNK_SIZE
- const size_t ChunkMarkBitmapOffset = 258104;
- const size_t ChunkMarkBitmapBits = 31744;
- #else
- const size_t ChunkMarkBitmapOffset = 1032352;
- const size_t ChunkMarkBitmapBits = 129024;
- #endif
- const size_t ChunkRuntimeOffset = ChunkSize - sizeof(void*);
- const size_t ChunkTrailerSize = 2 * sizeof(uintptr_t) + sizeof(uint64_t);
- const size_t ChunkLocationOffset = ChunkSize - ChunkTrailerSize;
- const size_t ChunkStoreBufferOffset = ChunkSize - ChunkTrailerSize + sizeof(uint64_t);
- const size_t ArenaZoneOffset = sizeof(size_t);
- const size_t ArenaHeaderSize = sizeof(size_t) + 2 * sizeof(uintptr_t) +
- sizeof(size_t) + sizeof(uintptr_t);
- /*
- * Live objects are marked black. How many other additional colors are available
- * depends on the size of the GCThing. Objects marked gray are eligible for
- * cycle collection.
- */
- static const uint32_t BLACK = 0;
- static const uint32_t GRAY = 1;
- /*
- * The "location" field in the Chunk trailer is a enum indicating various roles
- * of the chunk.
- */
- enum class ChunkLocation : uint32_t
- {
- Invalid = 0,
- Nursery = 1,
- TenuredHeap = 2
- };
- #ifdef JS_DEBUG
- /* When downcasting, ensure we are actually the right type. */
- extern JS_FRIEND_API(void)
- AssertGCThingHasType(js::gc::Cell* cell, JS::TraceKind kind);
- #else
- inline void
- AssertGCThingHasType(js::gc::Cell* cell, JS::TraceKind kind) {}
- #endif
- MOZ_ALWAYS_INLINE bool IsInsideNursery(const js::gc::Cell* cell);
- } /* namespace gc */
- } /* namespace js */
- namespace JS {
- struct Zone;
- /* Default size for the generational nursery in bytes. */
- const uint32_t DefaultNurseryBytes = 16 * js::gc::ChunkSize;
- /* Default maximum heap size in bytes to pass to JS_NewRuntime(). */
- const uint32_t DefaultHeapMaxBytes = 32 * 1024 * 1024;
- namespace shadow {
- struct Zone
- {
- protected:
- JSRuntime* const runtime_;
- JSTracer* const barrierTracer_; // A pointer to the JSRuntime's |gcMarker|.
- public:
- // Stack GC roots for Rooted GC pointers.
- js::RootedListHeads stackRoots_;
- template <typename T> friend class JS::Rooted;
- bool needsIncrementalBarrier_;
- Zone(JSRuntime* runtime, JSTracer* barrierTracerArg)
- : runtime_(runtime),
- barrierTracer_(barrierTracerArg),
- needsIncrementalBarrier_(false)
- {
- for (auto& stackRootPtr : stackRoots_)
- stackRootPtr = nullptr;
- }
- bool needsIncrementalBarrier() const {
- return needsIncrementalBarrier_;
- }
- JSTracer* barrierTracer() {
- MOZ_ASSERT(needsIncrementalBarrier_);
- MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_));
- return barrierTracer_;
- }
- JSRuntime* runtimeFromMainThread() const {
- MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_));
- return runtime_;
- }
- // Note: Unrestricted access to the zone's runtime from an arbitrary
- // thread can easily lead to races. Use this method very carefully.
- JSRuntime* runtimeFromAnyThread() const {
- return runtime_;
- }
- static MOZ_ALWAYS_INLINE JS::shadow::Zone* asShadowZone(JS::Zone* zone) {
- return reinterpret_cast<JS::shadow::Zone*>(zone);
- }
- };
- } /* namespace shadow */
- /**
- * A GC pointer, tagged with the trace kind.
- *
- * In general, a GC pointer should be stored with an exact type. This class
- * is for use when that is not possible because a single pointer must point
- * to several kinds of GC thing.
- */
- class JS_FRIEND_API(GCCellPtr)
- {
- public:
- // Construction from a void* and trace kind.
- GCCellPtr(void* gcthing, JS::TraceKind traceKind) : ptr(checkedCast(gcthing, traceKind)) {}
- // Automatically construct a null GCCellPtr from nullptr.
- MOZ_IMPLICIT GCCellPtr(decltype(nullptr)) : ptr(checkedCast(nullptr, JS::TraceKind::Null)) {}
- // Construction from an explicit type.
- template <typename T>
- explicit GCCellPtr(T* p) : ptr(checkedCast(p, JS::MapTypeToTraceKind<T>::kind)) { }
- explicit GCCellPtr(JSFunction* p) : ptr(checkedCast(p, JS::TraceKind::Object)) { }
- explicit GCCellPtr(JSFlatString* str) : ptr(checkedCast(str, JS::TraceKind::String)) { }
- explicit GCCellPtr(const Value& v);
- JS::TraceKind kind() const {
- JS::TraceKind traceKind = JS::TraceKind(ptr & OutOfLineTraceKindMask);
- if (uintptr_t(traceKind) != OutOfLineTraceKindMask)
- return traceKind;
- return outOfLineKind();
- }
- // Allow GCCellPtr to be used in a boolean context.
- explicit operator bool() const {
- MOZ_ASSERT(bool(asCell()) == (kind() != JS::TraceKind::Null));
- return asCell();
- }
- // Simplify checks to the kind.
- template <typename T>
- bool is() const { return kind() == JS::MapTypeToTraceKind<T>::kind; }
- // Conversions to more specific types must match the kind. Access to
- // further refined types is not allowed directly from a GCCellPtr.
- template <typename T>
- T& as() const {
- MOZ_ASSERT(kind() == JS::MapTypeToTraceKind<T>::kind);
- // We can't use static_cast here, because the fact that JSObject
- // inherits from js::gc::Cell is not part of the public API.
- return *reinterpret_cast<T*>(asCell());
- }
- // Return a pointer to the cell this |GCCellPtr| refers to, or |nullptr|.
- // (It would be more symmetrical with |to| for this to return a |Cell&|, but
- // the result can be |nullptr|, and null references are undefined behavior.)
- js::gc::Cell* asCell() const {
- return reinterpret_cast<js::gc::Cell*>(ptr & ~OutOfLineTraceKindMask);
- }
- // The CC's trace logger needs an identity that is XPIDL serializable.
- uint64_t unsafeAsInteger() const {
- return static_cast<uint64_t>(unsafeAsUIntPtr());
- }
- // Inline mark bitmap access requires direct pointer arithmetic.
- uintptr_t unsafeAsUIntPtr() const {
- MOZ_ASSERT(asCell());
- MOZ_ASSERT(!js::gc::IsInsideNursery(asCell()));
- return reinterpret_cast<uintptr_t>(asCell());
- }
- bool mayBeOwnedByOtherRuntime() const;
- private:
- static uintptr_t checkedCast(void* p, JS::TraceKind traceKind) {
- js::gc::Cell* cell = static_cast<js::gc::Cell*>(p);
- MOZ_ASSERT((uintptr_t(p) & OutOfLineTraceKindMask) == 0);
- AssertGCThingHasType(cell, traceKind);
- // Note: the OutOfLineTraceKindMask bits are set on all out-of-line kinds
- // so that we can mask instead of branching.
- MOZ_ASSERT_IF(uintptr_t(traceKind) >= OutOfLineTraceKindMask,
- (uintptr_t(traceKind) & OutOfLineTraceKindMask) == OutOfLineTraceKindMask);
- return uintptr_t(p) | (uintptr_t(traceKind) & OutOfLineTraceKindMask);
- }
- JS::TraceKind outOfLineKind() const;
- uintptr_t ptr;
- };
- inline bool
- operator==(const GCCellPtr& ptr1, const GCCellPtr& ptr2)
- {
- return ptr1.asCell() == ptr2.asCell();
- }
- inline bool
- operator!=(const GCCellPtr& ptr1, const GCCellPtr& ptr2)
- {
- return !(ptr1 == ptr2);
- }
- // Unwraps the given GCCellPtr and calls the given functor with a template
- // argument of the actual type of the pointer.
- template <typename F, typename... Args>
- auto
- DispatchTyped(F f, GCCellPtr thing, Args&&... args)
- -> decltype(f(static_cast<JSObject*>(nullptr), mozilla::Forward<Args>(args)...))
- {
- switch (thing.kind()) {
- #define JS_EXPAND_DEF(name, type, _) \
- case JS::TraceKind::name: \
- return f(&thing.as<type>(), mozilla::Forward<Args>(args)...);
- JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
- #undef JS_EXPAND_DEF
- default:
- MOZ_CRASH("Invalid trace kind in DispatchTyped for GCCellPtr.");
- }
- }
- } /* namespace JS */
- namespace js {
- namespace gc {
- namespace detail {
- static MOZ_ALWAYS_INLINE uintptr_t*
- GetGCThingMarkBitmap(const uintptr_t addr)
- {
- MOZ_ASSERT(addr);
- const uintptr_t bmap_addr = (addr & ~ChunkMask) | ChunkMarkBitmapOffset;
- return reinterpret_cast<uintptr_t*>(bmap_addr);
- }
- static MOZ_ALWAYS_INLINE void
- GetGCThingMarkWordAndMask(const uintptr_t addr, uint32_t color,
- uintptr_t** wordp, uintptr_t* maskp)
- {
- MOZ_ASSERT(addr);
- const size_t bit = (addr & js::gc::ChunkMask) / js::gc::CellSize + color;
- MOZ_ASSERT(bit < js::gc::ChunkMarkBitmapBits);
- uintptr_t* bitmap = GetGCThingMarkBitmap(addr);
- const uintptr_t nbits = sizeof(*bitmap) * CHAR_BIT;
- *maskp = uintptr_t(1) << (bit % nbits);
- *wordp = &bitmap[bit / nbits];
- }
- static MOZ_ALWAYS_INLINE JS::Zone*
- GetGCThingZone(const uintptr_t addr)
- {
- MOZ_ASSERT(addr);
- const uintptr_t zone_addr = (addr & ~ArenaMask) | ArenaZoneOffset;
- return *reinterpret_cast<JS::Zone**>(zone_addr);
- }
- static MOZ_ALWAYS_INLINE JS::shadow::Runtime*
- GetCellRuntime(const Cell* cell)
- {
- MOZ_ASSERT(cell);
- const uintptr_t addr = uintptr_t(cell);
- const uintptr_t rt_addr = (addr & ~ChunkMask) | ChunkRuntimeOffset;
- return *reinterpret_cast<JS::shadow::Runtime**>(rt_addr);
- }
- static MOZ_ALWAYS_INLINE bool
- CellIsMarkedGray(const Cell* cell)
- {
- MOZ_ASSERT(cell);
- if (js::gc::IsInsideNursery(cell))
- return false;
- uintptr_t* word, mask;
- js::gc::detail::GetGCThingMarkWordAndMask(uintptr_t(cell), js::gc::GRAY, &word, &mask);
- return *word & mask;
- }
- extern JS_PUBLIC_API(bool)
- CellIsMarkedGrayIfKnown(const Cell* cell);
- MOZ_ALWAYS_INLINE ChunkLocation GetCellLocation(const void* cell) {
- uintptr_t addr = uintptr_t(cell);
- addr &= ~js::gc::ChunkMask;
- addr |= js::gc::ChunkLocationOffset;
- return *reinterpret_cast<ChunkLocation*>(addr);
- }
- MOZ_ALWAYS_INLINE bool NurseryCellHasStoreBuffer(const void* cell) {
- uintptr_t addr = uintptr_t(cell);
- addr &= ~js::gc::ChunkMask;
- addr |= js::gc::ChunkStoreBufferOffset;
- return *reinterpret_cast<void**>(addr) != nullptr;
- }
- } /* namespace detail */
- MOZ_ALWAYS_INLINE bool
- IsInsideNursery(const js::gc::Cell* cell)
- {
- if (!cell)
- return false;
- uintptr_t addr = uintptr_t(cell);
- addr &= ~js::gc::ChunkMask;
- addr |= js::gc::ChunkLocationOffset;
- auto location = *reinterpret_cast<ChunkLocation*>(addr);
- MOZ_ASSERT(location == ChunkLocation::Nursery || location == ChunkLocation::TenuredHeap);
- return location == ChunkLocation::Nursery;
- }
- MOZ_ALWAYS_INLINE bool IsCellPointerValid(const void* cell) {
- auto addr = uintptr_t(cell);
- if (addr < ChunkSize || addr % CellSize != 0) {
- return false;
- }
- auto location = detail::GetCellLocation(cell);
- if (location == ChunkLocation::TenuredHeap) {
- return !!detail::GetGCThingZone(addr);
- }
- if (location == ChunkLocation::Nursery) {
- return detail::NurseryCellHasStoreBuffer(cell);
- }
- return false;
- }
- MOZ_ALWAYS_INLINE bool IsCellPointerValidOrNull(const void* cell) {
- if (!cell) {
- return true;
- }
- return IsCellPointerValid(cell);
- }
- } /* namespace gc */
- } /* namespace js */
- namespace JS {
- static MOZ_ALWAYS_INLINE Zone*
- GetTenuredGCThingZone(GCCellPtr thing)
- {
- MOZ_ASSERT(!js::gc::IsInsideNursery(thing.asCell()));
- return js::gc::detail::GetGCThingZone(thing.unsafeAsUIntPtr());
- }
- static MOZ_ALWAYS_INLINE Zone*
- GetStringZone(JSString* str)
- {
- return js::gc::detail::GetGCThingZone(uintptr_t(str));
- }
- extern JS_PUBLIC_API(Zone*)
- GetObjectZone(JSObject* obj);
- static MOZ_ALWAYS_INLINE bool
- GCThingIsMarkedGray(GCCellPtr thing)
- {
- if (thing.mayBeOwnedByOtherRuntime())
- return false;
- return js::gc::detail::CellIsMarkedGrayIfKnown(thing.asCell());
- }
- extern JS_PUBLIC_API(JS::TraceKind)
- GCThingTraceKind(void* thing);
- } /* namespace JS */
- namespace js {
- namespace gc {
- static MOZ_ALWAYS_INLINE bool
- IsIncrementalBarrierNeededOnTenuredGCThing(JS::shadow::Runtime* rt, const JS::GCCellPtr thing)
- {
- MOZ_ASSERT(thing);
- MOZ_ASSERT(!js::gc::IsInsideNursery(thing.asCell()));
- // TODO: I'd like to assert !isHeapBusy() here but this gets called while we
- // are tracing the heap, e.g. during memory reporting (see bug 1313318).
- MOZ_ASSERT(!rt->isHeapCollecting());
- JS::Zone* zone = JS::GetTenuredGCThingZone(thing);
- return JS::shadow::Zone::asShadowZone(zone)->needsIncrementalBarrier();
- }
- #ifdef MOZ_DEVTOOLS_SERVER
- /**
- * Create an object providing access to the garbage collector's internal notion
- * of the current state of memory (both GC heap memory and GCthing-controlled
- * malloc memory.
- */
- extern JS_PUBLIC_API(JSObject*)
- NewMemoryInfoObject(JSContext* cx);
- #endif
- } /* namespace gc */
- } /* namespace js */
- #endif /* js_HeapAPI_h */
|