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 // We're dividing JS objects into 3 categories:
9 // 1. "real" roots, held by the JS engine itself or rooted through the root
10 // and lock JS APIs. Roots from this category are considered black in the
11 // cycle collector, any cycle they participate in is uncollectable.
13 // 2. certain roots held by C++ objects that are guaranteed to be alive.
14 // Roots from this category are considered black in the cycle collector,
15 // and any cycle they participate in is uncollectable. These roots are
16 // traced from TraceNativeBlackRoots.
18 // 3. all other roots held by C++ objects that participate in cycle collection,
19 // held by us (see TraceNativeGrayRoots). Roots from this category are
20 // considered grey in the cycle collector; whether or not they are collected
21 // depends on the objects that hold them.
23 // Note that if a root is in multiple categories the fact that it is in
24 // category 1 or 2 that takes precedence, so it will be considered black.
26 // During garbage collection we switch to an additional mark color (gray) when
27 // tracing inside TraceNativeGrayRoots. This allows us to walk those roots later
28 // on and add all objects reachable only from them to the cycle collector.
32 // 1. marking of the roots in category 1 by having the JS GC do its marking
33 // 2. marking of the roots in category 2 by having the JS GC call us back
34 // (via JS_SetExtraGCRootsTracer) and running TraceNativeBlackRoots
35 // 3. marking of the roots in category 3 by
36 // TraceNativeGrayRootsInCollectingZones using an additional color (gray).
37 // 4. end of GC, GC can sweep its heap
39 // At some later point, when the cycle collector runs:
41 // 5. walk gray objects and add them to the cycle collector, cycle collect
43 // JS objects that are part of cycles the cycle collector breaks will be
44 // collected by the next JS GC.
46 // If WantAllTraces() is false the cycle collector will not traverse roots
47 // from category 1 or any JS objects held by them. Any JS objects they hold
48 // will already be marked by the JS GC and will thus be colored black
49 // themselves. Any C++ objects they hold will have a missing (untraversed)
50 // edge from the JS object to the C++ object and so it will be marked black
51 // too. This decreases the number of objects that the cycle collector has to
53 // To improve debugging, if WantAllTraces() is true all JS objects are
56 #include "mozilla/CycleCollectedJSRuntime.h"
62 #include "js/RealmOptions.h"
63 #include "js/friend/DumpFunctions.h" // js::DumpHeap
65 #include "js/HeapAPI.h"
66 #include "js/Object.h" // JS::GetClass, JS::GetCompartment, JS::GetPrivate
67 #include "js/PropertyAndElement.h" // JS_DefineProperty
68 #include "js/Warnings.h" // JS::SetWarningReporter
69 #include "js/ShadowRealmCallbacks.h"
70 #include "js/SliceBudget.h"
71 #include "jsfriendapi.h"
72 #include "mozilla/ArrayUtils.h"
73 #include "mozilla/AutoRestore.h"
74 #include "mozilla/CycleCollectedJSContext.h"
75 #include "mozilla/DebuggerOnGCRunnable.h"
76 #include "mozilla/MemoryReporting.h"
77 #include "mozilla/PerfStats.h"
78 #include "mozilla/ProfilerLabels.h"
79 #include "mozilla/ProfilerMarkers.h"
80 #include "mozilla/Sprintf.h"
81 #include "mozilla/StaticPrefs_javascript.h"
82 #include "mozilla/Telemetry.h"
83 #include "mozilla/Unused.h"
84 #include "mozilla/dom/AutoEntryScript.h"
85 #include "mozilla/dom/DOMJSClass.h"
86 #include "mozilla/dom/JSExecutionManager.h"
87 #include "mozilla/dom/Promise.h"
88 #include "mozilla/dom/PromiseBinding.h"
89 #include "mozilla/dom/PromiseDebugging.h"
90 #include "mozilla/dom/ScriptSettings.h"
91 #include "mozilla/dom/ShadowRealmGlobalScope.h"
92 #include "mozilla/dom/RegisterShadowRealmBindings.h"
93 #include "nsContentUtils.h"
94 #include "nsCycleCollectionNoteRootCallback.h"
95 #include "nsCycleCollectionParticipant.h"
96 #include "nsCycleCollector.h"
97 #include "nsDOMJSUtils.h"
98 #include "nsExceptionHandler.h"
99 #include "nsJSUtils.h"
100 #include "nsWrapperCache.h"
103 #if defined(XP_MACOSX)
104 # include "nsMacUtilsImpl.h"
107 #include "nsThread.h"
108 #include "nsThreadUtils.h"
109 #include "xpcpublic.h"
112 // For performance reasons, we make the JS Dev Error Interceptor a Nightly-only
114 # define MOZ_JS_DEV_ERROR_INTERCEPTOR = 1
115 #endif // NIGHTLY_BUILD
117 using namespace mozilla
;
118 using namespace mozilla::dom
;
122 struct DeferredFinalizeFunctionHolder
{
123 DeferredFinalizeFunction run
;
127 class IncrementalFinalizeRunnable
: public DiscardableRunnable
{
128 typedef AutoTArray
<DeferredFinalizeFunctionHolder
, 16> DeferredFinalizeArray
;
129 typedef CycleCollectedJSRuntime::DeferredFinalizerTable
130 DeferredFinalizerTable
;
132 CycleCollectedJSRuntime
* mRuntime
;
133 DeferredFinalizeArray mDeferredFinalizeFunctions
;
134 uint32_t mFinalizeFunctionToRun
;
137 static const PRTime SliceMillis
= 5; /* ms */
140 IncrementalFinalizeRunnable(CycleCollectedJSRuntime
* aRt
,
141 DeferredFinalizerTable
& aFinalizerTable
);
142 virtual ~IncrementalFinalizeRunnable();
144 void ReleaseNow(bool aLimited
);
149 } // namespace mozilla
151 struct NoteWeakMapChildrenTracer
: public JS::CallbackTracer
{
152 NoteWeakMapChildrenTracer(JSRuntime
* aRt
,
153 nsCycleCollectionNoteRootCallback
& aCb
)
154 : JS::CallbackTracer(aRt
, JS::TracerKind::Callback
),
159 mKeyDelegate(nullptr) {}
160 void onChild(JS::GCCellPtr aThing
, const char* name
) override
;
161 nsCycleCollectionNoteRootCallback
& mCb
;
165 JSObject
* mKeyDelegate
;
168 void NoteWeakMapChildrenTracer::onChild(JS::GCCellPtr aThing
,
170 if (aThing
.is
<JSString
>()) {
174 if (!JS::GCThingIsMarkedGrayInCC(aThing
) && !mCb
.WantAllTraces()) {
178 if (JS::IsCCTraceKind(aThing
.kind())) {
179 mCb
.NoteWeakMapping(mMap
, mKey
, mKeyDelegate
, aThing
);
182 JS::TraceChildren(this, aThing
);
186 struct NoteWeakMapsTracer
: public js::WeakMapTracer
{
187 NoteWeakMapsTracer(JSRuntime
* aRt
, nsCycleCollectionNoteRootCallback
& aCccb
)
188 : js::WeakMapTracer(aRt
), mCb(aCccb
), mChildTracer(aRt
, aCccb
) {}
189 void trace(JSObject
* aMap
, JS::GCCellPtr aKey
, JS::GCCellPtr aValue
) override
;
190 nsCycleCollectionNoteRootCallback
& mCb
;
191 NoteWeakMapChildrenTracer mChildTracer
;
194 void NoteWeakMapsTracer::trace(JSObject
* aMap
, JS::GCCellPtr aKey
,
195 JS::GCCellPtr aValue
) {
196 // If nothing that could be held alive by this entry is marked gray, return.
197 if ((!aKey
|| !JS::GCThingIsMarkedGrayInCC(aKey
)) &&
198 MOZ_LIKELY(!mCb
.WantAllTraces())) {
199 if (!aValue
|| !JS::GCThingIsMarkedGrayInCC(aValue
) ||
200 aValue
.is
<JSString
>()) {
205 // The cycle collector can only properly reason about weak maps if it can
206 // reason about the liveness of their keys, which in turn requires that
207 // the key can be represented in the cycle collector graph. All existing
208 // uses of weak maps use either objects or scripts as keys, which are okay.
209 MOZ_ASSERT(JS::IsCCTraceKind(aKey
.kind()));
211 // As an emergency fallback for non-debug builds, if the key is not
212 // representable in the cycle collector graph, we treat it as marked. This
213 // can cause leaks, but is preferable to ignoring the binding, which could
214 // cause the cycle collector to free live objects.
215 if (!JS::IsCCTraceKind(aKey
.kind())) {
219 JSObject
* kdelegate
= nullptr;
220 if (aKey
.is
<JSObject
>()) {
221 kdelegate
= js::UncheckedUnwrapWithoutExpose(&aKey
.as
<JSObject
>());
224 if (JS::IsCCTraceKind(aValue
.kind())) {
225 mCb
.NoteWeakMapping(aMap
, aKey
, kdelegate
, aValue
);
227 mChildTracer
.mTracedAny
= false;
228 mChildTracer
.mMap
= aMap
;
229 mChildTracer
.mKey
= aKey
;
230 mChildTracer
.mKeyDelegate
= kdelegate
;
232 if (!aValue
.is
<JSString
>()) {
233 JS::TraceChildren(&mChildTracer
, aValue
);
236 // The delegate could hold alive the key, so report something to the CC
237 // if we haven't already.
238 if (!mChildTracer
.mTracedAny
&& aKey
&& JS::GCThingIsMarkedGrayInCC(aKey
) &&
240 mCb
.NoteWeakMapping(aMap
, aKey
, kdelegate
, nullptr);
245 // Report whether the key or value of a weak mapping entry are gray but need to
247 static void ShouldWeakMappingEntryBeBlack(JSObject
* aMap
, JS::GCCellPtr aKey
,
248 JS::GCCellPtr aValue
,
249 bool* aKeyShouldBeBlack
,
250 bool* aValueShouldBeBlack
) {
251 *aKeyShouldBeBlack
= false;
252 *aValueShouldBeBlack
= false;
254 // If nothing that could be held alive by this entry is marked gray, return.
255 bool keyMightNeedMarking
= aKey
&& JS::GCThingIsMarkedGrayInCC(aKey
);
256 bool valueMightNeedMarking
= aValue
&& JS::GCThingIsMarkedGrayInCC(aValue
) &&
257 aValue
.kind() != JS::TraceKind::String
;
258 if (!keyMightNeedMarking
&& !valueMightNeedMarking
) {
262 if (!JS::IsCCTraceKind(aKey
.kind())) {
266 if (keyMightNeedMarking
&& aKey
.is
<JSObject
>()) {
267 JSObject
* kdelegate
=
268 js::UncheckedUnwrapWithoutExpose(&aKey
.as
<JSObject
>());
269 if (kdelegate
&& !JS::ObjectIsMarkedGray(kdelegate
) &&
270 (!aMap
|| !JS::ObjectIsMarkedGray(aMap
))) {
271 *aKeyShouldBeBlack
= true;
275 if (aValue
&& JS::GCThingIsMarkedGrayInCC(aValue
) &&
276 (!aKey
|| !JS::GCThingIsMarkedGrayInCC(aKey
)) &&
277 (!aMap
|| !JS::ObjectIsMarkedGray(aMap
)) &&
278 aValue
.kind() != JS::TraceKind::Shape
) {
279 *aValueShouldBeBlack
= true;
283 struct FixWeakMappingGrayBitsTracer
: public js::WeakMapTracer
{
284 explicit FixWeakMappingGrayBitsTracer(JSRuntime
* aRt
)
285 : js::WeakMapTracer(aRt
) {}
290 js::TraceWeakMaps(this);
291 } while (mAnyMarked
);
294 void trace(JSObject
* aMap
, JS::GCCellPtr aKey
,
295 JS::GCCellPtr aValue
) override
{
296 bool keyShouldBeBlack
;
297 bool valueShouldBeBlack
;
298 ShouldWeakMappingEntryBeBlack(aMap
, aKey
, aValue
, &keyShouldBeBlack
,
299 &valueShouldBeBlack
);
300 if (keyShouldBeBlack
&& JS::UnmarkGrayGCThingRecursively(aKey
)) {
304 if (valueShouldBeBlack
&& JS::UnmarkGrayGCThingRecursively(aValue
)) {
309 MOZ_INIT_OUTSIDE_CTOR
bool mAnyMarked
;
313 // Check whether weak maps are marked correctly according to the logic above.
314 struct CheckWeakMappingGrayBitsTracer
: public js::WeakMapTracer
{
315 explicit CheckWeakMappingGrayBitsTracer(JSRuntime
* aRt
)
316 : js::WeakMapTracer(aRt
), mFailed(false) {}
318 static bool Check(JSRuntime
* aRt
) {
319 CheckWeakMappingGrayBitsTracer
tracer(aRt
);
320 js::TraceWeakMaps(&tracer
);
321 return !tracer
.mFailed
;
324 void trace(JSObject
* aMap
, JS::GCCellPtr aKey
,
325 JS::GCCellPtr aValue
) override
{
326 bool keyShouldBeBlack
;
327 bool valueShouldBeBlack
;
328 ShouldWeakMappingEntryBeBlack(aMap
, aKey
, aValue
, &keyShouldBeBlack
,
329 &valueShouldBeBlack
);
331 if (keyShouldBeBlack
) {
332 fprintf(stderr
, "Weak mapping key %p of map %p should be black\n",
333 aKey
.asCell(), aMap
);
337 if (valueShouldBeBlack
) {
338 fprintf(stderr
, "Weak mapping value %p of map %p should be black\n",
339 aValue
.asCell(), aMap
);
348 static void CheckParticipatesInCycleCollection(JS::GCCellPtr aThing
,
351 bool* cycleCollectionEnabled
= static_cast<bool*>(aClosure
);
353 if (*cycleCollectionEnabled
) {
357 if (JS::IsCCTraceKind(aThing
.kind()) && JS::GCThingIsMarkedGrayInCC(aThing
)) {
358 *cycleCollectionEnabled
= true;
363 JSGCThingParticipant::TraverseNative(void* aPtr
,
364 nsCycleCollectionTraversalCallback
& aCb
) {
365 auto runtime
= reinterpret_cast<CycleCollectedJSRuntime
*>(
366 reinterpret_cast<char*>(this) -
367 offsetof(CycleCollectedJSRuntime
, mGCThingCycleCollectorGlobal
));
369 JS::GCCellPtr
cellPtr(aPtr
, JS::GCThingTraceKind(aPtr
));
370 runtime
->TraverseGCThing(CycleCollectedJSRuntime::TRAVERSE_FULL
, cellPtr
,
375 // NB: This is only used to initialize the participant in
376 // CycleCollectedJSRuntime. It should never be used directly.
377 static JSGCThingParticipant sGCThingCycleCollectorGlobal
;
380 JSZoneParticipant::TraverseNative(void* aPtr
,
381 nsCycleCollectionTraversalCallback
& aCb
) {
382 auto runtime
= reinterpret_cast<CycleCollectedJSRuntime
*>(
383 reinterpret_cast<char*>(this) -
384 offsetof(CycleCollectedJSRuntime
, mJSZoneCycleCollectorGlobal
));
386 MOZ_ASSERT(!aCb
.WantAllTraces());
387 JS::Zone
* zone
= static_cast<JS::Zone
*>(aPtr
);
389 runtime
->TraverseZone(zone
, aCb
);
393 struct TraversalTracer
: public JS::CallbackTracer
{
394 TraversalTracer(JSRuntime
* aRt
, nsCycleCollectionTraversalCallback
& aCb
)
395 : JS::CallbackTracer(aRt
, JS::TracerKind::Callback
,
396 JS::TraceOptions(JS::WeakMapTraceAction::Skip
,
397 JS::WeakEdgeTraceAction::Trace
)),
399 void onChild(JS::GCCellPtr aThing
, const char* name
) override
;
400 nsCycleCollectionTraversalCallback
& mCb
;
403 void TraversalTracer::onChild(JS::GCCellPtr aThing
, const char* name
) {
404 // Checking strings and symbols for being gray is rather slow, and we don't
405 // need either of them for the cycle collector.
406 if (aThing
.is
<JSString
>() || aThing
.is
<JS::Symbol
>()) {
410 // Don't traverse non-gray objects, unless we want all traces.
411 if (!JS::GCThingIsMarkedGrayInCC(aThing
) && !mCb
.WantAllTraces()) {
416 * This function needs to be careful to avoid stack overflow. Normally, when
417 * IsCCTraceKind is true, the recursion terminates immediately as we just add
418 * |thing| to the CC graph. So overflow is only possible when there are long
419 * or cyclic chains of non-IsCCTraceKind GC things. Places where this can
420 * occur use special APIs to handle such chains iteratively.
422 if (JS::IsCCTraceKind(aThing
.kind())) {
423 if (MOZ_UNLIKELY(mCb
.WantDebugInfo())) {
425 context().getEdgeName(name
, buffer
, sizeof(buffer
));
426 mCb
.NoteNextEdgeName(buffer
);
428 mCb
.NoteJSChild(aThing
);
432 // Allow re-use of this tracer inside trace callback.
433 JS::AutoClearTracingContext
actc(this);
435 if (aThing
.is
<js::Shape
>()) {
436 // The maximum depth of traversal when tracing a Shape is unbounded, due to
437 // the parent pointers on the shape.
438 JS_TraceShapeCycleCollectorChildren(this, aThing
);
440 JS::TraceChildren(this, aThing
);
445 * The cycle collection participant for a Zone is intended to produce the same
446 * results as if all of the gray GCthings in a zone were merged into a single
447 * node, except for self-edges. This avoids the overhead of representing all of
448 * the GCthings in the zone in the cycle collector graph, which should be much
449 * faster if many of the GCthings in the zone are gray.
451 * Zone merging should not always be used, because it is a conservative
452 * approximation of the true cycle collector graph that can incorrectly identify
453 * some garbage objects as being live. For instance, consider two cycles that
454 * pass through a zone, where one is garbage and the other is live. If we merge
455 * the entire zone, the cycle collector will think that both are alive.
457 * We don't have to worry about losing track of a garbage cycle, because any
458 * such garbage cycle incorrectly identified as live must contain at least one
459 * C++ to JS edge, and XPConnect will always add the C++ object to the CC graph.
460 * (This is in contrast to pure C++ garbage cycles, which must always be
461 * properly identified, because we clear the purple buffer during every CC,
462 * which may contain the last reference to a garbage cycle.)
465 // NB: This is only used to initialize the participant in
466 // CycleCollectedJSRuntime. It should never be used directly.
467 static const JSZoneParticipant sJSZoneCycleCollectorGlobal
;
469 static void JSObjectsTenuredCb(JS::GCContext
* aGCContext
, void* aData
) {
470 static_cast<CycleCollectedJSRuntime
*>(aData
)->JSObjectsTenured(aGCContext
);
473 static void MozCrashWarningReporter(JSContext
*, JSErrorReport
*) {
474 MOZ_CRASH("Why is someone touching JSAPI without an AutoJSAPI?");
477 JSHolderMap::Entry::Entry() : Entry(nullptr, nullptr, nullptr) {}
479 JSHolderMap::Entry::Entry(void* aHolder
, nsScriptObjectTracer
* aTracer
,
490 void JSHolderMap::EntryVectorIter::Settle() {
495 Entry
* entry
= &mIter
.Get();
497 // If the entry has been cleared, remove it and shrink the vector.
498 if (!entry
->mHolder
&& !mHolderMap
.RemoveEntry(mVector
, entry
)) {
499 // We removed the last entry, so reset the iterator to an empty one.
500 mIter
= EntryVector().Iter();
505 JSHolderMap::Iter::Iter(JSHolderMap
& aMap
, WhichHolders aWhich
)
506 : mHolderMap(aMap
), mIter(aMap
, aMap
.mAnyZoneJSHolders
) {
507 MOZ_RELEASE_ASSERT(!mHolderMap
.mHasIterator
);
508 mHolderMap
.mHasIterator
= true;
510 // Populate vector of zones to iterate after the any-zone holders.
511 for (auto i
= aMap
.mPerZoneJSHolders
.iter(); !i
.done(); i
.next()) {
512 JS::Zone
* zone
= i
.get().key();
513 if (aWhich
== AllHolders
|| JS::NeedGrayRootsForZone(i
.get().key())) {
514 MOZ_ALWAYS_TRUE(mZones
.append(zone
));
521 void JSHolderMap::Iter::Settle() {
522 while (mIter
.Done()) {
523 if (mZone
&& mIter
.Vector().IsEmpty()) {
524 mHolderMap
.mPerZoneJSHolders
.remove(mZone
);
528 if (mZones
.empty()) {
532 mZone
= mZones
.popCopy();
533 EntryVector
& vector
= *mHolderMap
.mPerZoneJSHolders
.lookup(mZone
)->value();
534 new (&mIter
) EntryVectorIter(mHolderMap
, vector
);
538 void JSHolderMap::Iter::UpdateForRemovals() {
543 JSHolderMap::JSHolderMap() : mJSHolderMap(256) {}
545 bool JSHolderMap::RemoveEntry(EntryVector
& aJSHolders
, Entry
* aEntry
) {
547 MOZ_ASSERT(!aEntry
->mHolder
);
549 // Remove all dead entries from the end of the vector.
550 while (!aJSHolders
.GetLast().mHolder
&& &aJSHolders
.GetLast() != aEntry
) {
551 aJSHolders
.PopLast();
554 // Swap the element we want to remove with the last one and update the hash
556 Entry
* lastEntry
= &aJSHolders
.GetLast();
557 if (aEntry
!= lastEntry
) {
558 MOZ_ASSERT(lastEntry
->mHolder
);
559 *aEntry
= *lastEntry
;
560 MOZ_ASSERT(mJSHolderMap
.has(aEntry
->mHolder
));
561 MOZ_ALWAYS_TRUE(mJSHolderMap
.put(aEntry
->mHolder
, aEntry
));
564 aJSHolders
.PopLast();
566 // Return whether aEntry is still in the vector.
567 return aEntry
!= lastEntry
;
570 bool JSHolderMap::Has(void* aHolder
) const { return mJSHolderMap
.has(aHolder
); }
572 nsScriptObjectTracer
* JSHolderMap::Get(void* aHolder
) const {
573 auto ptr
= mJSHolderMap
.lookup(aHolder
);
578 Entry
* entry
= ptr
->value();
579 MOZ_ASSERT(entry
->mHolder
== aHolder
);
580 return entry
->mTracer
;
583 nsScriptObjectTracer
* JSHolderMap::Extract(void* aHolder
) {
586 auto ptr
= mJSHolderMap
.lookup(aHolder
);
591 Entry
* entry
= ptr
->value();
592 MOZ_ASSERT(entry
->mHolder
== aHolder
);
593 nsScriptObjectTracer
* tracer
= entry
->mTracer
;
595 // Clear the entry's contents. It will be removed the next time iteration
596 // visits this entry.
599 mJSHolderMap
.remove(ptr
);
604 void JSHolderMap::Put(void* aHolder
, nsScriptObjectTracer
* aTracer
,
609 // Don't associate multi-zone holders with a zone, even if one is supplied.
610 if (!aTracer
->IsSingleZoneJSHolder()) {
614 auto ptr
= mJSHolderMap
.lookupForAdd(aHolder
);
616 Entry
* entry
= ptr
->value();
618 MOZ_ASSERT(entry
->mHolder
== aHolder
);
619 MOZ_ASSERT(entry
->mTracer
== aTracer
,
620 "Don't call HoldJSObjects in superclass ctors");
623 MOZ_ASSERT(entry
->mZone
== aZone
);
625 entry
->mZone
= aZone
;
629 entry
->mTracer
= aTracer
;
633 EntryVector
* vector
= &mAnyZoneJSHolders
;
635 auto ptr
= mPerZoneJSHolders
.lookupForAdd(aZone
);
638 mPerZoneJSHolders
.add(ptr
, aZone
, MakeUnique
<EntryVector
>()));
640 vector
= ptr
->value().get();
643 vector
->InfallibleAppend(Entry
{aHolder
, aTracer
, aZone
});
644 MOZ_ALWAYS_TRUE(mJSHolderMap
.add(ptr
, aHolder
, &vector
->GetLast()));
647 size_t JSHolderMap::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf
) const {
650 // We're deliberately not measuring anything hanging off the entries in
652 n
+= mJSHolderMap
.shallowSizeOfExcludingThis(aMallocSizeOf
);
653 n
+= mAnyZoneJSHolders
.SizeOfExcludingThis(aMallocSizeOf
);
654 n
+= mPerZoneJSHolders
.shallowSizeOfExcludingThis(aMallocSizeOf
);
655 for (auto i
= mPerZoneJSHolders
.iter(); !i
.done(); i
.next()) {
656 n
+= i
.get().value()->SizeOfExcludingThis(aMallocSizeOf
);
662 static bool InitializeShadowRealm(JSContext
* aCx
,
663 JS::Handle
<JSObject
*> aGlobal
) {
664 MOZ_ASSERT(StaticPrefs::javascript_options_experimental_shadow_realms());
666 JSAutoRealm
ar(aCx
, aGlobal
);
667 return dom::RegisterShadowRealmBindings(aCx
, aGlobal
);
670 CycleCollectedJSRuntime::CycleCollectedJSRuntime(JSContext
* aCx
)
672 mGCThingCycleCollectorGlobal(sGCThingCycleCollectorGlobal
),
673 mJSZoneCycleCollectorGlobal(sJSZoneCycleCollectorGlobal
),
674 mJSRuntime(JS_GetRuntime(aCx
)),
675 mHasPendingIdleGCTask(false),
676 mPrevGCSliceCallback(nullptr),
677 mOutOfMemoryState(OOMState::OK
),
678 mLargeAllocationFailureState(OOMState::OK
)
681 mShutdownCalled(false)
684 MOZ_COUNT_CTOR(CycleCollectedJSRuntime
);
686 MOZ_ASSERT(mJSRuntime
);
688 #if defined(XP_MACOSX)
689 if (!XRE_IsParentProcess()) {
690 nsMacUtilsImpl::EnableTCSMIfAvailable();
694 if (!JS_AddExtraGCRootsTracer(aCx
, TraceBlackJS
, this)) {
695 MOZ_CRASH("JS_AddExtraGCRootsTracer failed");
697 JS_SetGrayGCRootsTracer(aCx
, TraceGrayJS
, this);
698 JS_SetGCCallback(aCx
, GCCallback
, this);
699 mPrevGCSliceCallback
= JS::SetGCSliceCallback(aCx
, GCSliceCallback
);
701 JS::AddGCNurseryCollectionCallback(aCx
, GCNurseryCollectionCallback
, this);
703 JS_SetObjectsTenuredCallback(aCx
, JSObjectsTenuredCb
, this);
704 JS::SetOutOfMemoryCallback(aCx
, OutOfMemoryCallback
, this);
705 JS::SetWaitCallback(mJSRuntime
, BeforeWaitCallback
, AfterWaitCallback
,
706 sizeof(dom::AutoYieldJSThreadExecution
));
707 JS::SetWarningReporter(aCx
, MozCrashWarningReporter
);
708 JS::SetShadowRealmInitializeGlobalCallback(aCx
, InitializeShadowRealm
);
709 JS::SetShadowRealmGlobalCreationCallback(aCx
, dom::NewShadowRealmGlobal
);
711 js::AutoEnterOOMUnsafeRegion::setAnnotateOOMAllocationSizeCallback(
712 CrashReporter::AnnotateOOMAllocationSize
);
714 static js::DOMCallbacks DOMcallbacks
= {InstanceClassHasProtoAtDepth
};
715 SetDOMCallbacks(aCx
, &DOMcallbacks
);
716 js::SetScriptEnvironmentPreparer(aCx
, &mEnvironmentPreparer
);
718 JS::dbg::SetDebuggerMallocSizeOf(aCx
, moz_malloc_size_of
);
720 #ifdef MOZ_JS_DEV_ERROR_INTERCEPTOR
721 JS_SetErrorInterceptorCallback(mJSRuntime
, &mErrorInterceptor
);
722 #endif // MOZ_JS_DEV_ERROR_INTERCEPTOR
724 JS_SetDestroyZoneCallback(aCx
, OnZoneDestroyed
);
727 #ifdef NS_BUILD_REFCNT_LOGGING
728 class JSLeakTracer
: public JS::CallbackTracer
{
730 explicit JSLeakTracer(JSRuntime
* aRuntime
)
731 : JS::CallbackTracer(aRuntime
, JS::TracerKind::Callback
,
732 JS::WeakMapTraceAction::TraceKeysAndValues
) {}
735 void onChild(JS::GCCellPtr thing
, const char* name
) override
{
736 const char* kindName
= JS::GCTraceKindToAscii(thing
.kind());
737 size_t size
= JS::GCTraceKindSize(thing
.kind());
738 MOZ_LOG_CTOR(thing
.asCell(), kindName
, size
);
743 void CycleCollectedJSRuntime::Shutdown(JSContext
* aCx
) {
744 #ifdef MOZ_JS_DEV_ERROR_INTERCEPTOR
745 mErrorInterceptor
.Shutdown(mJSRuntime
);
746 #endif // MOZ_JS_DEV_ERROR_INTERCEPTOR
748 // There should not be any roots left to trace at this point. Ensure any that
749 // remain are flagged as leaks.
750 #ifdef NS_BUILD_REFCNT_LOGGING
751 JSLeakTracer
tracer(Runtime());
752 TraceNativeBlackRoots(&tracer
);
753 TraceAllNativeGrayRoots(&tracer
);
757 mShutdownCalled
= true;
760 JS_SetDestroyZoneCallback(aCx
, nullptr);
762 JS::RemoveGCNurseryCollectionCallback(aCx
, GCNurseryCollectionCallback
, this);
765 CycleCollectedJSRuntime::~CycleCollectedJSRuntime() {
766 MOZ_COUNT_DTOR(CycleCollectedJSRuntime
);
767 MOZ_ASSERT(!mDeferredFinalizerTable
.Count());
768 MOZ_ASSERT(!mFinalizeRunnable
);
769 MOZ_ASSERT(mShutdownCalled
);
772 void CycleCollectedJSRuntime::SetContext(CycleCollectedJSContext
* aContext
) {
773 MOZ_ASSERT(!mContext
|| !aContext
, "Don't replace the context!");
777 size_t CycleCollectedJSRuntime::SizeOfExcludingThis(
778 MallocSizeOf aMallocSizeOf
) const {
779 return mJSHolders
.SizeOfExcludingThis(aMallocSizeOf
);
782 void CycleCollectedJSRuntime::UnmarkSkippableJSHolders() {
783 for (JSHolderMap::Iter
entry(mJSHolders
); !entry
.Done(); entry
.Next()) {
784 entry
->mTracer
->CanSkip(entry
->mHolder
, true);
788 void CycleCollectedJSRuntime::DescribeGCThing(
789 bool aIsMarked
, JS::GCCellPtr aThing
,
790 nsCycleCollectionTraversalCallback
& aCb
) const {
791 if (!aCb
.WantDebugInfo()) {
792 aCb
.DescribeGCedNode(aIsMarked
, "JS Object");
797 uint64_t compartmentAddress
= 0;
798 if (aThing
.is
<JSObject
>()) {
799 JSObject
* obj
= &aThing
.as
<JSObject
>();
800 compartmentAddress
= (uint64_t)JS::GetCompartment(obj
);
801 const JSClass
* clasp
= JS::GetClass(obj
);
803 // Give the subclass a chance to do something
804 if (DescribeCustomObjects(obj
, clasp
, name
)) {
805 // Nothing else to do!
806 } else if (js::IsFunctionObject(obj
)) {
807 JSFunction
* fun
= JS_GetObjectFunction(obj
);
808 JSString
* str
= JS_GetMaybePartialFunctionDisplayId(fun
);
810 JSLinearString
* linear
= JS_ASSERT_STRING_IS_LINEAR(str
);
812 AssignJSLinearString(chars
, linear
);
813 NS_ConvertUTF16toUTF8
fname(chars
);
814 SprintfLiteral(name
, "JS Object (Function - %s)", fname
.get());
816 SprintfLiteral(name
, "JS Object (Function)");
819 SprintfLiteral(name
, "JS Object (%s)", clasp
->name
);
822 SprintfLiteral(name
, "%s", JS::GCTraceKindToAscii(aThing
.kind()));
825 // Disable printing global for objects while we figure out ObjShrink fallout.
826 aCb
.DescribeGCedNode(aIsMarked
, name
, compartmentAddress
);
829 void CycleCollectedJSRuntime::NoteGCThingJSChildren(
830 JS::GCCellPtr aThing
, nsCycleCollectionTraversalCallback
& aCb
) const {
831 TraversalTracer
trc(mJSRuntime
, aCb
);
832 JS::TraceChildren(&trc
, aThing
);
835 void CycleCollectedJSRuntime::NoteGCThingXPCOMChildren(
836 const JSClass
* aClasp
, JSObject
* aObj
,
837 nsCycleCollectionTraversalCallback
& aCb
) const {
839 MOZ_ASSERT(aClasp
== JS::GetClass(aObj
));
841 JS::Rooted
<JSObject
*> obj(RootingCx(), aObj
);
843 if (NoteCustomGCThingXPCOMChildren(aClasp
, obj
, aCb
)) {
844 // Nothing else to do!
848 // XXX This test does seem fragile, we should probably allowlist classes
849 // that do hold a strong reference, but that might not be possible.
850 if (aClasp
->slot0IsISupports()) {
851 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb
, "JS::GetObjectISupports(obj)");
852 aCb
.NoteXPCOMChild(JS::GetObjectISupports
<nsISupports
>(obj
));
856 const DOMJSClass
* domClass
= GetDOMClass(aClasp
);
858 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb
, "UnwrapDOMObject(obj)");
859 // It's possible that our object is an unforgeable holder object, in
860 // which case it doesn't actually have a C++ DOM object associated with
861 // it. Use UnwrapPossiblyNotInitializedDOMObject, which produces null in
862 // that case, since NoteXPCOMChild/NoteNativeChild are null-safe.
863 if (domClass
->mDOMObjectIsISupports
) {
865 UnwrapPossiblyNotInitializedDOMObject
<nsISupports
>(obj
));
866 } else if (domClass
->mParticipant
) {
867 aCb
.NoteNativeChild(UnwrapPossiblyNotInitializedDOMObject
<void>(obj
),
868 domClass
->mParticipant
);
873 if (IsRemoteObjectProxy(obj
)) {
875 static_cast<const RemoteObjectProxyBase
*>(js::GetProxyHandler(obj
));
876 return handler
->NoteChildren(obj
, aCb
);
879 JS::Value value
= js::MaybeGetScriptPrivate(obj
);
880 if (!value
.isUndefined()) {
881 aCb
.NoteXPCOMChild(static_cast<nsISupports
*>(value
.toPrivate()));
885 void CycleCollectedJSRuntime::TraverseGCThing(
886 TraverseSelect aTs
, JS::GCCellPtr aThing
,
887 nsCycleCollectionTraversalCallback
& aCb
) {
888 bool isMarkedGray
= JS::GCThingIsMarkedGrayInCC(aThing
);
890 if (aTs
== TRAVERSE_FULL
) {
891 DescribeGCThing(!isMarkedGray
, aThing
, aCb
);
894 // If this object is alive, then all of its children are alive. For JS
895 // objects, the black-gray invariant ensures the children are also marked
896 // black. For C++ objects, the ref count from this object will keep them
897 // alive. Thus we don't need to trace our children, unless we are debugging
898 // using WantAllTraces.
899 if (!isMarkedGray
&& !aCb
.WantAllTraces()) {
903 if (aTs
== TRAVERSE_FULL
) {
904 NoteGCThingJSChildren(aThing
, aCb
);
907 if (aThing
.is
<JSObject
>()) {
908 JSObject
* obj
= &aThing
.as
<JSObject
>();
909 NoteGCThingXPCOMChildren(JS::GetClass(obj
), obj
, aCb
);
913 struct TraverseObjectShimClosure
{
914 nsCycleCollectionTraversalCallback
& cb
;
915 CycleCollectedJSRuntime
* self
;
918 void CycleCollectedJSRuntime::TraverseZone(
919 JS::Zone
* aZone
, nsCycleCollectionTraversalCallback
& aCb
) {
921 * We treat the zone as being gray. We handle non-gray GCthings in the
922 * zone by not reporting their children to the CC. The black-gray invariant
923 * ensures that any JS children will also be non-gray, and thus don't need to
924 * be added to the graph. For C++ children, not representing the edge from the
925 * non-gray JS GCthings to the C++ object will keep the child alive.
927 * We don't allow zone merging in a WantAllTraces CC, because then these
928 * assumptions don't hold.
930 aCb
.DescribeGCedNode(false, "JS Zone");
933 * Every JS child of everything in the zone is either in the zone
934 * or is a cross-compartment wrapper. In the former case, we don't need to
935 * represent these edges in the CC graph because JS objects are not ref
936 * counted. In the latter case, the JS engine keeps a map of these wrappers,
937 * which we iterate over. Edges between compartments in the same zone will add
938 * unnecessary loop edges to the graph (bug 842137).
940 TraversalTracer
trc(mJSRuntime
, aCb
);
941 js::TraceGrayWrapperTargets(&trc
, aZone
);
944 * To find C++ children of things in the zone, we scan every JS Object in
945 * the zone. Only JS Objects can have C++ children.
947 TraverseObjectShimClosure closure
= {aCb
, this};
948 js::IterateGrayObjects(aZone
, TraverseObjectShim
, &closure
);
952 void CycleCollectedJSRuntime::TraverseObjectShim(
953 void* aData
, JS::GCCellPtr aThing
, const JS::AutoRequireNoGC
& nogc
) {
954 TraverseObjectShimClosure
* closure
=
955 static_cast<TraverseObjectShimClosure
*>(aData
);
957 MOZ_ASSERT(aThing
.is
<JSObject
>());
958 closure
->self
->TraverseGCThing(CycleCollectedJSRuntime::TRAVERSE_CPP
, aThing
,
962 void CycleCollectedJSRuntime::TraverseNativeRoots(
963 nsCycleCollectionNoteRootCallback
& aCb
) {
964 // NB: This is here just to preserve the existing XPConnect order. I doubt it
965 // would hurt to do this after the JS holders.
966 TraverseAdditionalNativeRoots(aCb
);
968 for (JSHolderMap::Iter
entry(mJSHolders
); !entry
.Done(); entry
.Next()) {
969 void* holder
= entry
->mHolder
;
970 nsScriptObjectTracer
* tracer
= entry
->mTracer
;
972 bool noteRoot
= false;
973 if (MOZ_UNLIKELY(aCb
.WantAllTraces())) {
976 tracer
->Trace(holder
,
977 TraceCallbackFunc(CheckParticipatesInCycleCollection
),
982 aCb
.NoteNativeRoot(holder
, tracer
);
988 void CycleCollectedJSRuntime::TraceBlackJS(JSTracer
* aTracer
, void* aData
) {
989 CycleCollectedJSRuntime
* self
= static_cast<CycleCollectedJSRuntime
*>(aData
);
991 self
->TraceNativeBlackRoots(aTracer
);
995 bool CycleCollectedJSRuntime::TraceGrayJS(JSTracer
* aTracer
,
996 JS::SliceBudget
& budget
,
998 CycleCollectedJSRuntime
* self
= static_cast<CycleCollectedJSRuntime
*>(aData
);
1000 // Mark these roots as gray so the CC can walk them later.
1002 JSHolderMap::WhichHolders which
= JSHolderMap::AllHolders
;
1004 // Only trace holders in collecting zones when marking, except if we are
1005 // collecting the atoms zone since any holder may point into that zone.
1006 if (aTracer
->isMarkingTracer() &&
1007 !JS::AtomsZoneIsCollecting(self
->Runtime())) {
1008 which
= JSHolderMap::HoldersRequiredForGrayMarking
;
1011 return self
->TraceNativeGrayRoots(aTracer
, which
, budget
);
1015 void CycleCollectedJSRuntime::GCCallback(JSContext
* aContext
,
1017 JS::GCReason aReason
, void* aData
) {
1018 CycleCollectedJSRuntime
* self
= static_cast<CycleCollectedJSRuntime
*>(aData
);
1020 MOZ_ASSERT(CycleCollectedJSContext::Get()->Context() == aContext
);
1021 MOZ_ASSERT(CycleCollectedJSContext::Get()->Runtime() == self
);
1023 self
->OnGC(aContext
, aStatus
, aReason
);
1026 struct GCMajorMarker
: public BaseMarkerType
<GCMajorMarker
> {
1027 static constexpr const char* Name
= "GCMajor";
1028 static constexpr const char* Description
=
1029 "Summary data for an entire major GC, encompassing a set of "
1030 "incremental slices. The main thread is not blocked for the "
1031 "entire major GC interval, only for the individual slices.";
1033 using MS
= MarkerSchema
;
1034 static constexpr MS::PayloadField PayloadFields
[] = {
1035 {"timings", MS::InputType::CString
, "GC timings"}};
1037 static constexpr MS::Location Locations
[] = {MS::Location::MarkerChart
,
1038 MS::Location::MarkerTable
,
1039 MS::Location::TimelineMemory
};
1040 static constexpr MS::ETWMarkerGroup Group
= MS::ETWMarkerGroup::Memory
;
1042 static void StreamJSONMarkerData(
1043 mozilla::baseprofiler::SpliceableJSONWriter
& aWriter
,
1044 const mozilla::ProfilerString8View
& aTimingJSON
) {
1045 if (aTimingJSON
.Length() != 0) {
1046 aWriter
.SplicedJSONProperty("timings", aTimingJSON
);
1048 aWriter
.NullProperty("timings");
1054 void CycleCollectedJSRuntime::GCSliceCallback(JSContext
* aContext
,
1055 JS::GCProgress aProgress
,
1056 const JS::GCDescription
& aDesc
) {
1057 CycleCollectedJSRuntime
* self
= CycleCollectedJSRuntime::Get();
1058 MOZ_ASSERT(CycleCollectedJSContext::Get()->Context() == aContext
);
1060 if (profiler_thread_is_being_profiled_for_markers()) {
1061 if (aProgress
== JS::GC_CYCLE_END
) {
1062 profiler_add_marker("GCMajor", baseprofiler::category::GCCC
,
1063 MarkerTiming::Interval(aDesc
.startTime(aContext
),
1064 aDesc
.endTime(aContext
)),
1066 ProfilerString8View::WrapNullTerminatedString(
1067 aDesc
.formatJSONProfiler(aContext
).get()));
1068 } else if (aProgress
== JS::GC_SLICE_END
) {
1069 struct GCSliceMarker
{
1070 static constexpr mozilla::Span
<const char> MarkerTypeName() {
1071 return mozilla::MakeStringSpan("GCSlice");
1073 static void StreamJSONMarkerData(
1074 mozilla::baseprofiler::SpliceableJSONWriter
& aWriter
,
1075 const mozilla::ProfilerString8View
& aTimingJSON
) {
1076 if (aTimingJSON
.Length() != 0) {
1077 aWriter
.SplicedJSONProperty("timings", aTimingJSON
);
1079 aWriter
.NullProperty("timings");
1082 static mozilla::MarkerSchema
MarkerTypeDisplay() {
1083 using MS
= mozilla::MarkerSchema
;
1084 MS schema
{MS::Location::MarkerChart
, MS::Location::MarkerTable
,
1085 MS::Location::TimelineMemory
};
1086 schema
.AddStaticLabelValue(
1088 "One slice of an incremental garbage collection (GC). The main "
1089 "thread is blocked during this time.");
1090 // No display instructions here, there is special handling in the
1096 profiler_add_marker("GCSlice", baseprofiler::category::GCCC
,
1097 MarkerTiming::Interval(aDesc
.lastSliceStart(aContext
),
1098 aDesc
.lastSliceEnd(aContext
)),
1100 ProfilerString8View::WrapNullTerminatedString(
1101 aDesc
.sliceToJSONProfiler(aContext
).get()));
1105 if (aProgress
== JS::GC_CYCLE_END
&&
1106 JS::dbg::FireOnGarbageCollectionHookRequired(aContext
)) {
1107 JS::GCReason reason
= aDesc
.reason_
;
1108 Unused
<< NS_WARN_IF(
1109 NS_FAILED(DebuggerOnGCRunnable::Enqueue(aContext
, aDesc
)) &&
1110 reason
!= JS::GCReason::SHUTDOWN_CC
&&
1111 reason
!= JS::GCReason::DESTROY_RUNTIME
&&
1112 reason
!= JS::GCReason::XPCONNECT_SHUTDOWN
);
1115 if (self
->mPrevGCSliceCallback
) {
1116 self
->mPrevGCSliceCallback(aContext
, aProgress
, aDesc
);
1121 void CycleCollectedJSRuntime::GCNurseryCollectionCallback(
1122 JSContext
* aContext
, JS::GCNurseryProgress aProgress
, JS::GCReason aReason
,
1124 CycleCollectedJSRuntime
* self
= static_cast<CycleCollectedJSRuntime
*>(data
);
1125 MOZ_ASSERT(self
->GetContext()->Context() == aContext
);
1127 TimeStamp now
= TimeStamp::Now();
1128 if (aProgress
== JS::GCNurseryProgress::GC_NURSERY_COLLECTION_START
) {
1129 self
->mLatestNurseryCollectionStart
= now
;
1130 } else if (aProgress
== JS::GCNurseryProgress::GC_NURSERY_COLLECTION_END
) {
1131 PerfStats::RecordMeasurement(PerfStats::Metric::MinorGC
,
1132 now
- self
->mLatestNurseryCollectionStart
);
1135 if (aProgress
== JS::GCNurseryProgress::GC_NURSERY_COLLECTION_END
&&
1136 profiler_thread_is_being_profiled_for_markers()) {
1137 struct GCMinorMarker
{
1138 static constexpr mozilla::Span
<const char> MarkerTypeName() {
1139 return mozilla::MakeStringSpan("GCMinor");
1141 static void StreamJSONMarkerData(
1142 mozilla::baseprofiler::SpliceableJSONWriter
& aWriter
,
1143 const mozilla::ProfilerString8View
& aTimingJSON
) {
1144 if (aTimingJSON
.Length() != 0) {
1145 aWriter
.SplicedJSONProperty("nursery", aTimingJSON
);
1147 aWriter
.NullProperty("nursery");
1150 static mozilla::MarkerSchema
MarkerTypeDisplay() {
1151 using MS
= mozilla::MarkerSchema
;
1152 MS schema
{MS::Location::MarkerChart
, MS::Location::MarkerTable
,
1153 MS::Location::TimelineMemory
};
1154 schema
.AddStaticLabelValue(
1156 "A minor GC (aka nursery collection) to clear out the buffer used "
1157 "for recent allocations and move surviving data to the tenured "
1158 "(long-lived) heap.");
1159 // No display instructions here, there is special handling in the
1165 profiler_add_marker(
1166 "GCMinor", baseprofiler::category::GCCC
,
1167 MarkerTiming::Interval(self
->mLatestNurseryCollectionStart
, now
),
1169 ProfilerString8View::WrapNullTerminatedString(
1170 JS::MinorGcToJSON(aContext
).get()));
1175 void CycleCollectedJSRuntime::OutOfMemoryCallback(JSContext
* aContext
,
1177 CycleCollectedJSRuntime
* self
= static_cast<CycleCollectedJSRuntime
*>(aData
);
1179 MOZ_ASSERT(CycleCollectedJSContext::Get()->Context() == aContext
);
1180 MOZ_ASSERT(CycleCollectedJSContext::Get()->Runtime() == self
);
1182 self
->OnOutOfMemory();
1186 void* CycleCollectedJSRuntime::BeforeWaitCallback(uint8_t* aMemory
) {
1187 MOZ_ASSERT(aMemory
);
1189 // aMemory is stack allocated memory to contain our RAII object. This allows
1190 // for us to avoid allocations on the heap during this callback.
1191 return new (aMemory
) dom::AutoYieldJSThreadExecution
;
1195 void CycleCollectedJSRuntime::AfterWaitCallback(void* aCookie
) {
1196 MOZ_ASSERT(aCookie
);
1197 static_cast<dom::AutoYieldJSThreadExecution
*>(aCookie
)
1198 ->~AutoYieldJSThreadExecution();
1201 struct JsGcTracer
: public TraceCallbacks
{
1202 virtual void Trace(JS::Heap
<JS::Value
>* aPtr
, const char* aName
,
1203 void* aClosure
) const override
{
1204 JS::TraceEdge(static_cast<JSTracer
*>(aClosure
), aPtr
, aName
);
1206 virtual void Trace(JS::Heap
<jsid
>* aPtr
, const char* aName
,
1207 void* aClosure
) const override
{
1208 JS::TraceEdge(static_cast<JSTracer
*>(aClosure
), aPtr
, aName
);
1210 virtual void Trace(JS::Heap
<JSObject
*>* aPtr
, const char* aName
,
1211 void* aClosure
) const override
{
1212 JS::TraceEdge(static_cast<JSTracer
*>(aClosure
), aPtr
, aName
);
1214 virtual void Trace(nsWrapperCache
* aPtr
, const char* aName
,
1215 void* aClosure
) const override
{
1216 aPtr
->TraceWrapper(static_cast<JSTracer
*>(aClosure
), aName
);
1218 virtual void Trace(JS::TenuredHeap
<JSObject
*>* aPtr
, const char* aName
,
1219 void* aClosure
) const override
{
1220 JS::TraceEdge(static_cast<JSTracer
*>(aClosure
), aPtr
, aName
);
1222 virtual void Trace(JS::Heap
<JSString
*>* aPtr
, const char* aName
,
1223 void* aClosure
) const override
{
1224 JS::TraceEdge(static_cast<JSTracer
*>(aClosure
), aPtr
, aName
);
1226 virtual void Trace(JS::Heap
<JSScript
*>* aPtr
, const char* aName
,
1227 void* aClosure
) const override
{
1228 JS::TraceEdge(static_cast<JSTracer
*>(aClosure
), aPtr
, aName
);
1230 virtual void Trace(JS::Heap
<JSFunction
*>* aPtr
, const char* aName
,
1231 void* aClosure
) const override
{
1232 JS::TraceEdge(static_cast<JSTracer
*>(aClosure
), aPtr
, aName
);
1236 void mozilla::TraceScriptHolder(nsISupports
* aHolder
, JSTracer
* aTracer
) {
1237 nsXPCOMCycleCollectionParticipant
* participant
= nullptr;
1238 CallQueryInterface(aHolder
, &participant
);
1239 participant
->Trace(aHolder
, JsGcTracer(), aTracer
);
1242 #if defined(NIGHTLY_BUILD) || defined(MOZ_DEV_EDITION) || defined(DEBUG)
1243 # define CHECK_SINGLE_ZONE_JS_HOLDERS
1246 #ifdef CHECK_SINGLE_ZONE_JS_HOLDERS
1248 // A tracer that checks that a JS holder only holds JS GC things in a single
1250 struct CheckZoneTracer
: public TraceCallbacks
{
1251 const char* mClassName
;
1252 mutable JS::Zone
* mZone
;
1254 explicit CheckZoneTracer(const char* aClassName
, JS::Zone
* aZone
= nullptr)
1255 : mClassName(aClassName
), mZone(aZone
) {}
1257 void checkZone(JS::Zone
* aZone
, const char* aName
) const {
1258 if (JS::IsAtomsZone(aZone
)) {
1259 // Any holder may contain pointers into the atoms zone.
1268 if (aZone
== mZone
) {
1272 // Most JS holders only contain pointers to GC things in a single zone. We
1273 // group holders by referent zone where possible, allowing us to improve GC
1274 // performance by only tracing holders for zones that are being collected.
1276 // Additionally, pointers from any holder into the atoms zone are allowed
1277 // since all holders are traced when we collect the atoms zone.
1279 // If you added a holder that has pointers into multiple zones do not
1280 // use NS_IMPL_CYCLE_COLLECTION_SINGLE_ZONE_SCRIPT_HOLDER_CLASS.
1281 MOZ_CRASH_UNSAFE_PRINTF(
1282 "JS holder %s contains pointers to GC things in more than one zone ("
1287 virtual void Trace(JS::Heap
<JS::Value
>* aPtr
, const char* aName
,
1288 void* aClosure
) const override
{
1289 JS::Value value
= aPtr
->unbarrieredGet();
1290 if (value
.isGCThing()) {
1291 checkZone(JS::GetGCThingZone(value
.toGCCellPtr()), aName
);
1294 virtual void Trace(JS::Heap
<jsid
>* aPtr
, const char* aName
,
1295 void* aClosure
) const override
{
1296 jsid id
= aPtr
->unbarrieredGet();
1297 if (id
.isGCThing()) {
1298 MOZ_ASSERT(JS::IsAtomsZone(JS::GetTenuredGCThingZone(id
.toGCCellPtr())));
1301 virtual void Trace(JS::Heap
<JSObject
*>* aPtr
, const char* aName
,
1302 void* aClosure
) const override
{
1303 JSObject
* obj
= aPtr
->unbarrieredGet();
1305 checkZone(js::GetObjectZoneFromAnyThread(obj
), aName
);
1308 virtual void Trace(nsWrapperCache
* aPtr
, const char* aName
,
1309 void* aClosure
) const override
{
1310 JSObject
* obj
= aPtr
->GetWrapperPreserveColor();
1312 checkZone(js::GetObjectZoneFromAnyThread(obj
), aName
);
1315 virtual void Trace(JS::TenuredHeap
<JSObject
*>* aPtr
, const char* aName
,
1316 void* aClosure
) const override
{
1317 JSObject
* obj
= aPtr
->unbarrieredGetPtr();
1319 checkZone(js::GetObjectZoneFromAnyThread(obj
), aName
);
1322 virtual void Trace(JS::Heap
<JSString
*>* aPtr
, const char* aName
,
1323 void* aClosure
) const override
{
1324 JSString
* str
= aPtr
->unbarrieredGet();
1326 checkZone(JS::GetStringZone(str
), aName
);
1329 virtual void Trace(JS::Heap
<JSScript
*>* aPtr
, const char* aName
,
1330 void* aClosure
) const override
{
1331 JSScript
* script
= aPtr
->unbarrieredGet();
1333 checkZone(JS::GetTenuredGCThingZone(JS::GCCellPtr(script
)), aName
);
1336 virtual void Trace(JS::Heap
<JSFunction
*>* aPtr
, const char* aName
,
1337 void* aClosure
) const override
{
1338 JSFunction
* fun
= aPtr
->unbarrieredGet();
1340 checkZone(js::GetObjectZoneFromAnyThread(JS_GetFunctionObject(fun
)),
1346 static inline void CheckHolderIsSingleZone(
1347 void* aHolder
, nsCycleCollectionParticipant
* aParticipant
,
1349 CheckZoneTracer
tracer(aParticipant
->ClassName(), aZone
);
1350 aParticipant
->Trace(aHolder
, tracer
, nullptr);
1355 static inline bool ShouldCheckSingleZoneHolders() {
1358 #elif defined(NIGHTLY_BUILD) || defined(MOZ_DEV_EDITION)
1359 // Don't check every time to avoid performance impact.
1360 return rand() % 256 == 0;
1366 #ifdef NS_BUILD_REFCNT_LOGGING
1367 void CycleCollectedJSRuntime::TraceAllNativeGrayRoots(JSTracer
* aTracer
) {
1368 MOZ_RELEASE_ASSERT(mHolderIter
.isNothing());
1369 JS::SliceBudget budget
= JS::SliceBudget::unlimited();
1371 TraceNativeGrayRoots(aTracer
, JSHolderMap::AllHolders
, budget
));
1375 bool CycleCollectedJSRuntime::TraceNativeGrayRoots(
1376 JSTracer
* aTracer
, JSHolderMap::WhichHolders aWhich
,
1377 JS::SliceBudget
& aBudget
) {
1379 // NB: This is here just to preserve the existing XPConnect order. I doubt
1380 // it would hurt to do this after the JS holders.
1381 TraceAdditionalNativeGrayRoots(aTracer
);
1383 mHolderIter
.emplace(mJSHolders
, aWhich
);
1384 aBudget
.forceCheck();
1386 // Holders may have been removed between slices, so we may need to update
1388 mHolderIter
->UpdateForRemovals();
1391 bool finished
= TraceJSHolders(aTracer
, *mHolderIter
, aBudget
);
1393 mHolderIter
.reset();
1399 class GetHolderAddressFunctor
: public JS::TracingContext::Functor
{
1401 GetHolderAddressFunctor() = default;
1403 virtual void operator()(JS::TracingContext
* aTrc
, const char* aName
,
1404 char* aBuf
, size_t aBufSize
) override
{
1405 SprintfBuf(aBuf
, aBufSize
, "%s, holder 0x%p", aName
, mHolder
);
1408 void SetHolder(void* aHolder
) { mHolder
= aHolder
; }
1411 void* mHolder
= nullptr;
1414 bool CycleCollectedJSRuntime::TraceJSHolders(JSTracer
* aTracer
,
1415 JSHolderMap::Iter
& aIter
,
1416 JS::SliceBudget
& aBudget
) {
1417 bool checkSingleZoneHolders
= ShouldCheckSingleZoneHolders();
1418 GetHolderAddressFunctor functor
;
1419 JS::AutoTracingDetails
tracingDetails(aTracer
, functor
);
1421 while (!aIter
.Done() && !aBudget
.isOverBudget()) {
1422 void* holder
= aIter
->mHolder
;
1423 nsScriptObjectTracer
* tracer
= aIter
->mTracer
;
1425 #ifdef CHECK_SINGLE_ZONE_JS_HOLDERS
1426 if (checkSingleZoneHolders
&& tracer
->IsSingleZoneJSHolder()) {
1427 CheckHolderIsSingleZone(holder
, tracer
, aIter
.Zone());
1430 Unused
<< checkSingleZoneHolders
;
1433 functor
.SetHolder(holder
);
1434 tracer
->Trace(holder
, JsGcTracer(), aTracer
);
1435 functor
.SetHolder(nullptr);
1441 return aIter
.Done();
1444 void CycleCollectedJSRuntime::AddJSHolder(void* aHolder
,
1445 nsScriptObjectTracer
* aTracer
,
1447 mJSHolders
.Put(aHolder
, aTracer
, aZone
);
1450 struct ClearJSHolder
: public TraceCallbacks
{
1451 virtual void Trace(JS::Heap
<JS::Value
>* aPtr
, const char*,
1452 void*) const override
{
1453 aPtr
->setUndefined();
1456 virtual void Trace(JS::Heap
<jsid
>* aPtr
, const char*, void*) const override
{
1457 *aPtr
= JS::PropertyKey::Void();
1460 virtual void Trace(JS::Heap
<JSObject
*>* aPtr
, const char*,
1461 void*) const override
{
1465 virtual void Trace(nsWrapperCache
* aPtr
, const char* aName
,
1466 void* aClosure
) const override
{
1467 aPtr
->ClearWrapper();
1470 virtual void Trace(JS::TenuredHeap
<JSObject
*>* aPtr
, const char*,
1471 void*) const override
{
1475 virtual void Trace(JS::Heap
<JSString
*>* aPtr
, const char*,
1476 void*) const override
{
1480 virtual void Trace(JS::Heap
<JSScript
*>* aPtr
, const char*,
1481 void*) const override
{
1485 virtual void Trace(JS::Heap
<JSFunction
*>* aPtr
, const char*,
1486 void*) const override
{
1491 void CycleCollectedJSRuntime::RemoveJSHolder(void* aHolder
) {
1492 nsScriptObjectTracer
* tracer
= mJSHolders
.Extract(aHolder
);
1494 // Bug 1531951: The analysis can't see through the virtual call but we know
1495 // that the ClearJSHolder tracer will never GC.
1496 JS::AutoSuppressGCAnalysis nogc
;
1497 tracer
->Trace(aHolder
, ClearJSHolder(), nullptr);
1502 static void AssertNoGcThing(JS::GCCellPtr aGCThing
, const char* aName
,
1504 MOZ_ASSERT(!aGCThing
);
1507 void CycleCollectedJSRuntime::AssertNoObjectsToTrace(void* aPossibleJSHolder
) {
1508 nsScriptObjectTracer
* tracer
= mJSHolders
.Get(aPossibleJSHolder
);
1510 tracer
->Trace(aPossibleJSHolder
, TraceCallbackFunc(AssertNoGcThing
),
1516 nsCycleCollectionParticipant
* CycleCollectedJSRuntime::GCThingParticipant() {
1517 return &mGCThingCycleCollectorGlobal
;
1520 nsCycleCollectionParticipant
* CycleCollectedJSRuntime::ZoneParticipant() {
1521 return &mJSZoneCycleCollectorGlobal
;
1524 nsresult
CycleCollectedJSRuntime::TraverseRoots(
1525 nsCycleCollectionNoteRootCallback
& aCb
) {
1526 TraverseNativeRoots(aCb
);
1528 NoteWeakMapsTracer
trc(mJSRuntime
, aCb
);
1529 js::TraceWeakMaps(&trc
);
1534 bool CycleCollectedJSRuntime::UsefulToMergeZones() const { return false; }
1536 void CycleCollectedJSRuntime::FixWeakMappingGrayBits() const {
1537 MOZ_ASSERT(!JS::IsIncrementalGCInProgress(mJSRuntime
),
1538 "Don't call FixWeakMappingGrayBits during a GC.");
1539 FixWeakMappingGrayBitsTracer
fixer(mJSRuntime
);
1543 void CycleCollectedJSRuntime::CheckGrayBits() const {
1544 MOZ_ASSERT(!JS::IsIncrementalGCInProgress(mJSRuntime
),
1545 "Don't call CheckGrayBits during a GC.");
1548 // Bug 1346874 - The gray state check is expensive. Android tests are already
1549 // slow enough that this check can easily push them over the threshold to a
1552 MOZ_ASSERT(js::CheckGrayMarkingState(mJSRuntime
));
1553 MOZ_ASSERT(CheckWeakMappingGrayBitsTracer::Check(mJSRuntime
));
1557 bool CycleCollectedJSRuntime::AreGCGrayBitsValid() const {
1558 return js::AreGCGrayBitsValid(mJSRuntime
);
1561 void CycleCollectedJSRuntime::GarbageCollect(JS::GCOptions aOptions
,
1562 JS::GCReason aReason
) const {
1563 JSContext
* cx
= CycleCollectedJSContext::Get()->Context();
1564 JS::PrepareForFullGC(cx
);
1565 JS::NonIncrementalGC(cx
, aOptions
, aReason
);
1568 void CycleCollectedJSRuntime::JSObjectsTenured(JS::GCContext
* aGCContext
) {
1569 NurseryObjectsVector objects
;
1570 std::swap(objects
, mNurseryObjects
);
1572 for (auto iter
= objects
.Iter(); !iter
.Done(); iter
.Next()) {
1573 nsWrapperCache
* cache
= iter
.Get();
1574 JSObject
* wrapper
= cache
->GetWrapperMaybeDead();
1575 MOZ_DIAGNOSTIC_ASSERT(wrapper
);
1577 if (js::gc::InCollectedNurseryRegion(wrapper
)) {
1578 MOZ_ASSERT(!cache
->PreservingWrapper());
1579 const JSClass
* jsClass
= JS::GetClass(wrapper
);
1580 jsClass
->doFinalize(aGCContext
, wrapper
);
1584 if (js::gc::IsInsideNursery(wrapper
)) {
1585 mNurseryObjects
.InfallibleAppend(cache
);
1590 void CycleCollectedJSRuntime::NurseryWrapperAdded(nsWrapperCache
* aCache
) {
1592 MOZ_ASSERT(aCache
->GetWrapperMaybeDead());
1593 MOZ_ASSERT(!JS::ObjectIsTenured(aCache
->GetWrapperMaybeDead()));
1594 mNurseryObjects
.InfallibleAppend(aCache
);
1597 void CycleCollectedJSRuntime::DeferredFinalize(
1598 DeferredFinalizeAppendFunction aAppendFunc
, DeferredFinalizeFunction aFunc
,
1600 // Tell the analysis that the function pointers will not GC.
1601 JS::AutoSuppressGCAnalysis suppress
;
1602 mDeferredFinalizerTable
.WithEntryHandle(aFunc
, [&](auto&& entry
) {
1604 aAppendFunc(entry
.Data(), aThing
);
1606 entry
.Insert(aAppendFunc(nullptr, aThing
));
1611 void CycleCollectedJSRuntime::DeferredFinalize(nsISupports
* aSupports
) {
1612 typedef DeferredFinalizerImpl
<nsISupports
> Impl
;
1613 DeferredFinalize(Impl::AppendDeferredFinalizePointer
, Impl::DeferredFinalize
,
1617 void CycleCollectedJSRuntime::DumpJSHeap(FILE* aFile
) {
1618 JSContext
* cx
= CycleCollectedJSContext::Get()->Context();
1620 mozilla::MallocSizeOf mallocSizeOf
=
1621 PR_GetEnv("MOZ_GC_LOG_SIZE") ? moz_malloc_size_of
: nullptr;
1622 js::DumpHeap(cx
, aFile
, js::CollectNurseryBeforeDump
, mallocSizeOf
);
1625 IncrementalFinalizeRunnable::IncrementalFinalizeRunnable(
1626 CycleCollectedJSRuntime
* aRt
, DeferredFinalizerTable
& aFinalizers
)
1627 : DiscardableRunnable("IncrementalFinalizeRunnable"),
1629 mFinalizeFunctionToRun(0),
1631 for (auto iter
= aFinalizers
.Iter(); !iter
.Done(); iter
.Next()) {
1632 DeferredFinalizeFunction
& function
= iter
.Key();
1633 void*& data
= iter
.Data();
1635 DeferredFinalizeFunctionHolder
* holder
=
1636 mDeferredFinalizeFunctions
.AppendElement();
1637 holder
->run
= function
;
1638 holder
->data
= data
;
1642 MOZ_ASSERT(mDeferredFinalizeFunctions
.Length());
1645 IncrementalFinalizeRunnable::~IncrementalFinalizeRunnable() {
1646 MOZ_ASSERT(!mDeferredFinalizeFunctions
.Length());
1647 MOZ_ASSERT(!mRuntime
);
1650 void IncrementalFinalizeRunnable::ReleaseNow(bool aLimited
) {
1652 NS_WARNING("Re-entering ReleaseNow");
1656 AUTO_PROFILER_LABEL("IncrementalFinalizeRunnable::ReleaseNow",
1659 mozilla::AutoRestore
<bool> ar(mReleasing
);
1661 MOZ_ASSERT(mDeferredFinalizeFunctions
.Length() != 0,
1662 "We should have at least ReleaseSliceNow to run");
1663 MOZ_ASSERT(mFinalizeFunctionToRun
< mDeferredFinalizeFunctions
.Length(),
1664 "No more finalizers to run?");
1666 TimeDuration sliceTime
= TimeDuration::FromMilliseconds(SliceMillis
);
1667 TimeStamp started
= aLimited
? TimeStamp::Now() : TimeStamp();
1668 bool timeout
= false;
1670 const DeferredFinalizeFunctionHolder
& function
=
1671 mDeferredFinalizeFunctions
[mFinalizeFunctionToRun
];
1674 while (!timeout
&& !done
) {
1676 * We don't want to read the clock too often, so we try to
1677 * release slices of 100 items.
1679 done
= function
.run(100, function
.data
);
1680 timeout
= TimeStamp::Now() - started
>= sliceTime
;
1683 ++mFinalizeFunctionToRun
;
1689 while (!function
.run(UINT32_MAX
, function
.data
));
1690 ++mFinalizeFunctionToRun
;
1692 } while (mFinalizeFunctionToRun
< mDeferredFinalizeFunctions
.Length());
1695 if (mFinalizeFunctionToRun
== mDeferredFinalizeFunctions
.Length()) {
1696 MOZ_ASSERT(mRuntime
->mFinalizeRunnable
== this);
1697 mDeferredFinalizeFunctions
.Clear();
1698 CycleCollectedJSRuntime
* runtime
= mRuntime
;
1700 // NB: This may delete this!
1701 runtime
->mFinalizeRunnable
= nullptr;
1706 IncrementalFinalizeRunnable::Run() {
1707 if (!mDeferredFinalizeFunctions
.Length()) {
1708 /* These items were already processed synchronously in JSGC_END. */
1709 MOZ_ASSERT(!mRuntime
);
1713 MOZ_ASSERT(mRuntime
->mFinalizeRunnable
== this);
1714 TimeStamp start
= TimeStamp::Now();
1717 if (mDeferredFinalizeFunctions
.Length()) {
1718 nsresult rv
= NS_DispatchToCurrentThread(this);
1719 if (NS_FAILED(rv
)) {
1723 MOZ_ASSERT(!mRuntime
);
1726 uint32_t duration
= (uint32_t)((TimeStamp::Now() - start
).ToMilliseconds());
1727 Telemetry::Accumulate(Telemetry::DEFERRED_FINALIZE_ASYNC
, duration
);
1732 void CycleCollectedJSRuntime::FinalizeDeferredThings(
1733 DeferredFinalizeType aType
) {
1734 // If mFinalizeRunnable isn't null, we didn't finalize everything from the
1736 if (mFinalizeRunnable
) {
1737 if (aType
== FinalizeLater
) {
1738 // We need to defer all finalization until we return to the event loop,
1739 // so leave things alone. Any new objects to be finalized from the current
1740 // GC will be handled by the existing mFinalizeRunnable.
1743 MOZ_ASSERT(aType
== FinalizeIncrementally
|| aType
== FinalizeNow
);
1744 // If we're finalizing incrementally, we don't want finalizers to build up,
1745 // so try to finish them off now.
1746 // If we're finalizing synchronously, also go ahead and clear them out,
1747 // so we make sure as much as possible is freed.
1748 mFinalizeRunnable
->ReleaseNow(false);
1749 if (mFinalizeRunnable
) {
1750 // If we re-entered ReleaseNow, we couldn't delete mFinalizeRunnable and
1751 // we need to just continue processing it.
1756 // If there's nothing to finalize, don't create a new runnable.
1757 if (mDeferredFinalizerTable
.Count() == 0) {
1762 new IncrementalFinalizeRunnable(this, mDeferredFinalizerTable
);
1764 // Everything should be gone now.
1765 MOZ_ASSERT(mDeferredFinalizerTable
.Count() == 0);
1767 if (aType
== FinalizeNow
) {
1768 mFinalizeRunnable
->ReleaseNow(false);
1769 MOZ_ASSERT(!mFinalizeRunnable
);
1771 MOZ_ASSERT(aType
== FinalizeIncrementally
|| aType
== FinalizeLater
);
1772 NS_DispatchToCurrentThreadQueue(do_AddRef(mFinalizeRunnable
), 2500,
1773 EventQueuePriority::Idle
);
1777 const char* CycleCollectedJSRuntime::OOMStateToString(
1778 const OOMState aOomState
) const {
1779 switch (aOomState
) {
1782 case OOMState::Reporting
:
1784 case OOMState::Reported
:
1786 case OOMState::Recovered
:
1789 MOZ_ASSERT_UNREACHABLE("OOMState holds an invalid value");
1794 bool CycleCollectedJSRuntime::OOMReported() {
1795 return mOutOfMemoryState
== OOMState::Reported
;
1798 void CycleCollectedJSRuntime::AnnotateAndSetOutOfMemory(OOMState
* aStatePtr
,
1799 OOMState aNewState
) {
1800 *aStatePtr
= aNewState
;
1801 CrashReporter::Annotation annotation
=
1802 (aStatePtr
== &mOutOfMemoryState
)
1803 ? CrashReporter::Annotation::JSOutOfMemory
1804 : CrashReporter::Annotation::JSLargeAllocationFailure
;
1806 CrashReporter::RecordAnnotationCString(annotation
,
1807 OOMStateToString(aNewState
));
1810 void CycleCollectedJSRuntime::OnGC(JSContext
* aContext
, JSGCStatus aStatus
,
1811 JS::GCReason aReason
) {
1814 MOZ_RELEASE_ASSERT(mHolderIter
.isNothing());
1815 nsCycleCollector_prepareForGarbageCollection();
1816 PrepareWaitingZonesForGC();
1819 MOZ_RELEASE_ASSERT(mHolderIter
.isNothing());
1820 if (mOutOfMemoryState
== OOMState::Reported
) {
1821 AnnotateAndSetOutOfMemory(&mOutOfMemoryState
, OOMState::Recovered
);
1823 if (mLargeAllocationFailureState
== OOMState::Reported
) {
1824 AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState
,
1825 OOMState::Recovered
);
1828 DeferredFinalizeType finalizeType
;
1829 if (JS_IsExceptionPending(aContext
)) {
1830 // There is a pending exception. The finalizers are not set up to run
1831 // in that state, so don't run the finalizer until we've returned to the
1833 finalizeType
= FinalizeLater
;
1834 } else if (JS::InternalGCReason(aReason
)) {
1835 if (aReason
== JS::GCReason::DESTROY_RUNTIME
) {
1836 // We're shutting down, so we need to destroy things immediately.
1837 finalizeType
= FinalizeNow
;
1839 // We may be in the middle of running some code that the JIT has
1840 // assumed can't have certain kinds of side effects. Finalizers can do
1841 // all sorts of things, such as run JS, so we want to run them later,
1842 // after we've returned to the event loop.
1843 finalizeType
= FinalizeLater
;
1845 } else if (JS::WasIncrementalGC(mJSRuntime
)) {
1846 // The GC was incremental, so we probably care about pauses. Try to
1847 // break up finalization, but it is okay if we do some now.
1848 finalizeType
= FinalizeIncrementally
;
1850 // If we're running a synchronous GC, we probably want to free things as
1851 // quickly as possible. This can happen during testing or if memory is
1853 finalizeType
= FinalizeNow
;
1855 FinalizeDeferredThings(finalizeType
);
1863 CustomGCCallback(aStatus
);
1866 void CycleCollectedJSRuntime::OnOutOfMemory() {
1867 AnnotateAndSetOutOfMemory(&mOutOfMemoryState
, OOMState::Reporting
);
1868 CustomOutOfMemoryCallback();
1869 AnnotateAndSetOutOfMemory(&mOutOfMemoryState
, OOMState::Reported
);
1872 void CycleCollectedJSRuntime::SetLargeAllocationFailure(OOMState aNewState
) {
1873 AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState
, aNewState
);
1876 void CycleCollectedJSRuntime::PrepareWaitingZonesForGC() {
1877 JSContext
* cx
= CycleCollectedJSContext::Get()->Context();
1878 if (mZonesWaitingForGC
.Count() == 0) {
1879 JS::PrepareForFullGC(cx
);
1881 for (const auto& key
: mZonesWaitingForGC
) {
1882 JS::PrepareZoneForGC(cx
, key
);
1884 mZonesWaitingForGC
.Clear();
1889 void CycleCollectedJSRuntime::OnZoneDestroyed(JS::GCContext
* aGcx
,
1891 // Remove the zone from the set of zones waiting for GC, if present. This can
1892 // happen if a zone is added to the set during an incremental GC in which it
1893 // is later destroyed.
1894 CycleCollectedJSRuntime
* runtime
= Get();
1895 runtime
->mZonesWaitingForGC
.Remove(aZone
);
1898 void CycleCollectedJSRuntime::EnvironmentPreparer::invoke(
1899 JS::HandleObject global
, js::ScriptEnvironmentPreparer::Closure
& closure
) {
1900 MOZ_ASSERT(JS_IsGlobalObject(global
));
1901 nsIGlobalObject
* nativeGlobal
= xpc::NativeGlobal(global
);
1903 // Not much we can do if we simply don't have a usable global here...
1904 NS_ENSURE_TRUE_VOID(nativeGlobal
&& nativeGlobal
->HasJSGlobal());
1906 AutoEntryScript
aes(nativeGlobal
, "JS-engine-initiated execution");
1908 MOZ_ASSERT(!JS_IsExceptionPending(aes
.cx()));
1910 DebugOnly
<bool> ok
= closure(aes
.cx());
1912 MOZ_ASSERT_IF(ok
, !JS_IsExceptionPending(aes
.cx()));
1914 // The AutoEntryScript will check for JS_IsExceptionPending on the
1915 // JSContext and report it as needed as it comes off the stack.
1919 CycleCollectedJSRuntime
* CycleCollectedJSRuntime::Get() {
1920 auto context
= CycleCollectedJSContext::Get();
1922 return context
->Runtime();
1927 #ifdef MOZ_JS_DEV_ERROR_INTERCEPTOR
1930 extern void DumpValue(const JS::Value
& val
);
1933 void CycleCollectedJSRuntime::ErrorInterceptor::Shutdown(JSRuntime
* rt
) {
1934 JS_SetErrorInterceptorCallback(rt
, nullptr);
1935 mThrownError
.reset();
1939 void CycleCollectedJSRuntime::ErrorInterceptor::interceptError(
1940 JSContext
* cx
, JS::HandleValue exn
) {
1942 // We already have an error, we don't need anything more.
1946 if (!nsContentUtils::ThreadsafeIsSystemCaller(cx
)) {
1947 // We are only interested in chrome code.
1951 const auto type
= JS_GetErrorType(exn
);
1953 // This is not one of the primitive error types.
1958 case JSExnType::JSEXN_REFERENCEERR
:
1959 case JSExnType::JSEXN_SYNTAXERR
:
1962 // Not one of the errors we are interested in.
1963 // Note that we are not interested in instances of `TypeError`
1964 // for the time being, as DOM (ab)uses this constructor to represent
1965 // all sorts of errors that are not even remotely related to type
1966 // errors (e.g. some network errors).
1967 // If we ever have a mechanism to differentiate between DOM-thrown
1968 // and SpiderMonkey-thrown instances of `TypeError`, we should
1969 // consider watching for `TypeError` here.
1973 // Now copy the details of the exception locally.
1974 // While copying the details of an exception could be expensive, in most runs,
1975 // this will be done at most once during the execution of the process, so the
1976 // total cost should be reasonable.
1978 ErrorDetails details
;
1979 details
.mType
= *type
;
1980 // If `exn` isn't an exception object, `ExtractErrorValues` could end up
1981 // calling `toString()`, which could in turn end up throwing an error. While
1982 // this should work, we want to avoid that complex use case. Fortunately, we
1983 // have already checked above that `exn` is an exception object, so nothing
1984 // such should happen.
1985 nsContentUtils::ExtractErrorValues(cx
, exn
, details
.mFilename
, &details
.mLine
,
1986 &details
.mColumn
, details
.mMessage
);
1988 JS::UniqueChars buf
=
1989 JS::FormatStackDump(cx
, /* showArgs = */ false, /* showLocals = */ false,
1990 /* showThisProps = */ false);
1991 CopyUTF8toUTF16(mozilla::MakeStringSpan(buf
.get()), details
.mStack
);
1993 mThrownError
.emplace(std::move(details
));
1996 void CycleCollectedJSRuntime::ClearRecentDevError() {
1997 mErrorInterceptor
.mThrownError
.reset();
2000 bool CycleCollectedJSRuntime::GetRecentDevError(
2001 JSContext
* cx
, JS::MutableHandle
<JS::Value
> error
) {
2002 if (!mErrorInterceptor
.mThrownError
) {
2006 // Create a copy of the exception.
2007 JS::RootedObject
obj(cx
, JS_NewPlainObject(cx
));
2012 JS::RootedValue
message(cx
);
2013 JS::RootedValue
filename(cx
);
2014 JS::RootedValue
stack(cx
);
2015 if (!ToJSValue(cx
, mErrorInterceptor
.mThrownError
->mMessage
, &message
) ||
2016 !ToJSValue(cx
, mErrorInterceptor
.mThrownError
->mFilename
, &filename
) ||
2017 !ToJSValue(cx
, mErrorInterceptor
.mThrownError
->mStack
, &stack
)) {
2021 // Build the object.
2022 const auto FLAGS
= JSPROP_READONLY
| JSPROP_ENUMERATE
| JSPROP_PERMANENT
;
2023 if (!JS_DefineProperty(cx
, obj
, "message", message
, FLAGS
) ||
2024 !JS_DefineProperty(cx
, obj
, "fileName", filename
, FLAGS
) ||
2025 !JS_DefineProperty(cx
, obj
, "lineNumber",
2026 mErrorInterceptor
.mThrownError
->mLine
, FLAGS
) ||
2027 !JS_DefineProperty(cx
, obj
, "stack", stack
, FLAGS
)) {
2032 error
.setObject(*obj
);
2035 #endif // MOZ_JS_DEV_ERROR_INTERCEPTOR
2037 #undef MOZ_JS_DEV_ERROR_INTERCEPTOR