Bug 1874684 - Part 10: Replace BigInt with Int128 in RoundNumberToIncrement. r=mgaudet
[gecko.git] / js / src / gc / GCAPI.cpp
blobd2eab44deaa76d97d6d4961e808aa3e3b81792da
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() { reset(); }
162 void JS::AutoAssertNoGC::reset() {
163 if (cx_) {
164 MOZ_ASSERT(cx_->inUnsafeRegion > 0);
165 cx_->inUnsafeRegion--;
166 cx_ = nullptr;
170 #endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
172 #ifdef DEBUG
174 JS::AutoEnterCycleCollection::AutoEnterCycleCollection(JSRuntime* rt)
175 : runtime_(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());
190 #endif // DEBUG
192 JS_PUBLIC_API const char* JS::GCTraceKindToAscii(JS::TraceKind kind) {
193 switch (kind) {
194 #define MAP_NAME(name, _0, _1, _2) \
195 case JS::TraceKind::name: \
196 return "JS " #name;
197 JS_FOR_EACH_TRACEKIND(MAP_NAME);
198 #undef MAP_NAME
199 default:
200 return "Invalid";
204 JS_PUBLIC_API size_t JS::GCTraceKindSize(JS::TraceKind kind) {
205 switch (kind) {
206 #define MAP_SIZE(name, type, _0, _1) \
207 case JS::TraceKind::name: \
208 return sizeof(type);
209 JS_FOR_EACH_TRACEKIND(MAP_SIZE);
210 #undef MAP_SIZE
211 default:
212 return 0;
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) {
226 AssertHeapIsIdle();
227 CHECK_THREAD(cx);
229 // If we got the zone from a shared atom, we may have the wrong atoms zone
230 // here.
231 if (zone->isAtomsZone()) {
232 zone = cx->runtime()->atomsZone();
235 MOZ_ASSERT(cx->runtime()->gc.hasZone(zone));
236 zone->scheduleGC();
239 JS_PUBLIC_API void JS::PrepareForFullGC(JSContext* cx) {
240 AssertHeapIsIdle();
241 CHECK_THREAD(cx);
243 cx->runtime()->gc.fullGCRequested = true;
244 for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) {
245 zone->scheduleGC();
249 JS_PUBLIC_API void JS::PrepareForIncrementalGC(JSContext* cx) {
250 AssertHeapIsIdle();
251 CHECK_THREAD(cx);
253 if (!JS::IsIncrementalGCInProgress(cx)) {
254 return;
257 for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) {
258 if (zone->wasGCStarted()) {
259 zone->scheduleGC();
264 JS_PUBLIC_API bool JS::IsGCScheduled(JSContext* cx) {
265 AssertHeapIsIdle();
266 CHECK_THREAD(cx);
268 for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) {
269 if (zone->isGCScheduled()) {
270 return true;
274 return false;
277 JS_PUBLIC_API void JS::SkipZoneForGC(JSContext* cx, Zone* zone) {
278 AssertHeapIsIdle();
279 CHECK_THREAD(cx);
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,
293 GCReason reason) {
294 AssertHeapIsIdle();
295 CHECK_THREAD(cx);
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,
304 GCReason reason,
305 const js::SliceBudget& budget) {
306 AssertHeapIsIdle();
307 CHECK_THREAD(cx);
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) {
315 AssertHeapIsIdle();
316 CHECK_THREAD(cx);
318 cx->runtime()->gc.gcSlice(reason, budget);
321 JS_PUBLIC_API bool JS::IncrementalGCHasForegroundWork(JSContext* cx) {
322 AssertHeapIsIdle();
323 CHECK_THREAD(cx);
325 return cx->runtime()->gc.hasForegroundWork();
328 JS_PUBLIC_API void JS::FinishIncrementalGC(JSContext* cx, GCReason reason) {
329 AssertHeapIsIdle();
330 CHECK_THREAD(cx);
332 cx->runtime()->gc.finishGC(reason);
335 JS_PUBLIC_API void JS::AbortIncrementalGC(JSContext* cx) {
336 AssertHeapIsIdle();
337 CHECK_THREAD(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));
349 if (!out) {
350 return nullptr;
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));
363 if (!out) {
364 return nullptr;
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()) {
454 return false;
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) {
462 if (!obj) {
463 return;
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) {
473 if (!thing) {
474 return;
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,
495 uint64_t* uidp) {
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
498 // tenured.
499 JSRuntime* runtime = cell->runtimeFromMainThread();
500 if (IsInsideNursery(cell) &&
501 !runtime->gc.nursery().addedUniqueIdToCell(cell)) {
502 return false;
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();
514 namespace js {
516 static const struct GCParamInfo {
517 const char* name;
518 JSGCParamKey key;
519 bool writable;
520 } GCParameters[] = {
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,
527 bool* writableOut) {
528 MOZ_ASSERT(keyOut);
529 MOZ_ASSERT(writableOut);
531 for (const GCParamInfo& info : GCParameters) {
532 if (strcmp(name, info.name) == 0) {
533 *keyOut = info.key;
534 *writableOut = info.writable;
535 return true;
539 return false;
542 namespace gc {
543 namespace MemInfo {
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()));
548 return true;
551 static bool MallocBytesGetter(JSContext* cx, unsigned argc, Value* vp) {
552 CallArgs args = CallArgsFromVp(argc, vp);
553 size_t bytes = 0;
554 for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) {
555 bytes += zone->mallocHeapSize.bytes();
557 args.rval().setNumber(bytes);
558 return true;
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()));
564 return true;
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());
571 return true;
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()));
577 return true;
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()));
583 return true;
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()));
589 return true;
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()));
595 return true;
598 static bool GCCompartmentCount(JSContext* cx, unsigned argc, Value* vp) {
599 CallArgs args = CallArgsFromVp(argc, vp);
600 size_t count = 0;
601 for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) {
602 count += zone->compartments().length();
605 args.rval().setNumber(double(count));
606 return true;
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));
613 if (!str) {
614 return false;
617 args.rval().setString(str);
618 return true;
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()));
624 return true;
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()));
630 return true;
633 static bool ZoneGCAllocTriggerGetter(JSContext* cx, unsigned argc, Value* vp) {
634 CallArgs args = CallArgsFromVp(argc, vp);
635 bool highFrequency =
636 cx->runtime()->gc.schedulingState.inHighFrequencyGCMode();
637 args.rval().setNumber(
638 double(cx->zone()->gcHeapThreshold.eagerAllocTrigger(highFrequency)));
639 return true;
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()));
645 return true;
648 static bool ZoneMallocTriggerBytesGetter(JSContext* cx, unsigned argc,
649 Value* vp) {
650 CallArgs args = CallArgsFromVp(argc, vp);
651 args.rval().setNumber(double(cx->zone()->mallocHeapThreshold.startBytes()));
652 return true;
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()));
658 return true;
661 #ifdef DEBUG
662 static bool DummyGetter(JSContext* cx, unsigned argc, Value* vp) {
663 CallArgs args = CallArgsFromVp(argc, vp);
664 args.rval().setUndefined();
665 return true;
667 #endif
669 } /* namespace MemInfo */
671 JSObject* NewMemoryInfoObject(JSContext* cx) {
672 RootedObject obj(cx, JS_NewObject(cx, nullptr));
673 if (!obj) {
674 return nullptr;
677 using namespace MemInfo;
678 struct NamedGetter {
679 const char* name;
680 JSNative getter;
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;
695 #ifdef DEBUG
696 if (js::SupportDifferentialTesting()) {
697 getter = DummyGetter;
699 #endif
701 if (!JS_DefineProperty(cx, obj, pair.name, getter, nullptr,
702 JSPROP_ENUMERATE)) {
703 return nullptr;
707 RootedObject zoneObj(cx, JS_NewObject(cx, nullptr));
708 if (!zoneObj) {
709 return nullptr;
712 if (!JS_DefineProperty(cx, obj, "zone", zoneObj, JSPROP_ENUMERATE)) {
713 return nullptr;
716 struct NamedZoneGetter {
717 const char* name;
718 JSNative getter;
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;
729 #ifdef DEBUG
730 if (js::SupportDifferentialTesting()) {
731 getter = DummyGetter;
733 #endif
735 if (!JS_DefineProperty(cx, zoneObj, pair.name, getter, nullptr,
736 JSPROP_ENUMERATE)) {
737 return nullptr;
741 return obj;
744 const char* StateName(State state) {
745 switch (state) {
746 #define MAKE_CASE(name) \
747 case State::name: \
748 return #name;
749 GCSTATES(MAKE_CASE)
750 #undef MAKE_CASE
752 MOZ_CRASH("Invalid gc::State enum value");
755 const char* StateName(JS::Zone::GCState state) {
756 switch (state) {
757 case JS::Zone::NoGC:
758 return "NoGC";
759 case JS::Zone::Prepare:
760 return "Prepare";
761 case JS::Zone::MarkBlackOnly:
762 return "MarkBlackOnly";
763 case JS::Zone::MarkBlackAndGray:
764 return "MarkBlackAndGray";
765 case JS::Zone::Sweep:
766 return "Sweep";
767 case JS::Zone::Finished:
768 return "Finished";
769 case JS::Zone::Compact:
770 return "Compact";
771 case JS::Zone::VerifyPreBarriers:
772 return "VerifyPreBarriers";
773 case JS::Zone::Limit:
774 break;
776 MOZ_CRASH("Invalid Zone::GCState enum value");
779 const char* CellColorName(CellColor color) {
780 switch (color) {
781 case CellColor::White:
782 return "white";
783 case CellColor::Black:
784 return "black";
785 case CellColor::Gray:
786 return "gray";
787 default:
788 MOZ_CRASH("Unexpected cell color");
792 } /* namespace gc */
793 } /* namespace js */
795 JS_PUBLIC_API bool js::gc::IsDeadNurseryObject(JSObject* obj) {
796 MOZ_ASSERT(JS::RuntimeHeapIsMinorCollecting());
797 MOZ_ASSERT(obj);
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,
805 JSObject* obj) {
806 CHECK_THREAD(cx);
807 MOZ_ASSERT(JS::RuntimeHeapIsMinorCollecting());
809 MOZ_ASSERT(obj);
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) {
819 CHECK_THREAD(cx);
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,
829 this)) {
830 cx_ = nullptr;
834 AutoSelectGCHeap::~AutoSelectGCHeap() {
835 if (cx_) {
836 JS::RemoveGCNurseryCollectionCallback(cx_, &NurseryCollectionCallback,
837 this);
841 /* static */
842 void AutoSelectGCHeap::NurseryCollectionCallback(JSContext* cx,
843 JS::GCNurseryProgress progress,
844 JS::GCReason reason,
845 void* data) {
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_--;
854 return;
857 heap_ = gc::Heap::Tenured;
860 JS_PUBLIC_API void js::gc::LockStoreBuffer(JSRuntime* runtime) {
861 MOZ_ASSERT(runtime);
862 runtime->gc.lockStoreBuffer();
865 JS_PUBLIC_API void js::gc::UnlockStoreBuffer(JSRuntime* runtime) {
866 MOZ_ASSERT(runtime);
867 runtime->gc.unlockStoreBuffer();