Backed out changeset 1e582a0e5593 (bug 1852921) for causing build bustages
[gecko.git] / js / src / gc / GCAPI.cpp
blob103c8b504996607646b656835816beba2c9a2a82
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/. */
7 /*
8 * API functions and methods used by the rest of SpiderMonkey and by embeddings.
9 */
11 #include "mozilla/TimeStamp.h"
13 #include "jsapi.h"
14 #include "jsfriendapi.h"
16 #include "gc/GC.h"
17 #include "gc/PublicIterators.h"
18 #include "jit/JitZone.h"
19 #include "js/HeapAPI.h"
20 #include "js/Value.h"
21 #include "util/DifferentialTesting.h"
22 #include "vm/HelperThreads.h"
23 #include "vm/Realm.h"
24 #include "vm/Scope.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"
31 using namespace js;
32 using namespace js::gc;
34 using mozilla::TimeStamp;
36 extern JS_PUBLIC_API bool js::AddRawValueRoot(JSContext* cx, Value* vp,
37 const char* name) {
38 MOZ_ASSERT(vp);
39 MOZ_ASSERT(name);
40 bool ok = cx->runtime()->gc.addRoot(vp, name);
41 if (!ok) {
42 JS_ReportOutOfMemory(cx);
44 return ok;
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)
56 : cx(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()) {
80 FinishGC(cx);
84 AutoDisableCompactingGC::~AutoDisableCompactingGC() {
85 MOZ_ASSERT(cx->compactingDisabledCount > 0);
86 --cx->compactingDisabledCount;
89 #ifdef DEBUG
91 /* Should only be called manually under gdb */
92 void PreventGCDuringInteractiveDebug() { TlsContext.get()->suppressGC++; }
94 #endif
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()) {
109 suppressGC_++;
112 #ifdef DEBUG
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) {
128 MOZ_ASSERT(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) {
135 if (!cell) {
136 MOZ_ASSERT(kind == JS::TraceKind::Null);
137 return;
140 MOZ_ASSERT(IsCellPointerValid(cell));
141 MOZ_ASSERT(cell->getTraceKind() == kind);
143 #endif
145 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
147 JS::AutoAssertNoGC::AutoAssertNoGC(JSContext* maybecx) {
148 if (maybecx) {
149 cx_ = maybecx;
150 } else if (TlsContext.initialized()) {
151 cx_ = TlsContext.get();
152 } else {
153 cx_ = nullptr;
155 if (cx_) {
156 cx_->inUnsafeRegion++;
160 JS::AutoAssertNoGC::~AutoAssertNoGC() {
161 if (cx_) {
162 MOZ_ASSERT(cx_->inUnsafeRegion > 0);
163 cx_->inUnsafeRegion--;
167 #endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
169 #ifdef DEBUG
171 JS::AutoEnterCycleCollection::AutoEnterCycleCollection(JSRuntime* rt)
172 : runtime_(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());
187 #endif // DEBUG
189 JS_PUBLIC_API const char* JS::GCTraceKindToAscii(JS::TraceKind kind) {
190 switch (kind) {
191 #define MAP_NAME(name, _0, _1, _2) \
192 case JS::TraceKind::name: \
193 return "JS " #name;
194 JS_FOR_EACH_TRACEKIND(MAP_NAME);
195 #undef MAP_NAME
196 default:
197 return "Invalid";
201 JS_PUBLIC_API size_t JS::GCTraceKindSize(JS::TraceKind kind) {
202 switch (kind) {
203 #define MAP_SIZE(name, type, _0, _1) \
204 case JS::TraceKind::name: \
205 return sizeof(type);
206 JS_FOR_EACH_TRACEKIND(MAP_SIZE);
207 #undef MAP_SIZE
208 default:
209 return 0;
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) {
223 AssertHeapIsIdle();
224 CHECK_THREAD(cx);
226 // If we got the zone from a shared atom, we may have the wrong atoms zone
227 // here.
228 if (zone->isAtomsZone()) {
229 zone = cx->runtime()->atomsZone();
232 MOZ_ASSERT(cx->runtime()->gc.hasZone(zone));
233 zone->scheduleGC();
236 JS_PUBLIC_API void JS::PrepareForFullGC(JSContext* cx) {
237 AssertHeapIsIdle();
238 CHECK_THREAD(cx);
240 cx->runtime()->gc.fullGCRequested = true;
241 for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) {
242 zone->scheduleGC();
246 JS_PUBLIC_API void JS::PrepareForIncrementalGC(JSContext* cx) {
247 AssertHeapIsIdle();
248 CHECK_THREAD(cx);
250 if (!JS::IsIncrementalGCInProgress(cx)) {
251 return;
254 for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) {
255 if (zone->wasGCStarted()) {
256 zone->scheduleGC();
261 JS_PUBLIC_API bool JS::IsGCScheduled(JSContext* cx) {
262 AssertHeapIsIdle();
263 CHECK_THREAD(cx);
265 for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) {
266 if (zone->isGCScheduled()) {
267 return true;
271 return false;
274 JS_PUBLIC_API void JS::SkipZoneForGC(JSContext* cx, Zone* zone) {
275 AssertHeapIsIdle();
276 CHECK_THREAD(cx);
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,
290 GCReason reason) {
291 AssertHeapIsIdle();
292 CHECK_THREAD(cx);
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,
301 GCReason reason,
302 const js::SliceBudget& budget) {
303 AssertHeapIsIdle();
304 CHECK_THREAD(cx);
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) {
312 AssertHeapIsIdle();
313 CHECK_THREAD(cx);
315 cx->runtime()->gc.gcSlice(reason, budget);
318 JS_PUBLIC_API bool JS::IncrementalGCHasForegroundWork(JSContext* cx) {
319 AssertHeapIsIdle();
320 CHECK_THREAD(cx);
322 return cx->runtime()->gc.hasForegroundWork();
325 JS_PUBLIC_API void JS::FinishIncrementalGC(JSContext* cx, GCReason reason) {
326 AssertHeapIsIdle();
327 CHECK_THREAD(cx);
329 cx->runtime()->gc.finishGC(reason);
332 JS_PUBLIC_API void JS::AbortIncrementalGC(JSContext* cx) {
333 AssertHeapIsIdle();
334 CHECK_THREAD(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));
346 if (!out) {
347 return nullptr;
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));
360 if (!out) {
361 return nullptr;
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()) {
451 return false;
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) {
459 if (!obj) {
460 return;
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) {
470 if (!thing) {
471 return;
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,
493 uint64_t* uidp) {
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
496 // tenured.
497 JSRuntime* runtime = cell->runtimeFromMainThread();
498 if (IsInsideNursery(cell) &&
499 !runtime->gc.nursery().addedUniqueIdToCell(cell)) {
500 return false;
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();
512 namespace js {
514 static const struct GCParamInfo {
515 const char* name;
516 JSGCParamKey key;
517 bool writable;
518 } GCParameters[] = {
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,
525 bool* writableOut) {
526 MOZ_ASSERT(keyOut);
527 MOZ_ASSERT(writableOut);
529 for (const GCParamInfo& info : GCParameters) {
530 if (strcmp(name, info.name) == 0) {
531 *keyOut = info.key;
532 *writableOut = info.writable;
533 return true;
537 return false;
540 namespace gc {
541 namespace MemInfo {
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()));
546 return true;
549 static bool MallocBytesGetter(JSContext* cx, unsigned argc, Value* vp) {
550 CallArgs args = CallArgsFromVp(argc, vp);
551 size_t bytes = 0;
552 for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) {
553 bytes += zone->mallocHeapSize.bytes();
555 args.rval().setNumber(bytes);
556 return true;
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()));
562 return true;
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());
569 return true;
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()));
575 return true;
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()));
581 return true;
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()));
587 return true;
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()));
593 return true;
596 static bool GCCompartmentCount(JSContext* cx, unsigned argc, Value* vp) {
597 CallArgs args = CallArgsFromVp(argc, vp);
598 size_t count = 0;
599 for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) {
600 count += zone->compartments().length();
603 args.rval().setNumber(double(count));
604 return true;
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));
611 if (!str) {
612 return false;
615 args.rval().setString(str);
616 return true;
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()));
622 return true;
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()));
628 return true;
631 static bool ZoneGCAllocTriggerGetter(JSContext* cx, unsigned argc, Value* vp) {
632 CallArgs args = CallArgsFromVp(argc, vp);
633 bool highFrequency =
634 cx->runtime()->gc.schedulingState.inHighFrequencyGCMode();
635 args.rval().setNumber(
636 double(cx->zone()->gcHeapThreshold.eagerAllocTrigger(highFrequency)));
637 return true;
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()));
643 return true;
646 static bool ZoneMallocTriggerBytesGetter(JSContext* cx, unsigned argc,
647 Value* vp) {
648 CallArgs args = CallArgsFromVp(argc, vp);
649 args.rval().setNumber(double(cx->zone()->mallocHeapThreshold.startBytes()));
650 return true;
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()));
656 return true;
659 #ifdef DEBUG
660 static bool DummyGetter(JSContext* cx, unsigned argc, Value* vp) {
661 CallArgs args = CallArgsFromVp(argc, vp);
662 args.rval().setUndefined();
663 return true;
665 #endif
667 } /* namespace MemInfo */
669 JSObject* NewMemoryInfoObject(JSContext* cx) {
670 RootedObject obj(cx, JS_NewObject(cx, nullptr));
671 if (!obj) {
672 return nullptr;
675 using namespace MemInfo;
676 struct NamedGetter {
677 const char* name;
678 JSNative getter;
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;
693 #ifdef DEBUG
694 if (js::SupportDifferentialTesting()) {
695 getter = DummyGetter;
697 #endif
699 if (!JS_DefineProperty(cx, obj, pair.name, getter, nullptr,
700 JSPROP_ENUMERATE)) {
701 return nullptr;
705 RootedObject zoneObj(cx, JS_NewObject(cx, nullptr));
706 if (!zoneObj) {
707 return nullptr;
710 if (!JS_DefineProperty(cx, obj, "zone", zoneObj, JSPROP_ENUMERATE)) {
711 return nullptr;
714 struct NamedZoneGetter {
715 const char* name;
716 JSNative getter;
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;
727 #ifdef DEBUG
728 if (js::SupportDifferentialTesting()) {
729 getter = DummyGetter;
731 #endif
733 if (!JS_DefineProperty(cx, zoneObj, pair.name, getter, nullptr,
734 JSPROP_ENUMERATE)) {
735 return nullptr;
739 return obj;
742 const char* StateName(State state) {
743 switch (state) {
744 #define MAKE_CASE(name) \
745 case State::name: \
746 return #name;
747 GCSTATES(MAKE_CASE)
748 #undef MAKE_CASE
750 MOZ_CRASH("Invalid gc::State enum value");
753 const char* StateName(JS::Zone::GCState state) {
754 switch (state) {
755 case JS::Zone::NoGC:
756 return "NoGC";
757 case JS::Zone::Prepare:
758 return "Prepare";
759 case JS::Zone::MarkBlackOnly:
760 return "MarkBlackOnly";
761 case JS::Zone::MarkBlackAndGray:
762 return "MarkBlackAndGray";
763 case JS::Zone::Sweep:
764 return "Sweep";
765 case JS::Zone::Finished:
766 return "Finished";
767 case JS::Zone::Compact:
768 return "Compact";
769 case JS::Zone::VerifyPreBarriers:
770 return "VerifyPreBarriers";
771 case JS::Zone::Limit:
772 break;
774 MOZ_CRASH("Invalid Zone::GCState enum value");
777 } /* namespace gc */
778 } /* namespace js */
780 JS_PUBLIC_API void js::gc::FinalizeDeadNurseryObject(JSContext* cx,
781 JSObject* obj) {
782 CHECK_THREAD(cx);
783 MOZ_ASSERT(JS::RuntimeHeapIsMinorCollecting());
785 MOZ_ASSERT(obj);
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) {
795 CHECK_THREAD(cx);
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);
811 /* static */
812 void AutoSelectGCHeap::NurseryCollectionCallback(JSContext* cx,
813 JS::GCNurseryProgress progress,
814 JS::GCReason reason,
815 void* data) {
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_--;
824 return;
827 heap_ = gc::Heap::Tenured;