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() {
162 MOZ_ASSERT(cx_
->inUnsafeRegion
> 0);
163 cx_
->inUnsafeRegion
--;
167 #endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
171 JS::AutoEnterCycleCollection::AutoEnterCycleCollection(JSRuntime
* rt
)
173 MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt
));
174 MOZ_ASSERT(!JS::RuntimeHeapIsBusy());
175 runtime_
->gc
.heapState_
= HeapState::CycleCollecting
;
178 JS::AutoEnterCycleCollection::~AutoEnterCycleCollection() {
179 MOZ_ASSERT(JS::RuntimeHeapIsCycleCollecting());
180 runtime_
->gc
.heapState_
= HeapState::Idle
;
183 JS::AutoAssertGCCallback::AutoAssertGCCallback() : AutoSuppressGCAnalysis() {
184 MOZ_ASSERT(JS::RuntimeHeapIsCollecting());
189 JS_PUBLIC_API
const char* JS::GCTraceKindToAscii(JS::TraceKind kind
) {
191 #define MAP_NAME(name, _0, _1, _2) \
192 case JS::TraceKind::name: \
194 JS_FOR_EACH_TRACEKIND(MAP_NAME
);
201 JS_PUBLIC_API
size_t JS::GCTraceKindSize(JS::TraceKind kind
) {
203 #define MAP_SIZE(name, type, _0, _1) \
204 case JS::TraceKind::name: \
206 JS_FOR_EACH_TRACEKIND(MAP_SIZE
);
213 JS::GCCellPtr::GCCellPtr(const Value
& v
)
214 : GCCellPtr(v
.toGCThing(), v
.traceKind()) {}
216 JS::TraceKind
JS::GCCellPtr::outOfLineKind() const {
217 MOZ_ASSERT((ptr
& OutOfLineTraceKindMask
) == OutOfLineTraceKindMask
);
218 MOZ_ASSERT(asCell()->isTenured());
219 return MapAllocToTraceKind(asCell()->asTenured().getAllocKind());
222 JS_PUBLIC_API
void JS::PrepareZoneForGC(JSContext
* cx
, Zone
* zone
) {
226 // If we got the zone from a shared atom, we may have the wrong atoms zone
228 if (zone
->isAtomsZone()) {
229 zone
= cx
->runtime()->atomsZone();
232 MOZ_ASSERT(cx
->runtime()->gc
.hasZone(zone
));
236 JS_PUBLIC_API
void JS::PrepareForFullGC(JSContext
* cx
) {
240 cx
->runtime()->gc
.fullGCRequested
= true;
241 for (ZonesIter
zone(cx
->runtime(), WithAtoms
); !zone
.done(); zone
.next()) {
246 JS_PUBLIC_API
void JS::PrepareForIncrementalGC(JSContext
* cx
) {
250 if (!JS::IsIncrementalGCInProgress(cx
)) {
254 for (ZonesIter
zone(cx
->runtime(), WithAtoms
); !zone
.done(); zone
.next()) {
255 if (zone
->wasGCStarted()) {
261 JS_PUBLIC_API
bool JS::IsGCScheduled(JSContext
* cx
) {
265 for (ZonesIter
zone(cx
->runtime(), WithAtoms
); !zone
.done(); zone
.next()) {
266 if (zone
->isGCScheduled()) {
274 JS_PUBLIC_API
void JS::SkipZoneForGC(JSContext
* cx
, Zone
* zone
) {
277 MOZ_ASSERT(cx
->runtime()->gc
.hasZone(zone
));
279 cx
->runtime()->gc
.fullGCRequested
= false;
280 zone
->unscheduleGC();
283 static inline void CheckGCOptions(JS::GCOptions options
) {
284 MOZ_ASSERT(options
== JS::GCOptions::Normal
||
285 options
== JS::GCOptions::Shrink
||
286 options
== JS::GCOptions::Shutdown
);
289 JS_PUBLIC_API
void JS::NonIncrementalGC(JSContext
* cx
, JS::GCOptions options
,
293 CheckGCOptions(options
);
295 cx
->runtime()->gc
.gc(options
, reason
);
297 MOZ_ASSERT(!IsIncrementalGCInProgress(cx
));
300 JS_PUBLIC_API
void JS::StartIncrementalGC(JSContext
* cx
, JS::GCOptions options
,
302 const js::SliceBudget
& budget
) {
305 CheckGCOptions(options
);
307 cx
->runtime()->gc
.startGC(options
, reason
, budget
);
310 JS_PUBLIC_API
void JS::IncrementalGCSlice(JSContext
* cx
, GCReason reason
,
311 const js::SliceBudget
& budget
) {
315 cx
->runtime()->gc
.gcSlice(reason
, budget
);
318 JS_PUBLIC_API
bool JS::IncrementalGCHasForegroundWork(JSContext
* cx
) {
322 return cx
->runtime()->gc
.hasForegroundWork();
325 JS_PUBLIC_API
void JS::FinishIncrementalGC(JSContext
* cx
, GCReason reason
) {
329 cx
->runtime()->gc
.finishGC(reason
);
332 JS_PUBLIC_API
void JS::AbortIncrementalGC(JSContext
* cx
) {
336 if (IsIncrementalGCInProgress(cx
)) {
337 cx
->runtime()->gc
.abortGC();
341 char16_t
* JS::GCDescription::formatSliceMessage(JSContext
* cx
) const {
342 UniqueChars cstr
= cx
->runtime()->gc
.stats().formatCompactSliceMessage();
344 size_t nchars
= strlen(cstr
.get());
345 UniqueTwoByteChars
out(js_pod_malloc
<char16_t
>(nchars
+ 1));
349 out
.get()[nchars
] = 0;
351 CopyAndInflateChars(out
.get(), cstr
.get(), nchars
);
352 return out
.release();
355 char16_t
* JS::GCDescription::formatSummaryMessage(JSContext
* cx
) const {
356 UniqueChars cstr
= cx
->runtime()->gc
.stats().formatCompactSummaryMessage();
358 size_t nchars
= strlen(cstr
.get());
359 UniqueTwoByteChars
out(js_pod_malloc
<char16_t
>(nchars
+ 1));
363 out
.get()[nchars
] = 0;
365 CopyAndInflateChars(out
.get(), cstr
.get(), nchars
);
366 return out
.release();
369 JS::dbg::GarbageCollectionEvent::Ptr
JS::GCDescription::toGCEvent(
370 JSContext
* cx
) const {
371 return JS::dbg::GarbageCollectionEvent::Create(
372 cx
->runtime(), cx
->runtime()->gc
.stats(),
373 cx
->runtime()->gc
.majorGCCount());
376 TimeStamp
JS::GCDescription::startTime(JSContext
* cx
) const {
377 return cx
->runtime()->gc
.stats().start();
380 TimeStamp
JS::GCDescription::endTime(JSContext
* cx
) const {
381 return cx
->runtime()->gc
.stats().end();
384 TimeStamp
JS::GCDescription::lastSliceStart(JSContext
* cx
) const {
385 return cx
->runtime()->gc
.stats().slices().back().start
;
388 TimeStamp
JS::GCDescription::lastSliceEnd(JSContext
* cx
) const {
389 return cx
->runtime()->gc
.stats().slices().back().end
;
392 JS::UniqueChars
JS::GCDescription::sliceToJSONProfiler(JSContext
* cx
) const {
393 size_t slices
= cx
->runtime()->gc
.stats().slices().length();
394 MOZ_ASSERT(slices
> 0);
395 return cx
->runtime()->gc
.stats().renderJsonSlice(slices
- 1);
398 JS::UniqueChars
JS::GCDescription::formatJSONProfiler(JSContext
* cx
) const {
399 return cx
->runtime()->gc
.stats().renderJsonMessage();
402 JS_PUBLIC_API
JS::UniqueChars
JS::MinorGcToJSON(JSContext
* cx
) {
403 JSRuntime
* rt
= cx
->runtime();
404 return rt
->gc
.stats().renderNurseryJson();
407 JS_PUBLIC_API
JS::GCSliceCallback
JS::SetGCSliceCallback(
408 JSContext
* cx
, GCSliceCallback callback
) {
409 return cx
->runtime()->gc
.setSliceCallback(callback
);
412 JS_PUBLIC_API
JS::DoCycleCollectionCallback
JS::SetDoCycleCollectionCallback(
413 JSContext
* cx
, JS::DoCycleCollectionCallback callback
) {
414 return cx
->runtime()->gc
.setDoCycleCollectionCallback(callback
);
417 JS_PUBLIC_API
bool JS::AddGCNurseryCollectionCallback(
418 JSContext
* cx
, GCNurseryCollectionCallback callback
, void* data
) {
419 return cx
->runtime()->gc
.addNurseryCollectionCallback(callback
, data
);
422 JS_PUBLIC_API
void JS::RemoveGCNurseryCollectionCallback(
423 JSContext
* cx
, GCNurseryCollectionCallback callback
, void* data
) {
424 return cx
->runtime()->gc
.removeNurseryCollectionCallback(callback
, data
);
427 JS_PUBLIC_API
void JS::SetLowMemoryState(JSContext
* cx
, bool newState
) {
428 return cx
->runtime()->gc
.setLowMemoryState(newState
);
431 JS_PUBLIC_API
void JS::DisableIncrementalGC(JSContext
* cx
) {
432 cx
->runtime()->gc
.disallowIncrementalGC();
435 JS_PUBLIC_API
bool JS::IsIncrementalGCEnabled(JSContext
* cx
) {
436 GCRuntime
& gc
= cx
->runtime()->gc
;
437 return gc
.isIncrementalGCEnabled() && gc
.isIncrementalGCAllowed();
440 JS_PUBLIC_API
bool JS::IsIncrementalGCInProgress(JSContext
* cx
) {
441 return cx
->runtime()->gc
.isIncrementalGCInProgress();
444 JS_PUBLIC_API
bool JS::IsIncrementalGCInProgress(JSRuntime
* rt
) {
445 return rt
->gc
.isIncrementalGCInProgress() &&
446 !rt
->gc
.isVerifyPreBarriersEnabled();
449 JS_PUBLIC_API
bool JS::IsIncrementalBarrierNeeded(JSContext
* cx
) {
450 if (JS::RuntimeHeapIsBusy()) {
454 auto state
= cx
->runtime()->gc
.state();
455 return state
!= gc::State::NotActive
&& state
<= gc::State::Sweep
;
458 JS_PUBLIC_API
void JS::IncrementalPreWriteBarrier(JSObject
* obj
) {
463 AutoGeckoProfilerEntry
profilingStackFrame(
464 TlsContext
.get(), "IncrementalPreWriteBarrier(JSObject*)",
465 JS::ProfilingCategoryPair::GCCC_Barrier
);
466 PreWriteBarrier(obj
);
469 JS_PUBLIC_API
void JS::IncrementalPreWriteBarrier(GCCellPtr thing
) {
474 AutoGeckoProfilerEntry
profilingStackFrame(
475 TlsContext
.get(), "IncrementalPreWriteBarrier(GCCellPtr)",
476 JS::ProfilingCategoryPair::GCCC_Barrier
);
477 CellPtrPreWriteBarrier(thing
);
480 JS_PUBLIC_API
bool JS::WasIncrementalGC(JSRuntime
* rt
) {
481 return rt
->gc
.isIncrementalGc();
484 bool js::gc::CreateUniqueIdForNativeObject(NativeObject
* nobj
, uint64_t* uidp
) {
485 JSRuntime
* runtime
= nobj
->runtimeFromMainThread();
486 *uidp
= NextCellUniqueId(runtime
);
487 JSContext
* cx
= runtime
->mainContextFromOwnThread();
488 return nobj
->setUniqueId(cx
, *uidp
);
491 bool js::gc::CreateUniqueIdForNonNativeObject(Cell
* cell
,
492 UniqueIdMap::AddPtr ptr
,
494 // If the cell is in the nursery, hopefully unlikely, then we need to tell the
495 // nursery about it so that it can sweep the uid if the thing does not get
497 JSRuntime
* runtime
= cell
->runtimeFromMainThread();
498 if (IsInsideNursery(cell
) &&
499 !runtime
->gc
.nursery().addedUniqueIdToCell(cell
)) {
503 // Set a new uid on the cell.
504 *uidp
= NextCellUniqueId(runtime
);
505 return cell
->zone()->uniqueIds().add(ptr
, cell
, *uidp
);
508 uint64_t js::gc::NextCellUniqueId(JSRuntime
* rt
) {
509 return rt
->gc
.nextCellUniqueId();
514 static const struct GCParamInfo
{
519 #define DEFINE_PARAM_INFO(name, key, writable) {name, key, writable},
520 FOR_EACH_GC_PARAM(DEFINE_PARAM_INFO
)
521 #undef DEFINE_PARAM_INFO
524 bool GetGCParameterInfo(const char* name
, JSGCParamKey
* keyOut
,
527 MOZ_ASSERT(writableOut
);
529 for (const GCParamInfo
& info
: GCParameters
) {
530 if (strcmp(name
, info
.name
) == 0) {
532 *writableOut
= info
.writable
;
543 static bool GCBytesGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
544 CallArgs args
= CallArgsFromVp(argc
, vp
);
545 args
.rval().setNumber(double(cx
->runtime()->gc
.heapSize
.bytes()));
549 static bool MallocBytesGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
550 CallArgs args
= CallArgsFromVp(argc
, vp
);
552 for (ZonesIter
zone(cx
->runtime(), WithAtoms
); !zone
.done(); zone
.next()) {
553 bytes
+= zone
->mallocHeapSize
.bytes();
555 args
.rval().setNumber(bytes
);
559 static bool GCMaxBytesGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
560 CallArgs args
= CallArgsFromVp(argc
, vp
);
561 args
.rval().setNumber(double(cx
->runtime()->gc
.tunables
.gcMaxBytes()));
565 static bool GCHighFreqGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
566 CallArgs args
= CallArgsFromVp(argc
, vp
);
567 args
.rval().setBoolean(
568 cx
->runtime()->gc
.schedulingState
.inHighFrequencyGCMode());
572 static bool GCNumberGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
573 CallArgs args
= CallArgsFromVp(argc
, vp
);
574 args
.rval().setNumber(double(cx
->runtime()->gc
.gcNumber()));
578 static bool MajorGCCountGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
579 CallArgs args
= CallArgsFromVp(argc
, vp
);
580 args
.rval().setNumber(double(cx
->runtime()->gc
.majorGCCount()));
584 static bool MinorGCCountGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
585 CallArgs args
= CallArgsFromVp(argc
, vp
);
586 args
.rval().setNumber(double(cx
->runtime()->gc
.minorGCCount()));
590 static bool GCSliceCountGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
591 CallArgs args
= CallArgsFromVp(argc
, vp
);
592 args
.rval().setNumber(double(cx
->runtime()->gc
.gcSliceCount()));
596 static bool GCCompartmentCount(JSContext
* cx
, unsigned argc
, Value
* vp
) {
597 CallArgs args
= CallArgsFromVp(argc
, vp
);
599 for (ZonesIter
zone(cx
->runtime(), WithAtoms
); !zone
.done(); zone
.next()) {
600 count
+= zone
->compartments().length();
603 args
.rval().setNumber(double(count
));
607 static bool GCLastStartReason(JSContext
* cx
, unsigned argc
, Value
* vp
) {
608 CallArgs args
= CallArgsFromVp(argc
, vp
);
609 const char* reason
= ExplainGCReason(cx
->runtime()->gc
.lastStartReason());
610 RootedString
str(cx
, JS_NewStringCopyZ(cx
, reason
));
615 args
.rval().setString(str
);
619 static bool ZoneGCBytesGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
620 CallArgs args
= CallArgsFromVp(argc
, vp
);
621 args
.rval().setNumber(double(cx
->zone()->gcHeapSize
.bytes()));
625 static bool ZoneGCTriggerBytesGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
626 CallArgs args
= CallArgsFromVp(argc
, vp
);
627 args
.rval().setNumber(double(cx
->zone()->gcHeapThreshold
.startBytes()));
631 static bool ZoneGCAllocTriggerGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
632 CallArgs args
= CallArgsFromVp(argc
, vp
);
634 cx
->runtime()->gc
.schedulingState
.inHighFrequencyGCMode();
635 args
.rval().setNumber(
636 double(cx
->zone()->gcHeapThreshold
.eagerAllocTrigger(highFrequency
)));
640 static bool ZoneMallocBytesGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
641 CallArgs args
= CallArgsFromVp(argc
, vp
);
642 args
.rval().setNumber(double(cx
->zone()->mallocHeapSize
.bytes()));
646 static bool ZoneMallocTriggerBytesGetter(JSContext
* cx
, unsigned argc
,
648 CallArgs args
= CallArgsFromVp(argc
, vp
);
649 args
.rval().setNumber(double(cx
->zone()->mallocHeapThreshold
.startBytes()));
653 static bool ZoneGCNumberGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
654 CallArgs args
= CallArgsFromVp(argc
, vp
);
655 args
.rval().setNumber(double(cx
->runtime()->gc
.gcNumber()));
660 static bool DummyGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
661 CallArgs args
= CallArgsFromVp(argc
, vp
);
662 args
.rval().setUndefined();
667 } /* namespace MemInfo */
669 JSObject
* NewMemoryInfoObject(JSContext
* cx
) {
670 RootedObject
obj(cx
, JS_NewObject(cx
, nullptr));
675 using namespace MemInfo
;
679 } getters
[] = {{"gcBytes", GCBytesGetter
},
680 {"gcMaxBytes", GCMaxBytesGetter
},
681 {"mallocBytes", MallocBytesGetter
},
682 {"gcIsHighFrequencyMode", GCHighFreqGetter
},
683 {"gcNumber", GCNumberGetter
},
684 {"majorGCCount", MajorGCCountGetter
},
685 {"minorGCCount", MinorGCCountGetter
},
686 {"sliceCount", GCSliceCountGetter
},
687 {"compartmentCount", GCCompartmentCount
},
688 {"lastStartReason", GCLastStartReason
}};
690 for (auto pair
: getters
) {
691 JSNative getter
= pair
.getter
;
694 if (js::SupportDifferentialTesting()) {
695 getter
= DummyGetter
;
699 if (!JS_DefineProperty(cx
, obj
, pair
.name
, getter
, nullptr,
705 RootedObject
zoneObj(cx
, JS_NewObject(cx
, nullptr));
710 if (!JS_DefineProperty(cx
, obj
, "zone", zoneObj
, JSPROP_ENUMERATE
)) {
714 struct NamedZoneGetter
{
717 } zoneGetters
[] = {{"gcBytes", ZoneGCBytesGetter
},
718 {"gcTriggerBytes", ZoneGCTriggerBytesGetter
},
719 {"gcAllocTrigger", ZoneGCAllocTriggerGetter
},
720 {"mallocBytes", ZoneMallocBytesGetter
},
721 {"mallocTriggerBytes", ZoneMallocTriggerBytesGetter
},
722 {"gcNumber", ZoneGCNumberGetter
}};
724 for (auto pair
: zoneGetters
) {
725 JSNative getter
= pair
.getter
;
728 if (js::SupportDifferentialTesting()) {
729 getter
= DummyGetter
;
733 if (!JS_DefineProperty(cx
, zoneObj
, pair
.name
, getter
, nullptr,
742 const char* StateName(State state
) {
744 #define MAKE_CASE(name) \
750 MOZ_CRASH("Invalid gc::State enum value");
753 const char* StateName(JS::Zone::GCState state
) {
757 case JS::Zone::Prepare
:
759 case JS::Zone::MarkBlackOnly
:
760 return "MarkBlackOnly";
761 case JS::Zone::MarkBlackAndGray
:
762 return "MarkBlackAndGray";
763 case JS::Zone::Sweep
:
765 case JS::Zone::Finished
:
767 case JS::Zone::Compact
:
769 case JS::Zone::VerifyPreBarriers
:
770 return "VerifyPreBarriers";
771 case JS::Zone::Limit
:
774 MOZ_CRASH("Invalid Zone::GCState enum value");
780 JS_PUBLIC_API
void js::gc::FinalizeDeadNurseryObject(JSContext
* cx
,
783 MOZ_ASSERT(JS::RuntimeHeapIsMinorCollecting());
786 MOZ_ASSERT(IsInsideNursery(obj
));
787 MOZ_ASSERT(!IsForwarded(obj
));
789 const JSClass
* jsClass
= JS::GetClass(obj
);
790 jsClass
->doFinalize(cx
->gcContext(), obj
);
793 JS_PUBLIC_API
void js::gc::SetPerformanceHint(JSContext
* cx
,
794 PerformanceHint hint
) {
796 MOZ_ASSERT(!JS::RuntimeHeapIsCollecting());
798 cx
->runtime()->gc
.setPerformanceHint(hint
);
801 AutoSelectGCHeap::AutoSelectGCHeap(JSContext
* cx
,
802 size_t allowedNurseryCollections
)
803 : cx_(cx
), allowedNurseryCollections_(allowedNurseryCollections
) {
804 JS::AddGCNurseryCollectionCallback(cx
, &NurseryCollectionCallback
, this);
807 AutoSelectGCHeap::~AutoSelectGCHeap() {
808 JS::RemoveGCNurseryCollectionCallback(cx_
, &NurseryCollectionCallback
, this);
812 void AutoSelectGCHeap::NurseryCollectionCallback(JSContext
* cx
,
813 JS::GCNurseryProgress progress
,
816 if (progress
== JS::GCNurseryProgress::GC_NURSERY_COLLECTION_END
) {
817 static_cast<AutoSelectGCHeap
*>(data
)->onNurseryCollectionEnd();
821 void AutoSelectGCHeap::onNurseryCollectionEnd() {
822 if (allowedNurseryCollections_
!= 0) {
823 allowedNurseryCollections_
--;
827 heap_
= gc::Heap::Tenured
;