summaryrefslogtreecommitdiffstats
path: root/js/src/gc/Zone.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/gc/Zone.h653
1 files changed, 653 insertions, 0 deletions
diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h
new file mode 100644
index 0000000000..ba3de7ec3e
--- /dev/null
+++ b/js/src/gc/Zone.h
@@ -0,0 +1,653 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * 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 gc_Zone_h
+#define gc_Zone_h
+
+#include "mozilla/Array.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/LinkedList.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/PodOperations.h"
+#include "mozilla/TimeStamp.h"
+
+#include "jstypes.h"
+
+#include "ds/Bitmap.h"
+#include "gc/ArenaList.h"
+#include "gc/Barrier.h"
+#include "gc/FindSCCs.h"
+#include "gc/GCMarker.h"
+#include "gc/NurseryAwareHashMap.h"
+#include "gc/Pretenuring.h"
+#include "gc/Statistics.h"
+#include "gc/ZoneAllocator.h"
+#include "js/GCHashTable.h"
+#include "js/Vector.h"
+#include "vm/AtomsTable.h"
+#include "vm/JSObject.h"
+#include "vm/JSScript.h"
+#include "vm/ShapeZone.h"
+
+namespace js {
+
+class DebugScriptMap;
+class RegExpZone;
+class WeakRefObject;
+
+namespace jit {
+class JitZone;
+} // namespace jit
+
+namespace gc {
+
+class FinalizationObservers;
+class ZoneList;
+
+using ZoneComponentFinder = ComponentFinder<JS::Zone>;
+
+struct UniqueIdGCPolicy {
+ static bool traceWeak(JSTracer* trc, Cell** keyp, uint64_t* valuep);
+};
+
+// Maps a Cell* to a unique, 64bit id.
+using UniqueIdMap = GCHashMap<Cell*, uint64_t, PointerHasher<Cell*>,
+ SystemAllocPolicy, UniqueIdGCPolicy>;
+
+template <typename T>
+class ZoneAllCellIter;
+
+template <typename T>
+class ZoneCellIter;
+
+} // namespace gc
+
+// If two different nursery strings are wrapped into the same zone, and have
+// the same contents, then deduplication may make them duplicates.
+// `DuplicatesPossible` will allow this and map both wrappers to the same (now
+// tenured) source string.
+using StringWrapperMap =
+ NurseryAwareHashMap<JSString*, JSString*, ZoneAllocPolicy,
+ DuplicatesPossible>;
+
+class MOZ_NON_TEMPORARY_CLASS ExternalStringCache {
+ static const size_t NumEntries = 4;
+ mozilla::Array<JSString*, NumEntries> entries_;
+
+ ExternalStringCache(const ExternalStringCache&) = delete;
+ void operator=(const ExternalStringCache&) = delete;
+
+ public:
+ ExternalStringCache() { purge(); }
+ void purge() { mozilla::PodArrayZero(entries_); }
+
+ MOZ_ALWAYS_INLINE JSString* lookup(const char16_t* chars, size_t len) const;
+ MOZ_ALWAYS_INLINE void put(JSString* s);
+};
+
+class MOZ_NON_TEMPORARY_CLASS FunctionToStringCache {
+ struct Entry {
+ BaseScript* script;
+ JSString* string;
+
+ void set(BaseScript* scriptArg, JSString* stringArg) {
+ script = scriptArg;
+ string = stringArg;
+ }
+ };
+ static const size_t NumEntries = 2;
+ mozilla::Array<Entry, NumEntries> entries_;
+
+ FunctionToStringCache(const FunctionToStringCache&) = delete;
+ void operator=(const FunctionToStringCache&) = delete;
+
+ public:
+ FunctionToStringCache() { purge(); }
+ void purge() { mozilla::PodArrayZero(entries_); }
+
+ MOZ_ALWAYS_INLINE JSString* lookup(BaseScript* script) const;
+ MOZ_ALWAYS_INLINE void put(BaseScript* script, JSString* string);
+};
+
+} // namespace js
+
+namespace JS {
+
+// [SMDOC] GC Zones
+//
+// A zone is a collection of compartments. Every compartment belongs to exactly
+// one zone. In Firefox, there is roughly one zone per tab along with a system
+// zone for everything else. Zones mainly serve as boundaries for garbage
+// collection. Unlike compartments, they have no special security properties.
+//
+// Every GC thing belongs to exactly one zone. GC things from the same zone but
+// different compartments can share an arena (4k page). GC things from different
+// zones cannot be stored in the same arena. The garbage collector is capable of
+// collecting one zone at a time; it cannot collect at the granularity of
+// compartments.
+//
+// GC things are tied to zones and compartments as follows:
+//
+// - JSObjects belong to a compartment and cannot be shared between
+// compartments. If an object needs to point to a JSObject in a different
+// compartment, regardless of zone, it must go through a cross-compartment
+// wrapper. Each compartment keeps track of its outgoing wrappers in a table.
+// JSObjects find their compartment via their ObjectGroup.
+//
+// - JSStrings do not belong to any particular compartment, but they do belong
+// to a zone. Thus, two different compartments in the same zone can point to a
+// JSString. When a string needs to be wrapped, we copy it if it's in a
+// different zone and do nothing if it's in the same zone. Thus, transferring
+// strings within a zone is very efficient.
+//
+// - Shapes and base shapes belong to a zone and are shared between compartments
+// in that zone where possible. Accessor shapes store getter and setter
+// JSObjects which belong to a single compartment, so these shapes and all
+// their descendants can't be shared with other compartments.
+//
+// - Scripts are also compartment-local and cannot be shared. A script points to
+// its compartment.
+//
+// - ObjectGroup and JitCode objects belong to a compartment and cannot be
+// shared. There is no mechanism to obtain the compartment from a JitCode
+// object.
+//
+// A zone remains alive as long as any GC things in the zone are alive. A
+// compartment remains alive as long as any JSObjects, scripts, shapes, or base
+// shapes within it are alive.
+//
+// We always guarantee that a zone has at least one live compartment by refusing
+// to delete the last compartment in a live zone.
+class Zone : public js::ZoneAllocator, public js::gc::GraphNodeBase<JS::Zone> {
+ public:
+ js::gc::ArenaLists arenas;
+
+ // Per-zone data for use by an embedder.
+ js::MainThreadData<void*> data;
+
+ js::MainThreadData<uint32_t> tenuredBigInts;
+
+ // Number of marked/finalized JSStrings/JSFatInlineStrings during major GC.
+ js::MainThreadOrGCTaskData<size_t> markedStrings;
+ js::MainThreadOrGCTaskData<size_t> finalizedStrings;
+
+ // When true, skip calling the metadata callback. We use this:
+ // - to avoid invoking the callback recursively;
+ // - to avoid observing lazy prototype setup (which confuses callbacks that
+ // want to use the types being set up!);
+ // - to avoid attaching allocation stacks to allocation stack nodes, which
+ // is silly
+ // And so on.
+ js::MainThreadData<bool> suppressAllocationMetadataBuilder;
+
+ // Flags permanently set when nursery allocation is disabled for this zone.
+ js::MainThreadData<bool> nurseryStringsDisabled;
+ js::MainThreadData<bool> nurseryBigIntsDisabled;
+
+ private:
+ // Flags dynamically updated based on more than one condition, including the
+ // flags above.
+ js::MainThreadOrIonCompileData<bool> allocNurseryObjects_;
+ js::MainThreadOrIonCompileData<bool> allocNurseryStrings_;
+ js::MainThreadOrIonCompileData<bool> allocNurseryBigInts_;
+
+ // Minimum Heap value which results in tenured allocation.
+ js::MainThreadData<js::gc::Heap> minObjectHeapToTenure_;
+ js::MainThreadData<js::gc::Heap> minStringHeapToTenure_;
+ js::MainThreadData<js::gc::Heap> minBigintHeapToTenure_;
+
+ public:
+ // Script side-tables. These used to be held by Realm, but are now placed
+ // here in order to allow JSScript to access them during finalize (see bug
+ // 1568245; this change in 1575350). The tables are initialized lazily by
+ // JSScript.
+ js::UniquePtr<js::ScriptCountsMap> scriptCountsMap;
+ js::UniquePtr<js::ScriptLCovMap> scriptLCovMap;
+ js::MainThreadData<js::DebugScriptMap*> debugScriptMap;
+#ifdef MOZ_VTUNE
+ js::UniquePtr<js::ScriptVTuneIdMap> scriptVTuneIdMap;
+#endif
+#ifdef JS_CACHEIR_SPEW
+ js::UniquePtr<js::ScriptFinalWarmUpCountMap> scriptFinalWarmUpCountMap;
+#endif
+
+ js::MainThreadData<js::StringStats> previousGCStringStats;
+ js::MainThreadData<js::StringStats> stringStats;
+
+#ifdef DEBUG
+ js::MainThreadData<unsigned> gcSweepGroupIndex;
+#endif
+
+ js::gc::PretenuringZone pretenuring;
+
+ private:
+ // Side map for storing unique ids for cells, independent of address.
+ js::MainThreadOrGCTaskData<js::gc::UniqueIdMap> uniqueIds_;
+
+ // Number of allocations since the most recent minor GC for this thread.
+ uint32_t tenuredAllocsSinceMinorGC_ = 0;
+
+ // Live weakmaps in this zone.
+ js::MainThreadOrGCTaskData<mozilla::LinkedList<js::WeakMapBase>>
+ gcWeakMapList_;
+
+ // The set of compartments in this zone.
+ using CompartmentVector =
+ js::Vector<JS::Compartment*, 1, js::SystemAllocPolicy>;
+ js::MainThreadOrGCTaskData<CompartmentVector> compartments_;
+
+ // All cross-zone string wrappers in the zone.
+ js::MainThreadOrGCTaskData<js::StringWrapperMap> crossZoneStringWrappers_;
+
+ // List of non-ephemeron weak containers to sweep during
+ // beginSweepingSweepGroup.
+ js::MainThreadOrGCTaskData<mozilla::LinkedList<detail::WeakCacheBase>>
+ weakCaches_;
+
+ // Mapping from not yet marked keys to a vector of all values that the key
+ // maps to in any live weak map. Separate tables for nursery and tenured
+ // keys.
+ js::MainThreadOrGCTaskData<js::gc::EphemeronEdgeTable> gcEphemeronEdges_;
+ js::MainThreadOrGCTaskData<js::gc::EphemeronEdgeTable>
+ gcNurseryEphemeronEdges_;
+
+ js::MainThreadData<js::UniquePtr<js::RegExpZone>> regExps_;
+
+ // Bitmap of atoms marked by this zone.
+ js::MainThreadOrGCTaskData<js::SparseBitmap> markedAtoms_;
+
+ // Set of atoms recently used by this Zone. Purged on GC.
+ js::MainThreadOrGCTaskData<js::AtomSet> atomCache_;
+
+ // Cache storing allocated external strings. Purged on GC.
+ js::MainThreadOrGCTaskData<js::ExternalStringCache> externalStringCache_;
+
+ // Cache for Function.prototype.toString. Purged on GC.
+ js::MainThreadOrGCTaskData<js::FunctionToStringCache> functionToStringCache_;
+
+ // Cache for Function.prototype.bind mapping an atom `name` to atom
+ // `"bound " + name`. Purged on GC.
+ using BoundPrefixCache =
+ js::HashMap<JSAtom*, JSAtom*, js::PointerHasher<JSAtom*>,
+ js::SystemAllocPolicy>;
+ js::MainThreadData<BoundPrefixCache> boundPrefixCache_;
+
+ // Information about Shapes and BaseShapes.
+ js::MainThreadData<js::ShapeZone> shapeZone_;
+
+ // Information about finalization registries, created on demand.
+ js::MainThreadOrGCTaskData<js::UniquePtr<js::gc::FinalizationObservers>>
+ finalizationObservers_;
+
+ js::MainThreadOrGCTaskData<js::jit::JitZone*> jitZone_;
+
+ // Last time at which JIT code was discarded for this zone. This is only set
+ // when JitScripts and Baseline code are discarded as well.
+ js::MainThreadData<mozilla::TimeStamp> lastDiscardedCodeTime_;
+
+ js::MainThreadData<bool> gcScheduled_;
+ js::MainThreadData<bool> gcScheduledSaved_;
+ js::MainThreadData<bool> gcPreserveCode_;
+ js::MainThreadData<bool> keepPropMapTables_;
+ js::MainThreadData<bool> wasCollected_;
+
+ // Allow zones to be linked into a list
+ js::MainThreadOrGCTaskData<Zone*> listNext_;
+ static Zone* const NotOnList;
+ friend class js::gc::ZoneList;
+
+ using KeptAliveSet =
+ JS::GCHashSet<js::HeapPtr<JSObject*>,
+ js::StableCellHasher<js::HeapPtr<JSObject*>>,
+ js::ZoneAllocPolicy>;
+ friend class js::WeakRefObject;
+ js::MainThreadOrGCTaskData<KeptAliveSet> keptObjects;
+
+ public:
+ static JS::Zone* from(ZoneAllocator* zoneAlloc) {
+ return static_cast<Zone*>(zoneAlloc);
+ }
+
+ explicit Zone(JSRuntime* rt, Kind kind = NormalZone);
+ ~Zone();
+
+ [[nodiscard]] bool init();
+
+ void destroy(JS::GCContext* gcx);
+
+ [[nodiscard]] bool findSweepGroupEdges(Zone* atomsZone);
+
+ struct DiscardOptions {
+ DiscardOptions() {}
+ bool discardBaselineCode = true;
+ bool discardJitScripts = false;
+ bool resetNurseryAllocSites = false;
+ bool resetPretenuredAllocSites = false;
+ };
+
+ void discardJitCode(JS::GCContext* gcx,
+ const DiscardOptions& options = DiscardOptions());
+
+ // Discard JIT code regardless of isPreservingCode().
+ void forceDiscardJitCode(JS::GCContext* gcx,
+ const DiscardOptions& options = DiscardOptions());
+
+ void resetAllocSitesAndInvalidate(bool resetNurserySites,
+ bool resetPretenuredSites);
+
+ void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
+ JS::CodeSizes* code, size_t* regexpZone,
+ size_t* jitZone, size_t* baselineStubsOptimized,
+ size_t* uniqueIdMap, size_t* initialPropMapTable,
+ size_t* shapeTables, size_t* atomsMarkBitmaps,
+ size_t* compartmentObjects,
+ size_t* crossCompartmentWrappersTables,
+ size_t* compartmentsPrivateData,
+ size_t* scriptCountsMapArg);
+
+ // Iterate over all cells in the zone. See the definition of ZoneCellIter
+ // in gc/GC-inl.h for the possible arguments and documentation.
+ template <typename T, typename... Args>
+ js::gc::ZoneCellIter<T> cellIter(Args&&... args) {
+ return js::gc::ZoneCellIter<T>(const_cast<Zone*>(this),
+ std::forward<Args>(args)...);
+ }
+
+ // As above, but can return about-to-be-finalised things.
+ template <typename T, typename... Args>
+ js::gc::ZoneAllCellIter<T> cellIterUnsafe(Args&&... args) {
+ return js::gc::ZoneAllCellIter<T>(const_cast<Zone*>(this),
+ std::forward<Args>(args)...);
+ }
+
+ bool hasMarkedRealms();
+
+ void scheduleGC() {
+ MOZ_ASSERT(!RuntimeHeapIsBusy());
+ gcScheduled_ = true;
+ }
+ void unscheduleGC() { gcScheduled_ = false; }
+ bool isGCScheduled() { return gcScheduled_; }
+
+ void setPreservingCode(bool preserving) { gcPreserveCode_ = preserving; }
+ bool isPreservingCode() const { return gcPreserveCode_; }
+
+ mozilla::TimeStamp lastDiscardedCodeTime() const {
+ return lastDiscardedCodeTime_;
+ }
+
+ void changeGCState(GCState prev, GCState next);
+
+ bool isCollecting() const {
+ MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtimeFromMainThread()));
+ return isCollectingFromAnyThread();
+ }
+
+ inline bool isCollectingFromAnyThread() const {
+ return needsIncrementalBarrier() || wasGCStarted();
+ }
+
+ GCState initialMarkingState() const;
+
+ bool shouldMarkInZone(js::gc::MarkColor color) const {
+ // Check whether the zone is in one or both of the MarkBlackOnly and
+ // MarkBlackAndGray states, depending on the mark color. Also check for
+ // VerifyPreBarriers when the mark color is black (we don't do any gray
+ // marking when verifying pre-barriers).
+ if (color == js::gc::MarkColor::Black) {
+ return isGCMarkingOrVerifyingPreBarriers();
+ }
+
+ return isGCMarkingBlackAndGray();
+ }
+
+ // Was this zone collected in the last GC.
+ bool wasCollected() const { return wasCollected_; }
+ void setWasCollected(bool v) { wasCollected_ = v; }
+
+ void setNeedsIncrementalBarrier(bool needs);
+ const BarrierState* addressOfNeedsIncrementalBarrier() const {
+ return &needsIncrementalBarrier_;
+ }
+
+ static constexpr size_t offsetOfNeedsIncrementalBarrier() {
+ return offsetof(Zone, needsIncrementalBarrier_);
+ }
+
+ js::jit::JitZone* getJitZone(JSContext* cx) {
+ return jitZone_ ? jitZone_ : createJitZone(cx);
+ }
+ js::jit::JitZone* jitZone() { return jitZone_; }
+
+ void prepareForCompacting();
+
+ void traceRootsInMajorGC(JSTracer* trc);
+
+ void sweepAfterMinorGC(JSTracer* trc);
+ void sweepUniqueIds();
+ void sweepCompartments(JS::GCContext* gcx, bool keepAtleastOne, bool lastGC);
+
+ // Remove dead weak maps from gcWeakMapList_ and remove entries from the
+ // remaining weak maps whose keys are dead.
+ void sweepWeakMaps(JSTracer* trc);
+
+ // Trace all weak maps in this zone. Used to update edges after a moving GC.
+ void traceWeakMaps(JSTracer* trc);
+
+ js::gc::UniqueIdMap& uniqueIds() { return uniqueIds_.ref(); }
+
+ void notifyObservingDebuggers();
+
+ void noteTenuredAlloc() { tenuredAllocsSinceMinorGC_++; }
+
+ uint32_t* addressOfTenuredAllocCount() { return &tenuredAllocsSinceMinorGC_; }
+
+ uint32_t getAndResetTenuredAllocsSinceMinorGC() {
+ uint32_t res = tenuredAllocsSinceMinorGC_;
+ tenuredAllocsSinceMinorGC_ = 0;
+ return res;
+ }
+
+ mozilla::LinkedList<js::WeakMapBase>& gcWeakMapList() {
+ return gcWeakMapList_.ref();
+ }
+
+ CompartmentVector& compartments() { return compartments_.ref(); }
+
+ js::StringWrapperMap& crossZoneStringWrappers() {
+ return crossZoneStringWrappers_.ref();
+ }
+ const js::StringWrapperMap& crossZoneStringWrappers() const {
+ return crossZoneStringWrappers_.ref();
+ }
+
+ void dropStringWrappersOnGC();
+
+ void traceWeakCCWEdges(JSTracer* trc);
+ static void fixupAllCrossCompartmentWrappersAfterMovingGC(JSTracer* trc);
+
+ void fixupAfterMovingGC();
+ void fixupScriptMapsAfterMovingGC(JSTracer* trc);
+
+ void setNurseryAllocFlags(bool allocObjects, bool allocStrings,
+ bool allocBigInts);
+
+ bool allocKindInNursery(JS::TraceKind kind) const {
+ switch (kind) {
+ case JS::TraceKind::Object:
+ return allocNurseryObjects_;
+ case JS::TraceKind::String:
+ return allocNurseryStrings_;
+ case JS::TraceKind::BigInt:
+ return allocNurseryBigInts_;
+ default:
+ MOZ_CRASH("Unsupported kind for nursery allocation");
+ }
+ }
+ bool allocNurseryObjects() const { return allocNurseryObjects_; }
+ bool allocNurseryStrings() const { return allocNurseryStrings_; }
+ bool allocNurseryBigInts() const { return allocNurseryBigInts_; }
+
+ js::gc::Heap minHeapToTenure(JS::TraceKind kind) const {
+ switch (kind) {
+ case JS::TraceKind::Object:
+ return minObjectHeapToTenure_;
+ case JS::TraceKind::String:
+ return minStringHeapToTenure_;
+ case JS::TraceKind::BigInt:
+ return minBigintHeapToTenure_;
+ default:
+ MOZ_CRASH("Unsupported kind for nursery allocation");
+ }
+ }
+
+ mozilla::LinkedList<detail::WeakCacheBase>& weakCaches() {
+ return weakCaches_.ref();
+ }
+ void registerWeakCache(detail::WeakCacheBase* cachep) {
+ weakCaches().insertBack(cachep);
+ }
+
+ void beforeClearDelegate(JSObject* wrapper, JSObject* delegate) {
+ if (needsIncrementalBarrier()) {
+ beforeClearDelegateInternal(wrapper, delegate);
+ }
+ }
+
+ void afterAddDelegate(JSObject* wrapper) {
+ if (needsIncrementalBarrier()) {
+ afterAddDelegateInternal(wrapper);
+ }
+ }
+
+ void beforeClearDelegateInternal(JSObject* wrapper, JSObject* delegate);
+ void afterAddDelegateInternal(JSObject* wrapper);
+ js::gc::EphemeronEdgeTable& gcEphemeronEdges() {
+ return gcEphemeronEdges_.ref();
+ }
+ js::gc::EphemeronEdgeTable& gcNurseryEphemeronEdges() {
+ return gcNurseryEphemeronEdges_.ref();
+ }
+
+ js::gc::EphemeronEdgeTable& gcEphemeronEdges(const js::gc::Cell* cell) {
+ return cell->isTenured() ? gcEphemeronEdges() : gcNurseryEphemeronEdges();
+ }
+
+ // Perform all pending weakmap entry marking for this zone after
+ // transitioning to weak marking mode.
+ js::gc::IncrementalProgress enterWeakMarkingMode(js::GCMarker* marker,
+ js::SliceBudget& budget);
+
+ // A set of edges from this zone to other zones used during GC to calculate
+ // sweep groups.
+ NodeSet& gcSweepGroupEdges() {
+ return gcGraphEdges; // Defined in GraphNodeBase base class.
+ }
+ bool hasSweepGroupEdgeTo(Zone* otherZone) const {
+ return gcGraphEdges.has(otherZone);
+ }
+ [[nodiscard]] bool addSweepGroupEdgeTo(Zone* otherZone) {
+ MOZ_ASSERT(otherZone->isGCMarking());
+ return gcSweepGroupEdges().put(otherZone);
+ }
+ void clearSweepGroupEdges() { gcSweepGroupEdges().clear(); }
+
+ js::RegExpZone& regExps() { return *regExps_.ref(); }
+
+ js::SparseBitmap& markedAtoms() { return markedAtoms_.ref(); }
+
+ js::AtomSet& atomCache() { return atomCache_.ref(); }
+
+ void purgeAtomCache();
+
+ js::ExternalStringCache& externalStringCache() {
+ return externalStringCache_.ref();
+ };
+
+ js::FunctionToStringCache& functionToStringCache() {
+ return functionToStringCache_.ref();
+ }
+
+ BoundPrefixCache& boundPrefixCache() { return boundPrefixCache_.ref(); }
+
+ js::ShapeZone& shapeZone() { return shapeZone_.ref(); }
+
+ bool keepPropMapTables() const { return keepPropMapTables_; }
+ void setKeepPropMapTables(bool b) { keepPropMapTables_ = b; }
+
+ void clearRootsForShutdownGC();
+ void finishRoots();
+
+ void traceScriptTableRoots(JSTracer* trc);
+
+ void clearScriptCounts(Realm* realm);
+ void clearScriptLCov(Realm* realm);
+
+ // Add the target of JS WeakRef to a kept-alive set maintained by GC.
+ // See: https://tc39.es/proposal-weakrefs/#sec-keepduringjob
+ bool keepDuringJob(HandleObject target);
+
+ void traceKeptObjects(JSTracer* trc);
+
+ // Clear the kept-alive set.
+ // See: https://tc39.es/proposal-weakrefs/#sec-clear-kept-objects
+ void clearKeptObjects();
+
+ js::gc::AllocSite* unknownAllocSite(JS::TraceKind kind) {
+ return &pretenuring.unknownAllocSite(kind);
+ }
+ js::gc::AllocSite* optimizedAllocSite() {
+ return &pretenuring.optimizedAllocSite;
+ }
+ uint32_t nurseryAllocCount(JS::TraceKind kind) const {
+ return pretenuring.nurseryAllocCount(kind);
+ }
+
+#ifdef JSGC_HASH_TABLE_CHECKS
+ void checkAllCrossCompartmentWrappersAfterMovingGC();
+ void checkStringWrappersAfterMovingGC();
+
+ // Assert that the UniqueId table has been redirected successfully.
+ void checkUniqueIdTableAfterMovingGC();
+
+ void checkScriptMapsAfterMovingGC();
+#endif
+
+#ifdef DEBUG
+ // For testing purposes, return the index of the sweep group which this zone
+ // was swept in in the last GC.
+ unsigned lastSweepGroupIndex() { return gcSweepGroupIndex; }
+#endif
+
+ private:
+ js::jit::JitZone* createJitZone(JSContext* cx);
+
+ bool isQueuedForBackgroundSweep() { return isOnList(); }
+
+ void sweepEphemeronTablesAfterMinorGC();
+
+ js::gc::FinalizationObservers* finalizationObservers() {
+ return finalizationObservers_.ref().get();
+ }
+ bool ensureFinalizationObservers();
+
+ bool isOnList() const;
+ Zone* nextZone() const;
+
+ friend bool js::CurrentThreadCanAccessZone(Zone* zone);
+ friend class js::gc::GCRuntime;
+};
+
+} // namespace JS
+
+namespace js {
+namespace gc {
+const char* StateName(JS::Zone::GCState state);
+} // namespace gc
+} // namespace js
+
+#endif // gc_Zone_h