1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
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/. */
8 * API functions and methods used by the rest of SpiderMonkey and by embeddings.
11 #include "mozilla/TimeStamp.h"
14 #include "jsfriendapi.h"
17 #include "gc/PublicIterators.h"
18 #include "jit/JitZone.h"
19 #include "js/HeapAPI.h"
21 #include "util/DifferentialTesting.h"
22 #include "vm/HelperThreads.h"
26 #include "gc/Marking-inl.h"
27 #include "gc/StableCellHasher-inl.h"
28 #include "vm/GeckoProfiler-inl.h"
29 #include "vm/JSContext-inl.h"
32 using namespace js::gc
;
34 using mozilla::TimeStamp
;
36 extern JS_PUBLIC_API
bool js::AddRawValueRoot(JSContext
* cx
, Value
* vp
,
40 bool ok
= cx
->runtime()->gc
.addRoot(vp
, name
);
42 JS_ReportOutOfMemory(cx
);
47 extern JS_PUBLIC_API
void js::RemoveRawValueRoot(JSContext
* cx
, Value
* vp
) {
48 cx
->runtime()->gc
.removeRoot(vp
);
51 JS_PUBLIC_API
JS::HeapState
JS::RuntimeHeapState() {
52 return TlsContext
.get()->runtime()->gc
.heapState();
55 JS::AutoDisableGenerationalGC::AutoDisableGenerationalGC(JSContext
* cx
)
57 if (!cx
->generationalDisabled
) {
58 cx
->runtime()->gc
.evictNursery(JS::GCReason::DISABLE_GENERATIONAL_GC
);
59 cx
->nursery().disable();
61 ++cx
->generationalDisabled
;
62 MOZ_ASSERT(cx
->nursery().isEmpty());
65 JS::AutoDisableGenerationalGC::~AutoDisableGenerationalGC() {
66 if (--cx
->generationalDisabled
== 0 &&
67 cx
->runtime()->gc
.tunables
.gcMaxNurseryBytes() > 0) {
68 cx
->nursery().enable();
72 JS_PUBLIC_API
bool JS::IsGenerationalGCEnabled(JSRuntime
* rt
) {
73 return !rt
->mainContextFromOwnThread()->generationalDisabled
;
76 AutoDisableCompactingGC::AutoDisableCompactingGC(JSContext
* cx
) : cx(cx
) {
77 ++cx
->compactingDisabledCount
;
78 if (cx
->runtime()->gc
.isIncrementalGCInProgress() &&
79 cx
->runtime()->gc
.isCompactingGc()) {
84 AutoDisableCompactingGC::~AutoDisableCompactingGC() {
85 MOZ_ASSERT(cx
->compactingDisabledCount
> 0);
86 --cx
->compactingDisabledCount
;
91 /* Should only be called manually under gdb */
92 void PreventGCDuringInteractiveDebug() { TlsContext
.get()->suppressGC
++; }
96 void js::ReleaseAllJITCode(JS::GCContext
* gcx
) {
97 js::CancelOffThreadIonCompile(gcx
->runtime());
99 for (ZonesIter
zone(gcx
->runtime(), SkipAtoms
); !zone
.done(); zone
.next()) {
100 zone
->forceDiscardJitCode(gcx
);
101 if (jit::JitZone
* jitZone
= zone
->jitZone()) {
102 jitZone
->discardStubs();
107 AutoSuppressGC::AutoSuppressGC(JSContext
* cx
)
108 : suppressGC_(cx
->suppressGC
.ref()) {
113 AutoDisableProxyCheck::AutoDisableProxyCheck() {
114 TlsContext
.get()->disableStrictProxyChecking();
117 AutoDisableProxyCheck::~AutoDisableProxyCheck() {
118 TlsContext
.get()->enableStrictProxyChecking();
121 JS_PUBLIC_API
void JS::AssertGCThingMustBeTenured(JSObject
* obj
) {
122 MOZ_ASSERT(obj
->isTenured());
123 MOZ_ASSERT(obj
->getClass()->hasFinalize() &&
124 !(obj
->getClass()->flags
& JSCLASS_SKIP_NURSERY_FINALIZE
));
127 JS_PUBLIC_API
void JS::AssertGCThingIsNotNurseryAllocable(Cell
* cell
) {
129 MOZ_ASSERT(!cell
->is
<JSObject
>() && !cell
->is
<JSString
>() &&
130 !cell
->is
<JS::BigInt
>());
133 JS_PUBLIC_API
void js::gc::AssertGCThingHasType(js::gc::Cell
* cell
,
134 JS::TraceKind kind
) {
136 MOZ_ASSERT(kind
== JS::TraceKind::Null
);
140 MOZ_ASSERT(IsCellPointerValid(cell
));
141 MOZ_ASSERT(cell
->getTraceKind() == kind
);
145 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
147 JS::AutoAssertNoGC::AutoAssertNoGC(JSContext
* maybecx
) {
150 } else if (TlsContext
.initialized()) {
151 cx_
= TlsContext
.get();
156 cx_
->inUnsafeRegion
++;
160 JS::AutoAssertNoGC::~AutoAssertNoGC() { reset(); }
162 void JS::AutoAssertNoGC::reset() {
164 MOZ_ASSERT(cx_
->inUnsafeRegion
> 0);
165 cx_
->inUnsafeRegion
--;
170 #endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
174 JS::AutoEnterCycleCollection::AutoEnterCycleCollection(JSRuntime
* rt
)
176 MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt
));
177 MOZ_ASSERT(!JS::RuntimeHeapIsBusy());
178 runtime_
->gc
.heapState_
= HeapState::CycleCollecting
;
181 JS::AutoEnterCycleCollection::~AutoEnterCycleCollection() {
182 MOZ_ASSERT(JS::RuntimeHeapIsCycleCollecting());
183 runtime_
->gc
.heapState_
= HeapState::Idle
;
186 JS::AutoAssertGCCallback::AutoAssertGCCallback() : AutoSuppressGCAnalysis() {
187 MOZ_ASSERT(JS::RuntimeHeapIsCollecting());
192 JS_PUBLIC_API
const char* JS::GCTraceKindToAscii(JS::TraceKind kind
) {
194 #define MAP_NAME(name, _0, _1, _2) \
195 case JS::TraceKind::name: \
197 JS_FOR_EACH_TRACEKIND(MAP_NAME
);
204 JS_PUBLIC_API
size_t JS::GCTraceKindSize(JS::TraceKind kind
) {
206 #define MAP_SIZE(name, type, _0, _1) \
207 case JS::TraceKind::name: \
209 JS_FOR_EACH_TRACEKIND(MAP_SIZE
);
216 JS::GCCellPtr::GCCellPtr(const Value
& v
)
217 : GCCellPtr(v
.toGCThing(), v
.traceKind()) {}
219 JS::TraceKind
JS::GCCellPtr::outOfLineKind() const {
220 MOZ_ASSERT((ptr
& OutOfLineTraceKindMask
) == OutOfLineTraceKindMask
);
221 MOZ_ASSERT(asCell()->isTenured());
222 return MapAllocToTraceKind(asCell()->asTenured().getAllocKind());
225 JS_PUBLIC_API
void JS::PrepareZoneForGC(JSContext
* cx
, Zone
* zone
) {
229 // If we got the zone from a shared atom, we may have the wrong atoms zone
231 if (zone
->isAtomsZone()) {
232 zone
= cx
->runtime()->atomsZone();
235 MOZ_ASSERT(cx
->runtime()->gc
.hasZone(zone
));
239 JS_PUBLIC_API
void JS::PrepareForFullGC(JSContext
* cx
) {
243 cx
->runtime()->gc
.fullGCRequested
= true;
244 for (ZonesIter
zone(cx
->runtime(), WithAtoms
); !zone
.done(); zone
.next()) {
249 JS_PUBLIC_API
void JS::PrepareForIncrementalGC(JSContext
* cx
) {
253 if (!JS::IsIncrementalGCInProgress(cx
)) {
257 for (ZonesIter
zone(cx
->runtime(), WithAtoms
); !zone
.done(); zone
.next()) {
258 if (zone
->wasGCStarted()) {
264 JS_PUBLIC_API
bool JS::IsGCScheduled(JSContext
* cx
) {
268 for (ZonesIter
zone(cx
->runtime(), WithAtoms
); !zone
.done(); zone
.next()) {
269 if (zone
->isGCScheduled()) {
277 JS_PUBLIC_API
void JS::SkipZoneForGC(JSContext
* cx
, Zone
* zone
) {
280 MOZ_ASSERT(cx
->runtime()->gc
.hasZone(zone
));
282 cx
->runtime()->gc
.fullGCRequested
= false;
283 zone
->unscheduleGC();
286 static inline void CheckGCOptions(JS::GCOptions options
) {
287 MOZ_ASSERT(options
== JS::GCOptions::Normal
||
288 options
== JS::GCOptions::Shrink
||
289 options
== JS::GCOptions::Shutdown
);
292 JS_PUBLIC_API
void JS::NonIncrementalGC(JSContext
* cx
, JS::GCOptions options
,
296 CheckGCOptions(options
);
298 cx
->runtime()->gc
.gc(options
, reason
);
300 MOZ_ASSERT(!IsIncrementalGCInProgress(cx
));
303 JS_PUBLIC_API
void JS::StartIncrementalGC(JSContext
* cx
, JS::GCOptions options
,
305 const js::SliceBudget
& budget
) {
308 CheckGCOptions(options
);
310 cx
->runtime()->gc
.startGC(options
, reason
, budget
);
313 JS_PUBLIC_API
void JS::IncrementalGCSlice(JSContext
* cx
, GCReason reason
,
314 const js::SliceBudget
& budget
) {
318 cx
->runtime()->gc
.gcSlice(reason
, budget
);
321 JS_PUBLIC_API
bool JS::IncrementalGCHasForegroundWork(JSContext
* cx
) {
325 return cx
->runtime()->gc
.hasForegroundWork();
328 JS_PUBLIC_API
void JS::FinishIncrementalGC(JSContext
* cx
, GCReason reason
) {
332 cx
->runtime()->gc
.finishGC(reason
);
335 JS_PUBLIC_API
void JS::AbortIncrementalGC(JSContext
* cx
) {
339 if (IsIncrementalGCInProgress(cx
)) {
340 cx
->runtime()->gc
.abortGC();
344 char16_t
* JS::GCDescription::formatSliceMessage(JSContext
* cx
) const {
345 UniqueChars cstr
= cx
->runtime()->gc
.stats().formatCompactSliceMessage();
347 size_t nchars
= strlen(cstr
.get());
348 UniqueTwoByteChars
out(js_pod_malloc
<char16_t
>(nchars
+ 1));
352 out
.get()[nchars
] = 0;
354 CopyAndInflateChars(out
.get(), cstr
.get(), nchars
);
355 return out
.release();
358 char16_t
* JS::GCDescription::formatSummaryMessage(JSContext
* cx
) const {
359 UniqueChars cstr
= cx
->runtime()->gc
.stats().formatCompactSummaryMessage();
361 size_t nchars
= strlen(cstr
.get());
362 UniqueTwoByteChars
out(js_pod_malloc
<char16_t
>(nchars
+ 1));
366 out
.get()[nchars
] = 0;
368 CopyAndInflateChars(out
.get(), cstr
.get(), nchars
);
369 return out
.release();
372 JS::dbg::GarbageCollectionEvent::Ptr
JS::GCDescription::toGCEvent(
373 JSContext
* cx
) const {
374 return JS::dbg::GarbageCollectionEvent::Create(
375 cx
->runtime(), cx
->runtime()->gc
.stats(),
376 cx
->runtime()->gc
.majorGCCount());
379 TimeStamp
JS::GCDescription::startTime(JSContext
* cx
) const {
380 return cx
->runtime()->gc
.stats().start();
383 TimeStamp
JS::GCDescription::endTime(JSContext
* cx
) const {
384 return cx
->runtime()->gc
.stats().end();
387 TimeStamp
JS::GCDescription::lastSliceStart(JSContext
* cx
) const {
388 return cx
->runtime()->gc
.stats().slices().back().start
;
391 TimeStamp
JS::GCDescription::lastSliceEnd(JSContext
* cx
) const {
392 return cx
->runtime()->gc
.stats().slices().back().end
;
395 JS::UniqueChars
JS::GCDescription::sliceToJSONProfiler(JSContext
* cx
) const {
396 size_t slices
= cx
->runtime()->gc
.stats().slices().length();
397 MOZ_ASSERT(slices
> 0);
398 return cx
->runtime()->gc
.stats().renderJsonSlice(slices
- 1);
401 JS::UniqueChars
JS::GCDescription::formatJSONProfiler(JSContext
* cx
) const {
402 return cx
->runtime()->gc
.stats().renderJsonMessage();
405 JS_PUBLIC_API
JS::UniqueChars
JS::MinorGcToJSON(JSContext
* cx
) {
406 JSRuntime
* rt
= cx
->runtime();
407 return rt
->gc
.stats().renderNurseryJson();
410 JS_PUBLIC_API
JS::GCSliceCallback
JS::SetGCSliceCallback(
411 JSContext
* cx
, GCSliceCallback callback
) {
412 return cx
->runtime()->gc
.setSliceCallback(callback
);
415 JS_PUBLIC_API
JS::DoCycleCollectionCallback
JS::SetDoCycleCollectionCallback(
416 JSContext
* cx
, JS::DoCycleCollectionCallback callback
) {
417 return cx
->runtime()->gc
.setDoCycleCollectionCallback(callback
);
420 JS_PUBLIC_API
bool JS::AddGCNurseryCollectionCallback(
421 JSContext
* cx
, GCNurseryCollectionCallback callback
, void* data
) {
422 return cx
->runtime()->gc
.addNurseryCollectionCallback(callback
, data
);
425 JS_PUBLIC_API
void JS::RemoveGCNurseryCollectionCallback(
426 JSContext
* cx
, GCNurseryCollectionCallback callback
, void* data
) {
427 return cx
->runtime()->gc
.removeNurseryCollectionCallback(callback
, data
);
430 JS_PUBLIC_API
void JS::SetLowMemoryState(JSContext
* cx
, bool newState
) {
431 return cx
->runtime()->gc
.setLowMemoryState(newState
);
434 JS_PUBLIC_API
void JS::DisableIncrementalGC(JSContext
* cx
) {
435 cx
->runtime()->gc
.disallowIncrementalGC();
438 JS_PUBLIC_API
bool JS::IsIncrementalGCEnabled(JSContext
* cx
) {
439 GCRuntime
& gc
= cx
->runtime()->gc
;
440 return gc
.isIncrementalGCEnabled() && gc
.isIncrementalGCAllowed();
443 JS_PUBLIC_API
bool JS::IsIncrementalGCInProgress(JSContext
* cx
) {
444 return cx
->runtime()->gc
.isIncrementalGCInProgress();
447 JS_PUBLIC_API
bool JS::IsIncrementalGCInProgress(JSRuntime
* rt
) {
448 return rt
->gc
.isIncrementalGCInProgress() &&
449 !rt
->gc
.isVerifyPreBarriersEnabled();
452 JS_PUBLIC_API
bool JS::IsIncrementalBarrierNeeded(JSContext
* cx
) {
453 if (JS::RuntimeHeapIsBusy()) {
457 auto state
= cx
->runtime()->gc
.state();
458 return state
!= gc::State::NotActive
&& state
<= gc::State::Sweep
;
461 JS_PUBLIC_API
void JS::IncrementalPreWriteBarrier(JSObject
* obj
) {
466 AutoGeckoProfilerEntry
profilingStackFrame(
467 TlsContext
.get(), "IncrementalPreWriteBarrier(JSObject*)",
468 JS::ProfilingCategoryPair::GCCC_Barrier
);
469 PreWriteBarrier(obj
);
472 JS_PUBLIC_API
void JS::IncrementalPreWriteBarrier(GCCellPtr thing
) {
477 AutoGeckoProfilerEntry
profilingStackFrame(
478 TlsContext
.get(), "IncrementalPreWriteBarrier(GCCellPtr)",
479 JS::ProfilingCategoryPair::GCCC_Barrier
);
480 CellPtrPreWriteBarrier(thing
);
483 JS_PUBLIC_API
bool JS::WasIncrementalGC(JSRuntime
* rt
) {
484 return rt
->gc
.isIncrementalGc();
487 bool js::gc::CreateUniqueIdForNativeObject(NativeObject
* nobj
, uint64_t* uidp
) {
488 JSRuntime
* runtime
= nobj
->runtimeFromMainThread();
489 *uidp
= NextCellUniqueId(runtime
);
490 return nobj
->setUniqueId(runtime
, *uidp
);
493 bool js::gc::CreateUniqueIdForNonNativeObject(Cell
* cell
,
494 UniqueIdMap::AddPtr ptr
,
496 // If the cell is in the nursery, hopefully unlikely, then we need to tell the
497 // nursery about it so that it can sweep the uid if the thing does not get
499 JSRuntime
* runtime
= cell
->runtimeFromMainThread();
500 if (IsInsideNursery(cell
) &&
501 !runtime
->gc
.nursery().addedUniqueIdToCell(cell
)) {
505 // Set a new uid on the cell.
506 *uidp
= NextCellUniqueId(runtime
);
507 return cell
->zone()->uniqueIds().add(ptr
, cell
, *uidp
);
510 uint64_t js::gc::NextCellUniqueId(JSRuntime
* rt
) {
511 return rt
->gc
.nextCellUniqueId();
516 static const struct GCParamInfo
{
521 #define DEFINE_PARAM_INFO(name, key, writable) {name, key, writable},
522 FOR_EACH_GC_PARAM(DEFINE_PARAM_INFO
)
523 #undef DEFINE_PARAM_INFO
526 bool GetGCParameterInfo(const char* name
, JSGCParamKey
* keyOut
,
529 MOZ_ASSERT(writableOut
);
531 for (const GCParamInfo
& info
: GCParameters
) {
532 if (strcmp(name
, info
.name
) == 0) {
534 *writableOut
= info
.writable
;
545 static bool GCBytesGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
546 CallArgs args
= CallArgsFromVp(argc
, vp
);
547 args
.rval().setNumber(double(cx
->runtime()->gc
.heapSize
.bytes()));
551 static bool MallocBytesGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
552 CallArgs args
= CallArgsFromVp(argc
, vp
);
554 for (ZonesIter
zone(cx
->runtime(), WithAtoms
); !zone
.done(); zone
.next()) {
555 bytes
+= zone
->mallocHeapSize
.bytes();
557 args
.rval().setNumber(bytes
);
561 static bool GCMaxBytesGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
562 CallArgs args
= CallArgsFromVp(argc
, vp
);
563 args
.rval().setNumber(double(cx
->runtime()->gc
.tunables
.gcMaxBytes()));
567 static bool GCHighFreqGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
568 CallArgs args
= CallArgsFromVp(argc
, vp
);
569 args
.rval().setBoolean(
570 cx
->runtime()->gc
.schedulingState
.inHighFrequencyGCMode());
574 static bool GCNumberGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
575 CallArgs args
= CallArgsFromVp(argc
, vp
);
576 args
.rval().setNumber(double(cx
->runtime()->gc
.gcNumber()));
580 static bool MajorGCCountGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
581 CallArgs args
= CallArgsFromVp(argc
, vp
);
582 args
.rval().setNumber(double(cx
->runtime()->gc
.majorGCCount()));
586 static bool MinorGCCountGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
587 CallArgs args
= CallArgsFromVp(argc
, vp
);
588 args
.rval().setNumber(double(cx
->runtime()->gc
.minorGCCount()));
592 static bool GCSliceCountGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
593 CallArgs args
= CallArgsFromVp(argc
, vp
);
594 args
.rval().setNumber(double(cx
->runtime()->gc
.gcSliceCount()));
598 static bool GCCompartmentCount(JSContext
* cx
, unsigned argc
, Value
* vp
) {
599 CallArgs args
= CallArgsFromVp(argc
, vp
);
601 for (ZonesIter
zone(cx
->runtime(), WithAtoms
); !zone
.done(); zone
.next()) {
602 count
+= zone
->compartments().length();
605 args
.rval().setNumber(double(count
));
609 static bool GCLastStartReason(JSContext
* cx
, unsigned argc
, Value
* vp
) {
610 CallArgs args
= CallArgsFromVp(argc
, vp
);
611 const char* reason
= ExplainGCReason(cx
->runtime()->gc
.lastStartReason());
612 RootedString
str(cx
, JS_NewStringCopyZ(cx
, reason
));
617 args
.rval().setString(str
);
621 static bool ZoneGCBytesGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
622 CallArgs args
= CallArgsFromVp(argc
, vp
);
623 args
.rval().setNumber(double(cx
->zone()->gcHeapSize
.bytes()));
627 static bool ZoneGCTriggerBytesGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
628 CallArgs args
= CallArgsFromVp(argc
, vp
);
629 args
.rval().setNumber(double(cx
->zone()->gcHeapThreshold
.startBytes()));
633 static bool ZoneGCAllocTriggerGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
634 CallArgs args
= CallArgsFromVp(argc
, vp
);
636 cx
->runtime()->gc
.schedulingState
.inHighFrequencyGCMode();
637 args
.rval().setNumber(
638 double(cx
->zone()->gcHeapThreshold
.eagerAllocTrigger(highFrequency
)));
642 static bool ZoneMallocBytesGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
643 CallArgs args
= CallArgsFromVp(argc
, vp
);
644 args
.rval().setNumber(double(cx
->zone()->mallocHeapSize
.bytes()));
648 static bool ZoneMallocTriggerBytesGetter(JSContext
* cx
, unsigned argc
,
650 CallArgs args
= CallArgsFromVp(argc
, vp
);
651 args
.rval().setNumber(double(cx
->zone()->mallocHeapThreshold
.startBytes()));
655 static bool ZoneGCNumberGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
656 CallArgs args
= CallArgsFromVp(argc
, vp
);
657 args
.rval().setNumber(double(cx
->runtime()->gc
.gcNumber()));
662 static bool DummyGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
663 CallArgs args
= CallArgsFromVp(argc
, vp
);
664 args
.rval().setUndefined();
669 } /* namespace MemInfo */
671 JSObject
* NewMemoryInfoObject(JSContext
* cx
) {
672 RootedObject
obj(cx
, JS_NewObject(cx
, nullptr));
677 using namespace MemInfo
;
681 } getters
[] = {{"gcBytes", GCBytesGetter
},
682 {"gcMaxBytes", GCMaxBytesGetter
},
683 {"mallocBytes", MallocBytesGetter
},
684 {"gcIsHighFrequencyMode", GCHighFreqGetter
},
685 {"gcNumber", GCNumberGetter
},
686 {"majorGCCount", MajorGCCountGetter
},
687 {"minorGCCount", MinorGCCountGetter
},
688 {"sliceCount", GCSliceCountGetter
},
689 {"compartmentCount", GCCompartmentCount
},
690 {"lastStartReason", GCLastStartReason
}};
692 for (auto pair
: getters
) {
693 JSNative getter
= pair
.getter
;
696 if (js::SupportDifferentialTesting()) {
697 getter
= DummyGetter
;
701 if (!JS_DefineProperty(cx
, obj
, pair
.name
, getter
, nullptr,
707 RootedObject
zoneObj(cx
, JS_NewObject(cx
, nullptr));
712 if (!JS_DefineProperty(cx
, obj
, "zone", zoneObj
, JSPROP_ENUMERATE
)) {
716 struct NamedZoneGetter
{
719 } zoneGetters
[] = {{"gcBytes", ZoneGCBytesGetter
},
720 {"gcTriggerBytes", ZoneGCTriggerBytesGetter
},
721 {"gcAllocTrigger", ZoneGCAllocTriggerGetter
},
722 {"mallocBytes", ZoneMallocBytesGetter
},
723 {"mallocTriggerBytes", ZoneMallocTriggerBytesGetter
},
724 {"gcNumber", ZoneGCNumberGetter
}};
726 for (auto pair
: zoneGetters
) {
727 JSNative getter
= pair
.getter
;
730 if (js::SupportDifferentialTesting()) {
731 getter
= DummyGetter
;
735 if (!JS_DefineProperty(cx
, zoneObj
, pair
.name
, getter
, nullptr,
744 const char* StateName(State state
) {
746 #define MAKE_CASE(name) \
752 MOZ_CRASH("Invalid gc::State enum value");
755 const char* StateName(JS::Zone::GCState state
) {
759 case JS::Zone::Prepare
:
761 case JS::Zone::MarkBlackOnly
:
762 return "MarkBlackOnly";
763 case JS::Zone::MarkBlackAndGray
:
764 return "MarkBlackAndGray";
765 case JS::Zone::Sweep
:
767 case JS::Zone::Finished
:
769 case JS::Zone::Compact
:
771 case JS::Zone::VerifyPreBarriers
:
772 return "VerifyPreBarriers";
773 case JS::Zone::Limit
:
776 MOZ_CRASH("Invalid Zone::GCState enum value");
779 const char* CellColorName(CellColor color
) {
781 case CellColor::White
:
783 case CellColor::Black
:
785 case CellColor::Gray
:
788 MOZ_CRASH("Unexpected cell color");
795 JS_PUBLIC_API
bool js::gc::IsDeadNurseryObject(JSObject
* obj
) {
796 MOZ_ASSERT(JS::RuntimeHeapIsMinorCollecting());
798 MOZ_ASSERT(IsInsideNursery(obj
));
799 MOZ_ASSERT(!IsForwarded(obj
));
801 return obj
->runtimeFromMainThread()->gc
.nursery().inCollectedRegion(obj
);
804 JS_PUBLIC_API
void js::gc::FinalizeDeadNurseryObject(JSContext
* cx
,
807 MOZ_ASSERT(JS::RuntimeHeapIsMinorCollecting());
810 MOZ_ASSERT(IsInsideNursery(obj
));
811 MOZ_ASSERT(!IsForwarded(obj
));
813 const JSClass
* jsClass
= JS::GetClass(obj
);
814 jsClass
->doFinalize(cx
->gcContext(), obj
);
817 JS_PUBLIC_API
void js::gc::SetPerformanceHint(JSContext
* cx
,
818 PerformanceHint hint
) {
820 MOZ_ASSERT(!JS::RuntimeHeapIsCollecting());
822 cx
->runtime()->gc
.setPerformanceHint(hint
);
825 AutoSelectGCHeap::AutoSelectGCHeap(JSContext
* cx
,
826 size_t allowedNurseryCollections
)
827 : cx_(cx
), allowedNurseryCollections_(allowedNurseryCollections
) {
828 if (!JS::AddGCNurseryCollectionCallback(cx
, &NurseryCollectionCallback
,
834 AutoSelectGCHeap::~AutoSelectGCHeap() {
836 JS::RemoveGCNurseryCollectionCallback(cx_
, &NurseryCollectionCallback
,
842 void AutoSelectGCHeap::NurseryCollectionCallback(JSContext
* cx
,
843 JS::GCNurseryProgress progress
,
846 if (progress
== JS::GCNurseryProgress::GC_NURSERY_COLLECTION_END
) {
847 static_cast<AutoSelectGCHeap
*>(data
)->onNurseryCollectionEnd();
851 void AutoSelectGCHeap::onNurseryCollectionEnd() {
852 if (allowedNurseryCollections_
!= 0) {
853 allowedNurseryCollections_
--;
857 heap_
= gc::Heap::Tenured
;
860 JS_PUBLIC_API
void js::gc::LockStoreBuffer(JSRuntime
* runtime
) {
862 runtime
->gc
.lockStoreBuffer();
865 JS_PUBLIC_API
void js::gc::UnlockStoreBuffer(JSRuntime
* runtime
) {
867 runtime
->gc
.unlockStoreBuffer();