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 "nsStringBuffer.h"
101 #include "nsWrapperCache.h"
104 #if defined(XP_MACOSX)
105 # include "nsMacUtilsImpl.h"
108 #include "nsThread.h"
109 #include "nsThreadUtils.h"
110 #include "xpcpublic.h"
113 // For performance reasons, we make the JS Dev Error Interceptor a Nightly-only
115 # define MOZ_JS_DEV_ERROR_INTERCEPTOR = 1
116 #endif // NIGHTLY_BUILD
118 using namespace mozilla
;
119 using namespace mozilla::dom
;
123 struct DeferredFinalizeFunctionHolder
{
124 DeferredFinalizeFunction run
;
128 class IncrementalFinalizeRunnable
: public DiscardableRunnable
{
129 typedef AutoTArray
<DeferredFinalizeFunctionHolder
, 16> DeferredFinalizeArray
;
130 typedef CycleCollectedJSRuntime::DeferredFinalizerTable
131 DeferredFinalizerTable
;
133 CycleCollectedJSRuntime
* mRuntime
;
134 DeferredFinalizeArray mDeferredFinalizeFunctions
;
135 uint32_t mFinalizeFunctionToRun
;
138 static const PRTime SliceMillis
= 5; /* ms */
141 IncrementalFinalizeRunnable(CycleCollectedJSRuntime
* aRt
,
142 DeferredFinalizerTable
& aFinalizerTable
);
143 virtual ~IncrementalFinalizeRunnable();
145 void ReleaseNow(bool aLimited
);
150 } // namespace mozilla
152 struct NoteWeakMapChildrenTracer
: public JS::CallbackTracer
{
153 NoteWeakMapChildrenTracer(JSRuntime
* aRt
,
154 nsCycleCollectionNoteRootCallback
& aCb
)
155 : JS::CallbackTracer(aRt
, JS::TracerKind::Callback
),
160 mKeyDelegate(nullptr) {}
161 void onChild(JS::GCCellPtr aThing
, const char* name
) override
;
162 nsCycleCollectionNoteRootCallback
& mCb
;
166 JSObject
* mKeyDelegate
;
169 void NoteWeakMapChildrenTracer::onChild(JS::GCCellPtr aThing
,
171 if (aThing
.is
<JSString
>()) {
175 if (!JS::GCThingIsMarkedGrayInCC(aThing
) && !mCb
.WantAllTraces()) {
179 if (JS::IsCCTraceKind(aThing
.kind())) {
180 mCb
.NoteWeakMapping(mMap
, mKey
, mKeyDelegate
, aThing
);
183 JS::TraceChildren(this, aThing
);
187 struct NoteWeakMapsTracer
: public js::WeakMapTracer
{
188 NoteWeakMapsTracer(JSRuntime
* aRt
, nsCycleCollectionNoteRootCallback
& aCccb
)
189 : js::WeakMapTracer(aRt
), mCb(aCccb
), mChildTracer(aRt
, aCccb
) {}
190 void trace(JSObject
* aMap
, JS::GCCellPtr aKey
, JS::GCCellPtr aValue
) override
;
191 nsCycleCollectionNoteRootCallback
& mCb
;
192 NoteWeakMapChildrenTracer mChildTracer
;
195 void NoteWeakMapsTracer::trace(JSObject
* aMap
, JS::GCCellPtr aKey
,
196 JS::GCCellPtr aValue
) {
197 // If nothing that could be held alive by this entry is marked gray, return.
198 if ((!aKey
|| !JS::GCThingIsMarkedGrayInCC(aKey
)) &&
199 MOZ_LIKELY(!mCb
.WantAllTraces())) {
200 if (!aValue
|| !JS::GCThingIsMarkedGrayInCC(aValue
) ||
201 aValue
.is
<JSString
>()) {
206 // The cycle collector can only properly reason about weak maps if it can
207 // reason about the liveness of their keys, which in turn requires that
208 // the key can be represented in the cycle collector graph. All existing
209 // uses of weak maps use either objects or scripts as keys, which are okay.
210 MOZ_ASSERT(JS::IsCCTraceKind(aKey
.kind()));
212 // As an emergency fallback for non-debug builds, if the key is not
213 // representable in the cycle collector graph, we treat it as marked. This
214 // can cause leaks, but is preferable to ignoring the binding, which could
215 // cause the cycle collector to free live objects.
216 if (!JS::IsCCTraceKind(aKey
.kind())) {
220 JSObject
* kdelegate
= nullptr;
221 if (aKey
.is
<JSObject
>()) {
222 kdelegate
= js::UncheckedUnwrapWithoutExpose(&aKey
.as
<JSObject
>());
225 if (JS::IsCCTraceKind(aValue
.kind())) {
226 mCb
.NoteWeakMapping(aMap
, aKey
, kdelegate
, aValue
);
228 mChildTracer
.mTracedAny
= false;
229 mChildTracer
.mMap
= aMap
;
230 mChildTracer
.mKey
= aKey
;
231 mChildTracer
.mKeyDelegate
= kdelegate
;
233 if (!aValue
.is
<JSString
>()) {
234 JS::TraceChildren(&mChildTracer
, aValue
);
237 // The delegate could hold alive the key, so report something to the CC
238 // if we haven't already.
239 if (!mChildTracer
.mTracedAny
&& aKey
&& JS::GCThingIsMarkedGrayInCC(aKey
) &&
241 mCb
.NoteWeakMapping(aMap
, aKey
, kdelegate
, nullptr);
246 // Report whether the key or value of a weak mapping entry are gray but need to
248 static void ShouldWeakMappingEntryBeBlack(JSObject
* aMap
, JS::GCCellPtr aKey
,
249 JS::GCCellPtr aValue
,
250 bool* aKeyShouldBeBlack
,
251 bool* aValueShouldBeBlack
) {
252 *aKeyShouldBeBlack
= false;
253 *aValueShouldBeBlack
= false;
255 // If nothing that could be held alive by this entry is marked gray, return.
256 bool keyMightNeedMarking
= aKey
&& JS::GCThingIsMarkedGrayInCC(aKey
);
257 bool valueMightNeedMarking
= aValue
&& JS::GCThingIsMarkedGrayInCC(aValue
) &&
258 aValue
.kind() != JS::TraceKind::String
;
259 if (!keyMightNeedMarking
&& !valueMightNeedMarking
) {
263 if (!JS::IsCCTraceKind(aKey
.kind())) {
267 if (keyMightNeedMarking
&& aKey
.is
<JSObject
>()) {
268 JSObject
* kdelegate
=
269 js::UncheckedUnwrapWithoutExpose(&aKey
.as
<JSObject
>());
270 if (kdelegate
&& !JS::ObjectIsMarkedGray(kdelegate
) &&
271 (!aMap
|| !JS::ObjectIsMarkedGray(aMap
))) {
272 *aKeyShouldBeBlack
= true;
276 if (aValue
&& JS::GCThingIsMarkedGrayInCC(aValue
) &&
277 (!aKey
|| !JS::GCThingIsMarkedGrayInCC(aKey
)) &&
278 (!aMap
|| !JS::ObjectIsMarkedGray(aMap
)) &&
279 aValue
.kind() != JS::TraceKind::Shape
) {
280 *aValueShouldBeBlack
= true;
284 struct FixWeakMappingGrayBitsTracer
: public js::WeakMapTracer
{
285 explicit FixWeakMappingGrayBitsTracer(JSRuntime
* aRt
)
286 : js::WeakMapTracer(aRt
) {}
291 js::TraceWeakMaps(this);
292 } while (mAnyMarked
);
295 void trace(JSObject
* aMap
, JS::GCCellPtr aKey
,
296 JS::GCCellPtr aValue
) override
{
297 bool keyShouldBeBlack
;
298 bool valueShouldBeBlack
;
299 ShouldWeakMappingEntryBeBlack(aMap
, aKey
, aValue
, &keyShouldBeBlack
,
300 &valueShouldBeBlack
);
301 if (keyShouldBeBlack
&& JS::UnmarkGrayGCThingRecursively(aKey
)) {
305 if (valueShouldBeBlack
&& JS::UnmarkGrayGCThingRecursively(aValue
)) {
310 MOZ_INIT_OUTSIDE_CTOR
bool mAnyMarked
;
314 // Check whether weak maps are marked correctly according to the logic above.
315 struct CheckWeakMappingGrayBitsTracer
: public js::WeakMapTracer
{
316 explicit CheckWeakMappingGrayBitsTracer(JSRuntime
* aRt
)
317 : js::WeakMapTracer(aRt
), mFailed(false) {}
319 static bool Check(JSRuntime
* aRt
) {
320 CheckWeakMappingGrayBitsTracer
tracer(aRt
);
321 js::TraceWeakMaps(&tracer
);
322 return !tracer
.mFailed
;
325 void trace(JSObject
* aMap
, JS::GCCellPtr aKey
,
326 JS::GCCellPtr aValue
) override
{
327 bool keyShouldBeBlack
;
328 bool valueShouldBeBlack
;
329 ShouldWeakMappingEntryBeBlack(aMap
, aKey
, aValue
, &keyShouldBeBlack
,
330 &valueShouldBeBlack
);
332 if (keyShouldBeBlack
) {
333 fprintf(stderr
, "Weak mapping key %p of map %p should be black\n",
334 aKey
.asCell(), aMap
);
338 if (valueShouldBeBlack
) {
339 fprintf(stderr
, "Weak mapping value %p of map %p should be black\n",
340 aValue
.asCell(), aMap
);
349 static void CheckParticipatesInCycleCollection(JS::GCCellPtr aThing
,
352 bool* cycleCollectionEnabled
= static_cast<bool*>(aClosure
);
354 if (*cycleCollectionEnabled
) {
358 if (JS::IsCCTraceKind(aThing
.kind()) && JS::GCThingIsMarkedGrayInCC(aThing
)) {
359 *cycleCollectionEnabled
= true;
364 JSGCThingParticipant::TraverseNative(void* aPtr
,
365 nsCycleCollectionTraversalCallback
& aCb
) {
366 auto runtime
= reinterpret_cast<CycleCollectedJSRuntime
*>(
367 reinterpret_cast<char*>(this) -
368 offsetof(CycleCollectedJSRuntime
, mGCThingCycleCollectorGlobal
));
370 JS::GCCellPtr
cellPtr(aPtr
, JS::GCThingTraceKind(aPtr
));
371 runtime
->TraverseGCThing(CycleCollectedJSRuntime::TRAVERSE_FULL
, cellPtr
,
376 // NB: This is only used to initialize the participant in
377 // CycleCollectedJSRuntime. It should never be used directly.
378 static JSGCThingParticipant sGCThingCycleCollectorGlobal
;
381 JSZoneParticipant::TraverseNative(void* aPtr
,
382 nsCycleCollectionTraversalCallback
& aCb
) {
383 auto runtime
= reinterpret_cast<CycleCollectedJSRuntime
*>(
384 reinterpret_cast<char*>(this) -
385 offsetof(CycleCollectedJSRuntime
, mJSZoneCycleCollectorGlobal
));
387 MOZ_ASSERT(!aCb
.WantAllTraces());
388 JS::Zone
* zone
= static_cast<JS::Zone
*>(aPtr
);
390 runtime
->TraverseZone(zone
, aCb
);
394 struct TraversalTracer
: public JS::CallbackTracer
{
395 TraversalTracer(JSRuntime
* aRt
, nsCycleCollectionTraversalCallback
& aCb
)
396 : JS::CallbackTracer(aRt
, JS::TracerKind::Callback
,
397 JS::TraceOptions(JS::WeakMapTraceAction::Skip
,
398 JS::WeakEdgeTraceAction::Trace
)),
400 void onChild(JS::GCCellPtr aThing
, const char* name
) override
;
401 nsCycleCollectionTraversalCallback
& mCb
;
404 void TraversalTracer::onChild(JS::GCCellPtr aThing
, const char* name
) {
405 // Checking strings and symbols for being gray is rather slow, and we don't
406 // need either of them for the cycle collector.
407 if (aThing
.is
<JSString
>() || aThing
.is
<JS::Symbol
>()) {
411 // Don't traverse non-gray objects, unless we want all traces.
412 if (!JS::GCThingIsMarkedGrayInCC(aThing
) && !mCb
.WantAllTraces()) {
417 * This function needs to be careful to avoid stack overflow. Normally, when
418 * IsCCTraceKind is true, the recursion terminates immediately as we just add
419 * |thing| to the CC graph. So overflow is only possible when there are long
420 * or cyclic chains of non-IsCCTraceKind GC things. Places where this can
421 * occur use special APIs to handle such chains iteratively.
423 if (JS::IsCCTraceKind(aThing
.kind())) {
424 if (MOZ_UNLIKELY(mCb
.WantDebugInfo())) {
426 context().getEdgeName(name
, buffer
, sizeof(buffer
));
427 mCb
.NoteNextEdgeName(buffer
);
429 mCb
.NoteJSChild(aThing
);
433 // Allow re-use of this tracer inside trace callback.
434 JS::AutoClearTracingContext
actc(this);
436 if (aThing
.is
<js::Shape
>()) {
437 // The maximum depth of traversal when tracing a Shape is unbounded, due to
438 // the parent pointers on the shape.
439 JS_TraceShapeCycleCollectorChildren(this, aThing
);
441 JS::TraceChildren(this, aThing
);
446 * The cycle collection participant for a Zone is intended to produce the same
447 * results as if all of the gray GCthings in a zone were merged into a single
448 * node, except for self-edges. This avoids the overhead of representing all of
449 * the GCthings in the zone in the cycle collector graph, which should be much
450 * faster if many of the GCthings in the zone are gray.
452 * Zone merging should not always be used, because it is a conservative
453 * approximation of the true cycle collector graph that can incorrectly identify
454 * some garbage objects as being live. For instance, consider two cycles that
455 * pass through a zone, where one is garbage and the other is live. If we merge
456 * the entire zone, the cycle collector will think that both are alive.
458 * We don't have to worry about losing track of a garbage cycle, because any
459 * such garbage cycle incorrectly identified as live must contain at least one
460 * C++ to JS edge, and XPConnect will always add the C++ object to the CC graph.
461 * (This is in contrast to pure C++ garbage cycles, which must always be
462 * properly identified, because we clear the purple buffer during every CC,
463 * which may contain the last reference to a garbage cycle.)
466 // NB: This is only used to initialize the participant in
467 // CycleCollectedJSRuntime. It should never be used directly.
468 static const JSZoneParticipant sJSZoneCycleCollectorGlobal
;
470 static void JSObjectsTenuredCb(JSContext
* aContext
, void* aData
) {
471 static_cast<CycleCollectedJSRuntime
*>(aData
)->JSObjectsTenured();
474 static void MozCrashWarningReporter(JSContext
*, JSErrorReport
*) {
475 MOZ_CRASH("Why is someone touching JSAPI without an AutoJSAPI?");
478 JSHolderMap::Entry::Entry() : Entry(nullptr, nullptr, nullptr) {}
480 JSHolderMap::Entry::Entry(void* aHolder
, nsScriptObjectTracer
* aTracer
,
491 void JSHolderMap::EntryVectorIter::Settle() {
496 Entry
* entry
= &mIter
.Get();
498 // If the entry has been cleared, remove it and shrink the vector.
499 if (!entry
->mHolder
&& !mHolderMap
.RemoveEntry(mVector
, entry
)) {
500 // We removed the last entry, so reset the iterator to an empty one.
501 mIter
= EntryVector().Iter();
506 JSHolderMap::Iter::Iter(JSHolderMap
& aMap
, WhichHolders aWhich
)
507 : mHolderMap(aMap
), mIter(aMap
, aMap
.mAnyZoneJSHolders
) {
508 MOZ_RELEASE_ASSERT(!mHolderMap
.mHasIterator
);
509 mHolderMap
.mHasIterator
= true;
511 // Populate vector of zones to iterate after the any-zone holders.
512 for (auto i
= aMap
.mPerZoneJSHolders
.iter(); !i
.done(); i
.next()) {
513 JS::Zone
* zone
= i
.get().key();
514 if (aWhich
== AllHolders
|| JS::NeedGrayRootsForZone(i
.get().key())) {
515 MOZ_ALWAYS_TRUE(mZones
.append(zone
));
522 void JSHolderMap::Iter::Settle() {
523 while (mIter
.Done()) {
524 if (mZone
&& mIter
.Vector().IsEmpty()) {
525 mHolderMap
.mPerZoneJSHolders
.remove(mZone
);
529 if (mZones
.empty()) {
533 mZone
= mZones
.popCopy();
534 EntryVector
& vector
= *mHolderMap
.mPerZoneJSHolders
.lookup(mZone
)->value();
535 new (&mIter
) EntryVectorIter(mHolderMap
, vector
);
539 void JSHolderMap::Iter::UpdateForRemovals() {
544 JSHolderMap::JSHolderMap() : mJSHolderMap(256) {}
546 bool JSHolderMap::RemoveEntry(EntryVector
& aJSHolders
, Entry
* aEntry
) {
548 MOZ_ASSERT(!aEntry
->mHolder
);
550 // Remove all dead entries from the end of the vector.
551 while (!aJSHolders
.GetLast().mHolder
&& &aJSHolders
.GetLast() != aEntry
) {
552 aJSHolders
.PopLast();
555 // Swap the element we want to remove with the last one and update the hash
557 Entry
* lastEntry
= &aJSHolders
.GetLast();
558 if (aEntry
!= lastEntry
) {
559 MOZ_ASSERT(lastEntry
->mHolder
);
560 *aEntry
= *lastEntry
;
561 MOZ_ASSERT(mJSHolderMap
.has(aEntry
->mHolder
));
562 MOZ_ALWAYS_TRUE(mJSHolderMap
.put(aEntry
->mHolder
, aEntry
));
565 aJSHolders
.PopLast();
567 // Return whether aEntry is still in the vector.
568 return aEntry
!= lastEntry
;
571 bool JSHolderMap::Has(void* aHolder
) const { return mJSHolderMap
.has(aHolder
); }
573 nsScriptObjectTracer
* JSHolderMap::Get(void* aHolder
) const {
574 auto ptr
= mJSHolderMap
.lookup(aHolder
);
579 Entry
* entry
= ptr
->value();
580 MOZ_ASSERT(entry
->mHolder
== aHolder
);
581 return entry
->mTracer
;
584 nsScriptObjectTracer
* JSHolderMap::Extract(void* aHolder
) {
587 auto ptr
= mJSHolderMap
.lookup(aHolder
);
592 Entry
* entry
= ptr
->value();
593 MOZ_ASSERT(entry
->mHolder
== aHolder
);
594 nsScriptObjectTracer
* tracer
= entry
->mTracer
;
596 // Clear the entry's contents. It will be removed the next time iteration
597 // visits this entry.
600 mJSHolderMap
.remove(ptr
);
605 void JSHolderMap::Put(void* aHolder
, nsScriptObjectTracer
* aTracer
,
610 // Don't associate multi-zone holders with a zone, even if one is supplied.
611 if (!aTracer
->IsSingleZoneJSHolder()) {
615 auto ptr
= mJSHolderMap
.lookupForAdd(aHolder
);
617 Entry
* entry
= ptr
->value();
619 MOZ_ASSERT(entry
->mHolder
== aHolder
);
620 MOZ_ASSERT(entry
->mTracer
== aTracer
,
621 "Don't call HoldJSObjects in superclass ctors");
624 MOZ_ASSERT(entry
->mZone
== aZone
);
626 entry
->mZone
= aZone
;
630 entry
->mTracer
= aTracer
;
634 EntryVector
* vector
= &mAnyZoneJSHolders
;
636 auto ptr
= mPerZoneJSHolders
.lookupForAdd(aZone
);
639 mPerZoneJSHolders
.add(ptr
, aZone
, MakeUnique
<EntryVector
>()));
641 vector
= ptr
->value().get();
644 vector
->InfallibleAppend(Entry
{aHolder
, aTracer
, aZone
});
645 MOZ_ALWAYS_TRUE(mJSHolderMap
.add(ptr
, aHolder
, &vector
->GetLast()));
648 size_t JSHolderMap::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf
) const {
651 // We're deliberately not measuring anything hanging off the entries in
653 n
+= mJSHolderMap
.shallowSizeOfExcludingThis(aMallocSizeOf
);
654 n
+= mAnyZoneJSHolders
.SizeOfExcludingThis(aMallocSizeOf
);
655 n
+= mPerZoneJSHolders
.shallowSizeOfExcludingThis(aMallocSizeOf
);
656 for (auto i
= mPerZoneJSHolders
.iter(); !i
.done(); i
.next()) {
657 n
+= i
.get().value()->SizeOfExcludingThis(aMallocSizeOf
);
663 static bool InitializeShadowRealm(JSContext
* aCx
,
664 JS::Handle
<JSObject
*> aGlobal
) {
665 MOZ_ASSERT(StaticPrefs::javascript_options_experimental_shadow_realms());
667 JSAutoRealm
ar(aCx
, aGlobal
);
668 return dom::RegisterShadowRealmBindings(aCx
, aGlobal
);
671 CycleCollectedJSRuntime::CycleCollectedJSRuntime(JSContext
* aCx
)
673 mGCThingCycleCollectorGlobal(sGCThingCycleCollectorGlobal
),
674 mJSZoneCycleCollectorGlobal(sJSZoneCycleCollectorGlobal
),
675 mJSRuntime(JS_GetRuntime(aCx
)),
676 mHasPendingIdleGCTask(false),
677 mPrevGCSliceCallback(nullptr),
678 mOutOfMemoryState(OOMState::OK
),
679 mLargeAllocationFailureState(OOMState::OK
)
682 mShutdownCalled(false)
685 MOZ_COUNT_CTOR(CycleCollectedJSRuntime
);
687 MOZ_ASSERT(mJSRuntime
);
689 #if defined(XP_MACOSX)
690 if (!XRE_IsParentProcess()) {
691 nsMacUtilsImpl::EnableTCSMIfAvailable();
695 if (!JS_AddExtraGCRootsTracer(aCx
, TraceBlackJS
, this)) {
696 MOZ_CRASH("JS_AddExtraGCRootsTracer failed");
698 JS_SetGrayGCRootsTracer(aCx
, TraceGrayJS
, this);
699 JS_SetGCCallback(aCx
, GCCallback
, this);
700 mPrevGCSliceCallback
= JS::SetGCSliceCallback(aCx
, GCSliceCallback
);
702 if (NS_IsMainThread()) {
703 // We would like to support all threads here, but the way timeline consumers
704 // are set up currently, you can either add a marker for one specific
705 // docshell, or for every consumer globally. We would like to add a marker
706 // for every consumer observing anything on this thread, but that is not
707 // currently possible. For now, add global markers only when we are on the
708 // main thread, since the UI for this tracing data only displays data
709 // relevant to the main-thread.
710 JS::AddGCNurseryCollectionCallback(aCx
, GCNurseryCollectionCallback
,
714 JS_SetObjectsTenuredCallback(aCx
, JSObjectsTenuredCb
, this);
715 JS::SetOutOfMemoryCallback(aCx
, OutOfMemoryCallback
, this);
716 JS::SetWaitCallback(mJSRuntime
, BeforeWaitCallback
, AfterWaitCallback
,
717 sizeof(dom::AutoYieldJSThreadExecution
));
718 JS::SetWarningReporter(aCx
, MozCrashWarningReporter
);
719 JS::SetShadowRealmInitializeGlobalCallback(aCx
, InitializeShadowRealm
);
720 JS::SetShadowRealmGlobalCreationCallback(aCx
, dom::NewShadowRealmGlobal
);
722 js::AutoEnterOOMUnsafeRegion::setAnnotateOOMAllocationSizeCallback(
723 CrashReporter::AnnotateOOMAllocationSize
);
725 static js::DOMCallbacks DOMcallbacks
= {InstanceClassHasProtoAtDepth
};
726 SetDOMCallbacks(aCx
, &DOMcallbacks
);
727 js::SetScriptEnvironmentPreparer(aCx
, &mEnvironmentPreparer
);
729 JS::dbg::SetDebuggerMallocSizeOf(aCx
, moz_malloc_size_of
);
731 #ifdef MOZ_JS_DEV_ERROR_INTERCEPTOR
732 JS_SetErrorInterceptorCallback(mJSRuntime
, &mErrorInterceptor
);
733 #endif // MOZ_JS_DEV_ERROR_INTERCEPTOR
735 JS_SetDestroyZoneCallback(aCx
, OnZoneDestroyed
);
738 #ifdef NS_BUILD_REFCNT_LOGGING
739 class JSLeakTracer
: public JS::CallbackTracer
{
741 explicit JSLeakTracer(JSRuntime
* aRuntime
)
742 : JS::CallbackTracer(aRuntime
, JS::TracerKind::Callback
,
743 JS::WeakMapTraceAction::TraceKeysAndValues
) {}
746 void onChild(JS::GCCellPtr thing
, const char* name
) override
{
747 const char* kindName
= JS::GCTraceKindToAscii(thing
.kind());
748 size_t size
= JS::GCTraceKindSize(thing
.kind());
749 MOZ_LOG_CTOR(thing
.asCell(), kindName
, size
);
754 void CycleCollectedJSRuntime::Shutdown(JSContext
* aCx
) {
755 #ifdef MOZ_JS_DEV_ERROR_INTERCEPTOR
756 mErrorInterceptor
.Shutdown(mJSRuntime
);
757 #endif // MOZ_JS_DEV_ERROR_INTERCEPTOR
759 // There should not be any roots left to trace at this point. Ensure any that
760 // remain are flagged as leaks.
761 #ifdef NS_BUILD_REFCNT_LOGGING
762 JSLeakTracer
tracer(Runtime());
763 TraceNativeBlackRoots(&tracer
);
764 TraceAllNativeGrayRoots(&tracer
);
768 mShutdownCalled
= true;
771 JS_SetDestroyZoneCallback(aCx
, nullptr);
773 if (NS_IsMainThread()) {
774 JS::RemoveGCNurseryCollectionCallback(aCx
, GCNurseryCollectionCallback
,
779 CycleCollectedJSRuntime::~CycleCollectedJSRuntime() {
780 MOZ_COUNT_DTOR(CycleCollectedJSRuntime
);
781 MOZ_ASSERT(!mDeferredFinalizerTable
.Count());
782 MOZ_ASSERT(!mFinalizeRunnable
);
783 MOZ_ASSERT(mShutdownCalled
);
786 void CycleCollectedJSRuntime::SetContext(CycleCollectedJSContext
* aContext
) {
787 MOZ_ASSERT(!mContext
|| !aContext
, "Don't replace the context!");
791 size_t CycleCollectedJSRuntime::SizeOfExcludingThis(
792 MallocSizeOf aMallocSizeOf
) const {
793 return mJSHolders
.SizeOfExcludingThis(aMallocSizeOf
);
796 void CycleCollectedJSRuntime::UnmarkSkippableJSHolders() {
797 for (JSHolderMap::Iter
entry(mJSHolders
); !entry
.Done(); entry
.Next()) {
798 entry
->mTracer
->CanSkip(entry
->mHolder
, true);
802 void CycleCollectedJSRuntime::DescribeGCThing(
803 bool aIsMarked
, JS::GCCellPtr aThing
,
804 nsCycleCollectionTraversalCallback
& aCb
) const {
805 if (!aCb
.WantDebugInfo()) {
806 aCb
.DescribeGCedNode(aIsMarked
, "JS Object");
811 uint64_t compartmentAddress
= 0;
812 if (aThing
.is
<JSObject
>()) {
813 JSObject
* obj
= &aThing
.as
<JSObject
>();
814 compartmentAddress
= (uint64_t)JS::GetCompartment(obj
);
815 const JSClass
* clasp
= JS::GetClass(obj
);
817 // Give the subclass a chance to do something
818 if (DescribeCustomObjects(obj
, clasp
, name
)) {
819 // Nothing else to do!
820 } else if (js::IsFunctionObject(obj
)) {
821 JSFunction
* fun
= JS_GetObjectFunction(obj
);
822 JSString
* str
= JS_GetMaybePartialFunctionDisplayId(fun
);
824 JSLinearString
* linear
= JS_ASSERT_STRING_IS_LINEAR(str
);
826 AssignJSLinearString(chars
, linear
);
827 NS_ConvertUTF16toUTF8
fname(chars
);
828 SprintfLiteral(name
, "JS Object (Function - %s)", fname
.get());
830 SprintfLiteral(name
, "JS Object (Function)");
833 SprintfLiteral(name
, "JS Object (%s)", clasp
->name
);
836 SprintfLiteral(name
, "%s", JS::GCTraceKindToAscii(aThing
.kind()));
839 // Disable printing global for objects while we figure out ObjShrink fallout.
840 aCb
.DescribeGCedNode(aIsMarked
, name
, compartmentAddress
);
843 void CycleCollectedJSRuntime::NoteGCThingJSChildren(
844 JS::GCCellPtr aThing
, nsCycleCollectionTraversalCallback
& aCb
) const {
845 TraversalTracer
trc(mJSRuntime
, aCb
);
846 JS::TraceChildren(&trc
, aThing
);
849 void CycleCollectedJSRuntime::NoteGCThingXPCOMChildren(
850 const JSClass
* aClasp
, JSObject
* aObj
,
851 nsCycleCollectionTraversalCallback
& aCb
) const {
853 MOZ_ASSERT(aClasp
== JS::GetClass(aObj
));
855 JS::Rooted
<JSObject
*> obj(RootingCx(), aObj
);
857 if (NoteCustomGCThingXPCOMChildren(aClasp
, obj
, aCb
)) {
858 // Nothing else to do!
862 // XXX This test does seem fragile, we should probably allowlist classes
863 // that do hold a strong reference, but that might not be possible.
864 if (aClasp
->slot0IsISupports()) {
865 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb
, "JS::GetObjectISupports(obj)");
866 aCb
.NoteXPCOMChild(JS::GetObjectISupports
<nsISupports
>(obj
));
870 const DOMJSClass
* domClass
= GetDOMClass(aClasp
);
872 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb
, "UnwrapDOMObject(obj)");
873 // It's possible that our object is an unforgeable holder object, in
874 // which case it doesn't actually have a C++ DOM object associated with
875 // it. Use UnwrapPossiblyNotInitializedDOMObject, which produces null in
876 // that case, since NoteXPCOMChild/NoteNativeChild are null-safe.
877 if (domClass
->mDOMObjectIsISupports
) {
879 UnwrapPossiblyNotInitializedDOMObject
<nsISupports
>(obj
));
880 } else if (domClass
->mParticipant
) {
881 aCb
.NoteNativeChild(UnwrapPossiblyNotInitializedDOMObject
<void>(obj
),
882 domClass
->mParticipant
);
887 if (IsRemoteObjectProxy(obj
)) {
889 static_cast<const RemoteObjectProxyBase
*>(js::GetProxyHandler(obj
));
890 return handler
->NoteChildren(obj
, aCb
);
893 JS::Value value
= js::MaybeGetScriptPrivate(obj
);
894 if (!value
.isUndefined()) {
895 aCb
.NoteXPCOMChild(static_cast<nsISupports
*>(value
.toPrivate()));
899 void CycleCollectedJSRuntime::TraverseGCThing(
900 TraverseSelect aTs
, JS::GCCellPtr aThing
,
901 nsCycleCollectionTraversalCallback
& aCb
) {
902 bool isMarkedGray
= JS::GCThingIsMarkedGrayInCC(aThing
);
904 if (aTs
== TRAVERSE_FULL
) {
905 DescribeGCThing(!isMarkedGray
, aThing
, aCb
);
908 // If this object is alive, then all of its children are alive. For JS
909 // objects, the black-gray invariant ensures the children are also marked
910 // black. For C++ objects, the ref count from this object will keep them
911 // alive. Thus we don't need to trace our children, unless we are debugging
912 // using WantAllTraces.
913 if (!isMarkedGray
&& !aCb
.WantAllTraces()) {
917 if (aTs
== TRAVERSE_FULL
) {
918 NoteGCThingJSChildren(aThing
, aCb
);
921 if (aThing
.is
<JSObject
>()) {
922 JSObject
* obj
= &aThing
.as
<JSObject
>();
923 NoteGCThingXPCOMChildren(JS::GetClass(obj
), obj
, aCb
);
927 struct TraverseObjectShimClosure
{
928 nsCycleCollectionTraversalCallback
& cb
;
929 CycleCollectedJSRuntime
* self
;
932 void CycleCollectedJSRuntime::TraverseZone(
933 JS::Zone
* aZone
, nsCycleCollectionTraversalCallback
& aCb
) {
935 * We treat the zone as being gray. We handle non-gray GCthings in the
936 * zone by not reporting their children to the CC. The black-gray invariant
937 * ensures that any JS children will also be non-gray, and thus don't need to
938 * be added to the graph. For C++ children, not representing the edge from the
939 * non-gray JS GCthings to the C++ object will keep the child alive.
941 * We don't allow zone merging in a WantAllTraces CC, because then these
942 * assumptions don't hold.
944 aCb
.DescribeGCedNode(false, "JS Zone");
947 * Every JS child of everything in the zone is either in the zone
948 * or is a cross-compartment wrapper. In the former case, we don't need to
949 * represent these edges in the CC graph because JS objects are not ref
950 * counted. In the latter case, the JS engine keeps a map of these wrappers,
951 * which we iterate over. Edges between compartments in the same zone will add
952 * unnecessary loop edges to the graph (bug 842137).
954 TraversalTracer
trc(mJSRuntime
, aCb
);
955 js::TraceGrayWrapperTargets(&trc
, aZone
);
958 * To find C++ children of things in the zone, we scan every JS Object in
959 * the zone. Only JS Objects can have C++ children.
961 TraverseObjectShimClosure closure
= {aCb
, this};
962 js::IterateGrayObjects(aZone
, TraverseObjectShim
, &closure
);
966 void CycleCollectedJSRuntime::TraverseObjectShim(
967 void* aData
, JS::GCCellPtr aThing
, const JS::AutoRequireNoGC
& nogc
) {
968 TraverseObjectShimClosure
* closure
=
969 static_cast<TraverseObjectShimClosure
*>(aData
);
971 MOZ_ASSERT(aThing
.is
<JSObject
>());
972 closure
->self
->TraverseGCThing(CycleCollectedJSRuntime::TRAVERSE_CPP
, aThing
,
976 void CycleCollectedJSRuntime::TraverseNativeRoots(
977 nsCycleCollectionNoteRootCallback
& aCb
) {
978 // NB: This is here just to preserve the existing XPConnect order. I doubt it
979 // would hurt to do this after the JS holders.
980 TraverseAdditionalNativeRoots(aCb
);
982 for (JSHolderMap::Iter
entry(mJSHolders
); !entry
.Done(); entry
.Next()) {
983 void* holder
= entry
->mHolder
;
984 nsScriptObjectTracer
* tracer
= entry
->mTracer
;
986 bool noteRoot
= false;
987 if (MOZ_UNLIKELY(aCb
.WantAllTraces())) {
990 tracer
->Trace(holder
,
991 TraceCallbackFunc(CheckParticipatesInCycleCollection
),
996 aCb
.NoteNativeRoot(holder
, tracer
);
1002 void CycleCollectedJSRuntime::TraceBlackJS(JSTracer
* aTracer
, void* aData
) {
1003 CycleCollectedJSRuntime
* self
= static_cast<CycleCollectedJSRuntime
*>(aData
);
1005 self
->TraceNativeBlackRoots(aTracer
);
1009 bool CycleCollectedJSRuntime::TraceGrayJS(JSTracer
* aTracer
,
1010 js::SliceBudget
& budget
,
1012 CycleCollectedJSRuntime
* self
= static_cast<CycleCollectedJSRuntime
*>(aData
);
1014 // Mark these roots as gray so the CC can walk them later.
1016 JSHolderMap::WhichHolders which
= JSHolderMap::AllHolders
;
1018 // Only trace holders in collecting zones when marking, except if we are
1019 // collecting the atoms zone since any holder may point into that zone.
1020 if (aTracer
->isMarkingTracer() &&
1021 !JS::AtomsZoneIsCollecting(self
->Runtime())) {
1022 which
= JSHolderMap::HoldersRequiredForGrayMarking
;
1025 return self
->TraceNativeGrayRoots(aTracer
, which
, budget
);
1029 void CycleCollectedJSRuntime::GCCallback(JSContext
* aContext
,
1031 JS::GCReason aReason
, void* aData
) {
1032 CycleCollectedJSRuntime
* self
= static_cast<CycleCollectedJSRuntime
*>(aData
);
1034 MOZ_ASSERT(CycleCollectedJSContext::Get()->Context() == aContext
);
1035 MOZ_ASSERT(CycleCollectedJSContext::Get()->Runtime() == self
);
1037 self
->OnGC(aContext
, aStatus
, aReason
);
1041 void CycleCollectedJSRuntime::GCSliceCallback(JSContext
* aContext
,
1042 JS::GCProgress aProgress
,
1043 const JS::GCDescription
& aDesc
) {
1044 CycleCollectedJSRuntime
* self
= CycleCollectedJSRuntime::Get();
1045 MOZ_ASSERT(CycleCollectedJSContext::Get()->Context() == aContext
);
1047 if (profiler_thread_is_being_profiled_for_markers()) {
1048 if (aProgress
== JS::GC_CYCLE_END
) {
1049 struct GCMajorMarker
{
1050 static constexpr mozilla::Span
<const char> MarkerTypeName() {
1051 return mozilla::MakeStringSpan("GCMajor");
1053 static void StreamJSONMarkerData(
1054 mozilla::baseprofiler::SpliceableJSONWriter
& aWriter
,
1055 const mozilla::ProfilerString8View
& aTimingJSON
) {
1056 if (aTimingJSON
.Length() != 0) {
1057 aWriter
.SplicedJSONProperty("timings", aTimingJSON
);
1059 aWriter
.NullProperty("timings");
1062 static mozilla::MarkerSchema
MarkerTypeDisplay() {
1063 using MS
= mozilla::MarkerSchema
;
1064 MS schema
{MS::Location::MarkerChart
, MS::Location::MarkerTable
,
1065 MS::Location::TimelineMemory
};
1066 schema
.AddStaticLabelValue(
1068 "Summary data for an entire major GC, encompassing a set of "
1069 "incremental slices. The main thread is not blocked for the "
1070 "entire major GC interval, only for the individual slices.");
1071 // No display instructions here, there is special handling in the
1077 profiler_add_marker("GCMajor", baseprofiler::category::GCCC
,
1078 MarkerTiming::Interval(aDesc
.startTime(aContext
),
1079 aDesc
.endTime(aContext
)),
1081 ProfilerString8View::WrapNullTerminatedString(
1082 aDesc
.formatJSONProfiler(aContext
).get()));
1083 } else if (aProgress
== JS::GC_SLICE_END
) {
1084 struct GCSliceMarker
{
1085 static constexpr mozilla::Span
<const char> MarkerTypeName() {
1086 return mozilla::MakeStringSpan("GCSlice");
1088 static void StreamJSONMarkerData(
1089 mozilla::baseprofiler::SpliceableJSONWriter
& aWriter
,
1090 const mozilla::ProfilerString8View
& aTimingJSON
) {
1091 if (aTimingJSON
.Length() != 0) {
1092 aWriter
.SplicedJSONProperty("timings", aTimingJSON
);
1094 aWriter
.NullProperty("timings");
1097 static mozilla::MarkerSchema
MarkerTypeDisplay() {
1098 using MS
= mozilla::MarkerSchema
;
1099 MS schema
{MS::Location::MarkerChart
, MS::Location::MarkerTable
,
1100 MS::Location::TimelineMemory
};
1101 schema
.AddStaticLabelValue(
1103 "One slice of an incremental garbage collection (GC). The main "
1104 "thread is blocked during this time.");
1105 // No display instructions here, there is special handling in the
1111 profiler_add_marker("GCSlice", baseprofiler::category::GCCC
,
1112 MarkerTiming::Interval(aDesc
.lastSliceStart(aContext
),
1113 aDesc
.lastSliceEnd(aContext
)),
1115 ProfilerString8View::WrapNullTerminatedString(
1116 aDesc
.sliceToJSONProfiler(aContext
).get()));
1120 if (aProgress
== JS::GC_CYCLE_END
&&
1121 JS::dbg::FireOnGarbageCollectionHookRequired(aContext
)) {
1122 JS::GCReason reason
= aDesc
.reason_
;
1123 Unused
<< NS_WARN_IF(
1124 NS_FAILED(DebuggerOnGCRunnable::Enqueue(aContext
, aDesc
)) &&
1125 reason
!= JS::GCReason::SHUTDOWN_CC
&&
1126 reason
!= JS::GCReason::DESTROY_RUNTIME
&&
1127 reason
!= JS::GCReason::XPCONNECT_SHUTDOWN
);
1130 if (self
->mPrevGCSliceCallback
) {
1131 self
->mPrevGCSliceCallback(aContext
, aProgress
, aDesc
);
1136 void CycleCollectedJSRuntime::GCNurseryCollectionCallback(
1137 JSContext
* aContext
, JS::GCNurseryProgress aProgress
, JS::GCReason aReason
,
1139 CycleCollectedJSRuntime
* self
= CycleCollectedJSRuntime::Get();
1140 MOZ_ASSERT(CycleCollectedJSContext::Get()->Context() == aContext
);
1141 MOZ_ASSERT(NS_IsMainThread());
1143 TimeStamp now
= TimeStamp::Now();
1144 if (aProgress
== JS::GCNurseryProgress::GC_NURSERY_COLLECTION_START
) {
1145 self
->mLatestNurseryCollectionStart
= now
;
1146 } else if (aProgress
== JS::GCNurseryProgress::GC_NURSERY_COLLECTION_END
) {
1147 PerfStats::RecordMeasurement(PerfStats::Metric::MinorGC
,
1148 now
- self
->mLatestNurseryCollectionStart
);
1151 if (aProgress
== JS::GCNurseryProgress::GC_NURSERY_COLLECTION_END
&&
1152 profiler_thread_is_being_profiled_for_markers()) {
1153 struct GCMinorMarker
{
1154 static constexpr mozilla::Span
<const char> MarkerTypeName() {
1155 return mozilla::MakeStringSpan("GCMinor");
1157 static void StreamJSONMarkerData(
1158 mozilla::baseprofiler::SpliceableJSONWriter
& aWriter
,
1159 const mozilla::ProfilerString8View
& aTimingJSON
) {
1160 if (aTimingJSON
.Length() != 0) {
1161 aWriter
.SplicedJSONProperty("nursery", aTimingJSON
);
1163 aWriter
.NullProperty("nursery");
1166 static mozilla::MarkerSchema
MarkerTypeDisplay() {
1167 using MS
= mozilla::MarkerSchema
;
1168 MS schema
{MS::Location::MarkerChart
, MS::Location::MarkerTable
,
1169 MS::Location::TimelineMemory
};
1170 schema
.AddStaticLabelValue(
1172 "A minor GC (aka nursery collection) to clear out the buffer used "
1173 "for recent allocations and move surviving data to the tenured "
1174 "(long-lived) heap.");
1175 // No display instructions here, there is special handling in the
1181 profiler_add_marker(
1182 "GCMinor", baseprofiler::category::GCCC
,
1183 MarkerTiming::Interval(self
->mLatestNurseryCollectionStart
, now
),
1185 ProfilerString8View::WrapNullTerminatedString(
1186 JS::MinorGcToJSON(aContext
).get()));
1191 void CycleCollectedJSRuntime::OutOfMemoryCallback(JSContext
* aContext
,
1193 CycleCollectedJSRuntime
* self
= static_cast<CycleCollectedJSRuntime
*>(aData
);
1195 MOZ_ASSERT(CycleCollectedJSContext::Get()->Context() == aContext
);
1196 MOZ_ASSERT(CycleCollectedJSContext::Get()->Runtime() == self
);
1198 self
->OnOutOfMemory();
1202 void* CycleCollectedJSRuntime::BeforeWaitCallback(uint8_t* aMemory
) {
1203 MOZ_ASSERT(aMemory
);
1205 // aMemory is stack allocated memory to contain our RAII object. This allows
1206 // for us to avoid allocations on the heap during this callback.
1207 return new (aMemory
) dom::AutoYieldJSThreadExecution
;
1211 void CycleCollectedJSRuntime::AfterWaitCallback(void* aCookie
) {
1212 MOZ_ASSERT(aCookie
);
1213 static_cast<dom::AutoYieldJSThreadExecution
*>(aCookie
)
1214 ->~AutoYieldJSThreadExecution();
1217 struct JsGcTracer
: public TraceCallbacks
{
1218 virtual void Trace(JS::Heap
<JS::Value
>* aPtr
, const char* aName
,
1219 void* aClosure
) const override
{
1220 JS::TraceEdge(static_cast<JSTracer
*>(aClosure
), aPtr
, aName
);
1222 virtual void Trace(JS::Heap
<jsid
>* aPtr
, const char* aName
,
1223 void* aClosure
) const override
{
1224 JS::TraceEdge(static_cast<JSTracer
*>(aClosure
), aPtr
, aName
);
1226 virtual void Trace(JS::Heap
<JSObject
*>* aPtr
, const char* aName
,
1227 void* aClosure
) const override
{
1228 JS::TraceEdge(static_cast<JSTracer
*>(aClosure
), aPtr
, aName
);
1230 virtual void Trace(nsWrapperCache
* aPtr
, const char* aName
,
1231 void* aClosure
) const override
{
1232 aPtr
->TraceWrapper(static_cast<JSTracer
*>(aClosure
), aName
);
1234 virtual void Trace(JS::TenuredHeap
<JSObject
*>* aPtr
, const char* aName
,
1235 void* aClosure
) const override
{
1236 JS::TraceEdge(static_cast<JSTracer
*>(aClosure
), aPtr
, aName
);
1238 virtual void Trace(JS::Heap
<JSString
*>* aPtr
, const char* aName
,
1239 void* aClosure
) const override
{
1240 JS::TraceEdge(static_cast<JSTracer
*>(aClosure
), aPtr
, aName
);
1242 virtual void Trace(JS::Heap
<JSScript
*>* aPtr
, const char* aName
,
1243 void* aClosure
) const override
{
1244 JS::TraceEdge(static_cast<JSTracer
*>(aClosure
), aPtr
, aName
);
1246 virtual void Trace(JS::Heap
<JSFunction
*>* aPtr
, const char* aName
,
1247 void* aClosure
) const override
{
1248 JS::TraceEdge(static_cast<JSTracer
*>(aClosure
), aPtr
, aName
);
1252 void mozilla::TraceScriptHolder(nsISupports
* aHolder
, JSTracer
* aTracer
) {
1253 nsXPCOMCycleCollectionParticipant
* participant
= nullptr;
1254 CallQueryInterface(aHolder
, &participant
);
1255 participant
->Trace(aHolder
, JsGcTracer(), aTracer
);
1258 #if defined(NIGHTLY_BUILD) || defined(MOZ_DEV_EDITION) || defined(DEBUG)
1259 # define CHECK_SINGLE_ZONE_JS_HOLDERS
1262 #ifdef CHECK_SINGLE_ZONE_JS_HOLDERS
1264 // A tracer that checks that a JS holder only holds JS GC things in a single
1266 struct CheckZoneTracer
: public TraceCallbacks
{
1267 const char* mClassName
;
1268 mutable JS::Zone
* mZone
;
1270 explicit CheckZoneTracer(const char* aClassName
, JS::Zone
* aZone
= nullptr)
1271 : mClassName(aClassName
), mZone(aZone
) {}
1273 void checkZone(JS::Zone
* aZone
, const char* aName
) const {
1274 if (JS::IsAtomsZone(aZone
)) {
1275 // Any holder may contain pointers into the atoms zone.
1284 if (aZone
== mZone
) {
1288 // Most JS holders only contain pointers to GC things in a single zone. We
1289 // group holders by referent zone where possible, allowing us to improve GC
1290 // performance by only tracing holders for zones that are being collected.
1292 // Additionally, pointers from any holder into the atoms zone are allowed
1293 // since all holders are traced when we collect the atoms zone.
1295 // If you added a holder that has pointers into multiple zones do not
1296 // use NS_IMPL_CYCLE_COLLECTION_SINGLE_ZONE_SCRIPT_HOLDER_CLASS.
1297 MOZ_CRASH_UNSAFE_PRINTF(
1298 "JS holder %s contains pointers to GC things in more than one zone ("
1303 virtual void Trace(JS::Heap
<JS::Value
>* aPtr
, const char* aName
,
1304 void* aClosure
) const override
{
1305 JS::Value value
= aPtr
->unbarrieredGet();
1306 if (value
.isGCThing()) {
1307 checkZone(JS::GetGCThingZone(value
.toGCCellPtr()), aName
);
1310 virtual void Trace(JS::Heap
<jsid
>* aPtr
, const char* aName
,
1311 void* aClosure
) const override
{
1312 jsid id
= aPtr
->unbarrieredGet();
1313 if (id
.isGCThing()) {
1314 MOZ_ASSERT(JS::IsAtomsZone(JS::GetTenuredGCThingZone(id
.toGCCellPtr())));
1317 virtual void Trace(JS::Heap
<JSObject
*>* aPtr
, const char* aName
,
1318 void* aClosure
) const override
{
1319 JSObject
* obj
= aPtr
->unbarrieredGet();
1321 checkZone(js::GetObjectZoneFromAnyThread(obj
), aName
);
1324 virtual void Trace(nsWrapperCache
* aPtr
, const char* aName
,
1325 void* aClosure
) const override
{
1326 JSObject
* obj
= aPtr
->GetWrapperPreserveColor();
1328 checkZone(js::GetObjectZoneFromAnyThread(obj
), aName
);
1331 virtual void Trace(JS::TenuredHeap
<JSObject
*>* aPtr
, const char* aName
,
1332 void* aClosure
) const override
{
1333 JSObject
* obj
= aPtr
->unbarrieredGetPtr();
1335 checkZone(js::GetObjectZoneFromAnyThread(obj
), aName
);
1338 virtual void Trace(JS::Heap
<JSString
*>* aPtr
, const char* aName
,
1339 void* aClosure
) const override
{
1340 JSString
* str
= aPtr
->unbarrieredGet();
1342 checkZone(JS::GetStringZone(str
), aName
);
1345 virtual void Trace(JS::Heap
<JSScript
*>* aPtr
, const char* aName
,
1346 void* aClosure
) const override
{
1347 JSScript
* script
= aPtr
->unbarrieredGet();
1349 checkZone(JS::GetTenuredGCThingZone(JS::GCCellPtr(script
)), aName
);
1352 virtual void Trace(JS::Heap
<JSFunction
*>* aPtr
, const char* aName
,
1353 void* aClosure
) const override
{
1354 JSFunction
* fun
= aPtr
->unbarrieredGet();
1356 checkZone(js::GetObjectZoneFromAnyThread(JS_GetFunctionObject(fun
)),
1362 static inline void CheckHolderIsSingleZone(
1363 void* aHolder
, nsCycleCollectionParticipant
* aParticipant
,
1365 CheckZoneTracer
tracer(aParticipant
->ClassName(), aZone
);
1366 aParticipant
->Trace(aHolder
, tracer
, nullptr);
1371 static inline bool ShouldCheckSingleZoneHolders() {
1374 #elif defined(NIGHTLY_BUILD) || defined(MOZ_DEV_EDITION)
1375 // Don't check every time to avoid performance impact.
1376 return rand() % 256 == 0;
1382 #ifdef NS_BUILD_REFCNT_LOGGING
1383 void CycleCollectedJSRuntime::TraceAllNativeGrayRoots(JSTracer
* aTracer
) {
1384 MOZ_RELEASE_ASSERT(mHolderIter
.isNothing());
1385 js::SliceBudget budget
= js::SliceBudget::unlimited();
1387 TraceNativeGrayRoots(aTracer
, JSHolderMap::AllHolders
, budget
));
1391 bool CycleCollectedJSRuntime::TraceNativeGrayRoots(
1392 JSTracer
* aTracer
, JSHolderMap::WhichHolders aWhich
,
1393 js::SliceBudget
& aBudget
) {
1395 // NB: This is here just to preserve the existing XPConnect order. I doubt
1396 // it would hurt to do this after the JS holders.
1397 TraceAdditionalNativeGrayRoots(aTracer
);
1399 mHolderIter
.emplace(mJSHolders
, aWhich
);
1400 aBudget
.stepAndForceCheck();
1402 // Holders may have been removed between slices, so we may need to update
1404 mHolderIter
->UpdateForRemovals();
1407 bool finished
= TraceJSHolders(aTracer
, *mHolderIter
, aBudget
);
1409 mHolderIter
.reset();
1415 bool CycleCollectedJSRuntime::TraceJSHolders(JSTracer
* aTracer
,
1416 JSHolderMap::Iter
& aIter
,
1417 js::SliceBudget
& aBudget
) {
1418 bool checkSingleZoneHolders
= ShouldCheckSingleZoneHolders();
1420 while (!aIter
.Done() && !aBudget
.isOverBudget()) {
1421 void* holder
= aIter
->mHolder
;
1422 nsScriptObjectTracer
* tracer
= aIter
->mTracer
;
1424 #ifdef CHECK_SINGLE_ZONE_JS_HOLDERS
1425 if (checkSingleZoneHolders
&& tracer
->IsSingleZoneJSHolder()) {
1426 CheckHolderIsSingleZone(holder
, tracer
, aIter
.Zone());
1429 Unused
<< checkSingleZoneHolders
;
1432 tracer
->Trace(holder
, JsGcTracer(), aTracer
);
1438 return aIter
.Done();
1441 void CycleCollectedJSRuntime::AddJSHolder(void* aHolder
,
1442 nsScriptObjectTracer
* aTracer
,
1444 mJSHolders
.Put(aHolder
, aTracer
, aZone
);
1447 struct ClearJSHolder
: public TraceCallbacks
{
1448 virtual void Trace(JS::Heap
<JS::Value
>* aPtr
, const char*,
1449 void*) const override
{
1450 aPtr
->setUndefined();
1453 virtual void Trace(JS::Heap
<jsid
>* aPtr
, const char*, void*) const override
{
1454 *aPtr
= JS::PropertyKey::Void();
1457 virtual void Trace(JS::Heap
<JSObject
*>* aPtr
, const char*,
1458 void*) const override
{
1462 virtual void Trace(nsWrapperCache
* aPtr
, const char* aName
,
1463 void* aClosure
) const override
{
1464 aPtr
->ClearWrapper();
1467 virtual void Trace(JS::TenuredHeap
<JSObject
*>* aPtr
, const char*,
1468 void*) const override
{
1472 virtual void Trace(JS::Heap
<JSString
*>* aPtr
, const char*,
1473 void*) const override
{
1477 virtual void Trace(JS::Heap
<JSScript
*>* aPtr
, const char*,
1478 void*) const override
{
1482 virtual void Trace(JS::Heap
<JSFunction
*>* aPtr
, const char*,
1483 void*) const override
{
1488 void CycleCollectedJSRuntime::RemoveJSHolder(void* aHolder
) {
1489 nsScriptObjectTracer
* tracer
= mJSHolders
.Extract(aHolder
);
1491 // Bug 1531951: The analysis can't see through the virtual call but we know
1492 // that the ClearJSHolder tracer will never GC.
1493 JS::AutoSuppressGCAnalysis nogc
;
1494 tracer
->Trace(aHolder
, ClearJSHolder(), nullptr);
1499 static void AssertNoGcThing(JS::GCCellPtr aGCThing
, const char* aName
,
1501 MOZ_ASSERT(!aGCThing
);
1504 void CycleCollectedJSRuntime::AssertNoObjectsToTrace(void* aPossibleJSHolder
) {
1505 nsScriptObjectTracer
* tracer
= mJSHolders
.Get(aPossibleJSHolder
);
1507 tracer
->Trace(aPossibleJSHolder
, TraceCallbackFunc(AssertNoGcThing
),
1513 nsCycleCollectionParticipant
* CycleCollectedJSRuntime::GCThingParticipant() {
1514 return &mGCThingCycleCollectorGlobal
;
1517 nsCycleCollectionParticipant
* CycleCollectedJSRuntime::ZoneParticipant() {
1518 return &mJSZoneCycleCollectorGlobal
;
1521 nsresult
CycleCollectedJSRuntime::TraverseRoots(
1522 nsCycleCollectionNoteRootCallback
& aCb
) {
1523 TraverseNativeRoots(aCb
);
1525 NoteWeakMapsTracer
trc(mJSRuntime
, aCb
);
1526 js::TraceWeakMaps(&trc
);
1531 bool CycleCollectedJSRuntime::UsefulToMergeZones() const { return false; }
1533 void CycleCollectedJSRuntime::FixWeakMappingGrayBits() const {
1534 MOZ_ASSERT(!JS::IsIncrementalGCInProgress(mJSRuntime
),
1535 "Don't call FixWeakMappingGrayBits during a GC.");
1536 FixWeakMappingGrayBitsTracer
fixer(mJSRuntime
);
1540 void CycleCollectedJSRuntime::CheckGrayBits() const {
1541 MOZ_ASSERT(!JS::IsIncrementalGCInProgress(mJSRuntime
),
1542 "Don't call CheckGrayBits during a GC.");
1545 // Bug 1346874 - The gray state check is expensive. Android tests are already
1546 // slow enough that this check can easily push them over the threshold to a
1549 MOZ_ASSERT(js::CheckGrayMarkingState(mJSRuntime
));
1550 MOZ_ASSERT(CheckWeakMappingGrayBitsTracer::Check(mJSRuntime
));
1554 bool CycleCollectedJSRuntime::AreGCGrayBitsValid() const {
1555 return js::AreGCGrayBitsValid(mJSRuntime
);
1558 void CycleCollectedJSRuntime::GarbageCollect(JS::GCOptions aOptions
,
1559 JS::GCReason aReason
) const {
1560 JSContext
* cx
= CycleCollectedJSContext::Get()->Context();
1561 JS::PrepareForFullGC(cx
);
1562 JS::NonIncrementalGC(cx
, aOptions
, aReason
);
1565 void CycleCollectedJSRuntime::JSObjectsTenured() {
1566 JSContext
* cx
= CycleCollectedJSContext::Get()->Context();
1567 for (auto iter
= mNurseryObjects
.Iter(); !iter
.Done(); iter
.Next()) {
1568 nsWrapperCache
* cache
= iter
.Get();
1569 JSObject
* wrapper
= cache
->GetWrapperMaybeDead();
1570 MOZ_DIAGNOSTIC_ASSERT(wrapper
);
1571 if (!JS::ObjectIsTenured(wrapper
)) {
1572 MOZ_ASSERT(!cache
->PreservingWrapper());
1573 js::gc::FinalizeDeadNurseryObject(cx
, wrapper
);
1577 mNurseryObjects
.Clear();
1580 void CycleCollectedJSRuntime::NurseryWrapperAdded(nsWrapperCache
* aCache
) {
1582 MOZ_ASSERT(aCache
->GetWrapperMaybeDead());
1583 MOZ_ASSERT(!JS::ObjectIsTenured(aCache
->GetWrapperMaybeDead()));
1584 mNurseryObjects
.InfallibleAppend(aCache
);
1587 void CycleCollectedJSRuntime::DeferredFinalize(
1588 DeferredFinalizeAppendFunction aAppendFunc
, DeferredFinalizeFunction aFunc
,
1590 // Tell the analysis that the function pointers will not GC.
1591 JS::AutoSuppressGCAnalysis suppress
;
1592 mDeferredFinalizerTable
.WithEntryHandle(aFunc
, [&](auto&& entry
) {
1594 aAppendFunc(entry
.Data(), aThing
);
1596 entry
.Insert(aAppendFunc(nullptr, aThing
));
1601 void CycleCollectedJSRuntime::DeferredFinalize(nsISupports
* aSupports
) {
1602 typedef DeferredFinalizerImpl
<nsISupports
> Impl
;
1603 DeferredFinalize(Impl::AppendDeferredFinalizePointer
, Impl::DeferredFinalize
,
1607 void CycleCollectedJSRuntime::DumpJSHeap(FILE* aFile
) {
1608 JSContext
* cx
= CycleCollectedJSContext::Get()->Context();
1610 mozilla::MallocSizeOf mallocSizeOf
=
1611 PR_GetEnv("MOZ_GC_LOG_SIZE") ? moz_malloc_size_of
: nullptr;
1612 js::DumpHeap(cx
, aFile
, js::CollectNurseryBeforeDump
, mallocSizeOf
);
1615 IncrementalFinalizeRunnable::IncrementalFinalizeRunnable(
1616 CycleCollectedJSRuntime
* aRt
, DeferredFinalizerTable
& aFinalizers
)
1617 : DiscardableRunnable("IncrementalFinalizeRunnable"),
1619 mFinalizeFunctionToRun(0),
1621 for (auto iter
= aFinalizers
.Iter(); !iter
.Done(); iter
.Next()) {
1622 DeferredFinalizeFunction
& function
= iter
.Key();
1623 void*& data
= iter
.Data();
1625 DeferredFinalizeFunctionHolder
* holder
=
1626 mDeferredFinalizeFunctions
.AppendElement();
1627 holder
->run
= function
;
1628 holder
->data
= data
;
1632 MOZ_ASSERT(mDeferredFinalizeFunctions
.Length());
1635 IncrementalFinalizeRunnable::~IncrementalFinalizeRunnable() {
1636 MOZ_ASSERT(!mDeferredFinalizeFunctions
.Length());
1637 MOZ_ASSERT(!mRuntime
);
1640 void IncrementalFinalizeRunnable::ReleaseNow(bool aLimited
) {
1642 NS_WARNING("Re-entering ReleaseNow");
1646 AUTO_PROFILER_LABEL("IncrementalFinalizeRunnable::ReleaseNow",
1649 mozilla::AutoRestore
<bool> ar(mReleasing
);
1651 MOZ_ASSERT(mDeferredFinalizeFunctions
.Length() != 0,
1652 "We should have at least ReleaseSliceNow to run");
1653 MOZ_ASSERT(mFinalizeFunctionToRun
< mDeferredFinalizeFunctions
.Length(),
1654 "No more finalizers to run?");
1656 TimeDuration sliceTime
= TimeDuration::FromMilliseconds(SliceMillis
);
1657 TimeStamp started
= aLimited
? TimeStamp::Now() : TimeStamp();
1658 bool timeout
= false;
1660 const DeferredFinalizeFunctionHolder
& function
=
1661 mDeferredFinalizeFunctions
[mFinalizeFunctionToRun
];
1664 while (!timeout
&& !done
) {
1666 * We don't want to read the clock too often, so we try to
1667 * release slices of 100 items.
1669 done
= function
.run(100, function
.data
);
1670 timeout
= TimeStamp::Now() - started
>= sliceTime
;
1673 ++mFinalizeFunctionToRun
;
1679 while (!function
.run(UINT32_MAX
, function
.data
))
1681 ++mFinalizeFunctionToRun
;
1683 } while (mFinalizeFunctionToRun
< mDeferredFinalizeFunctions
.Length());
1686 if (mFinalizeFunctionToRun
== mDeferredFinalizeFunctions
.Length()) {
1687 MOZ_ASSERT(mRuntime
->mFinalizeRunnable
== this);
1688 mDeferredFinalizeFunctions
.Clear();
1689 CycleCollectedJSRuntime
* runtime
= mRuntime
;
1691 // NB: This may delete this!
1692 runtime
->mFinalizeRunnable
= nullptr;
1697 IncrementalFinalizeRunnable::Run() {
1698 if (!mDeferredFinalizeFunctions
.Length()) {
1699 /* These items were already processed synchronously in JSGC_END. */
1700 MOZ_ASSERT(!mRuntime
);
1704 MOZ_ASSERT(mRuntime
->mFinalizeRunnable
== this);
1705 TimeStamp start
= TimeStamp::Now();
1708 if (mDeferredFinalizeFunctions
.Length()) {
1709 nsresult rv
= NS_DispatchToCurrentThread(this);
1710 if (NS_FAILED(rv
)) {
1714 MOZ_ASSERT(!mRuntime
);
1717 uint32_t duration
= (uint32_t)((TimeStamp::Now() - start
).ToMilliseconds());
1718 Telemetry::Accumulate(Telemetry::DEFERRED_FINALIZE_ASYNC
, duration
);
1723 void CycleCollectedJSRuntime::FinalizeDeferredThings(
1724 DeferredFinalizeType aType
) {
1725 // If mFinalizeRunnable isn't null, we didn't finalize everything from the
1727 if (mFinalizeRunnable
) {
1728 if (aType
== FinalizeLater
) {
1729 // We need to defer all finalization until we return to the event loop,
1730 // so leave things alone. Any new objects to be finalized from the current
1731 // GC will be handled by the existing mFinalizeRunnable.
1734 MOZ_ASSERT(aType
== FinalizeIncrementally
|| aType
== FinalizeNow
);
1735 // If we're finalizing incrementally, we don't want finalizers to build up,
1736 // so try to finish them off now.
1737 // If we're finalizing synchronously, also go ahead and clear them out,
1738 // so we make sure as much as possible is freed.
1739 mFinalizeRunnable
->ReleaseNow(false);
1740 if (mFinalizeRunnable
) {
1741 // If we re-entered ReleaseNow, we couldn't delete mFinalizeRunnable and
1742 // we need to just continue processing it.
1747 // If there's nothing to finalize, don't create a new runnable.
1748 if (mDeferredFinalizerTable
.Count() == 0) {
1753 new IncrementalFinalizeRunnable(this, mDeferredFinalizerTable
);
1755 // Everything should be gone now.
1756 MOZ_ASSERT(mDeferredFinalizerTable
.Count() == 0);
1758 if (aType
== FinalizeNow
) {
1759 mFinalizeRunnable
->ReleaseNow(false);
1760 MOZ_ASSERT(!mFinalizeRunnable
);
1762 MOZ_ASSERT(aType
== FinalizeIncrementally
|| aType
== FinalizeLater
);
1763 NS_DispatchToCurrentThreadQueue(do_AddRef(mFinalizeRunnable
), 2500,
1764 EventQueuePriority::Idle
);
1768 const char* CycleCollectedJSRuntime::OOMStateToString(
1769 const OOMState aOomState
) const {
1770 switch (aOomState
) {
1773 case OOMState::Reporting
:
1775 case OOMState::Reported
:
1777 case OOMState::Recovered
:
1780 MOZ_ASSERT_UNREACHABLE("OOMState holds an invalid value");
1785 bool CycleCollectedJSRuntime::OOMReported() {
1786 return mOutOfMemoryState
== OOMState::Reported
;
1789 void CycleCollectedJSRuntime::AnnotateAndSetOutOfMemory(OOMState
* aStatePtr
,
1790 OOMState aNewState
) {
1791 *aStatePtr
= aNewState
;
1792 CrashReporter::Annotation annotation
=
1793 (aStatePtr
== &mOutOfMemoryState
)
1794 ? CrashReporter::Annotation::JSOutOfMemory
1795 : CrashReporter::Annotation::JSLargeAllocationFailure
;
1797 CrashReporter::AnnotateCrashReport(
1798 annotation
, nsDependentCString(OOMStateToString(aNewState
)));
1801 void CycleCollectedJSRuntime::OnGC(JSContext
* aContext
, JSGCStatus aStatus
,
1802 JS::GCReason aReason
) {
1805 MOZ_RELEASE_ASSERT(mHolderIter
.isNothing());
1806 nsCycleCollector_prepareForGarbageCollection();
1807 PrepareWaitingZonesForGC();
1810 MOZ_RELEASE_ASSERT(mHolderIter
.isNothing());
1811 if (mOutOfMemoryState
== OOMState::Reported
) {
1812 AnnotateAndSetOutOfMemory(&mOutOfMemoryState
, OOMState::Recovered
);
1814 if (mLargeAllocationFailureState
== OOMState::Reported
) {
1815 AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState
,
1816 OOMState::Recovered
);
1819 DeferredFinalizeType finalizeType
;
1820 if (JS_IsExceptionPending(aContext
)) {
1821 // There is a pending exception. The finalizers are not set up to run
1822 // in that state, so don't run the finalizer until we've returned to the
1824 finalizeType
= FinalizeLater
;
1825 } else if (JS::InternalGCReason(aReason
)) {
1826 if (aReason
== JS::GCReason::DESTROY_RUNTIME
) {
1827 // We're shutting down, so we need to destroy things immediately.
1828 finalizeType
= FinalizeNow
;
1830 // We may be in the middle of running some code that the JIT has
1831 // assumed can't have certain kinds of side effects. Finalizers can do
1832 // all sorts of things, such as run JS, so we want to run them later,
1833 // after we've returned to the event loop.
1834 finalizeType
= FinalizeLater
;
1836 } else if (JS::WasIncrementalGC(mJSRuntime
)) {
1837 // The GC was incremental, so we probably care about pauses. Try to
1838 // break up finalization, but it is okay if we do some now.
1839 finalizeType
= FinalizeIncrementally
;
1841 // If we're running a synchronous GC, we probably want to free things as
1842 // quickly as possible. This can happen during testing or if memory is
1844 finalizeType
= FinalizeNow
;
1846 FinalizeDeferredThings(finalizeType
);
1854 CustomGCCallback(aStatus
);
1857 void CycleCollectedJSRuntime::OnOutOfMemory() {
1858 AnnotateAndSetOutOfMemory(&mOutOfMemoryState
, OOMState::Reporting
);
1859 CustomOutOfMemoryCallback();
1860 AnnotateAndSetOutOfMemory(&mOutOfMemoryState
, OOMState::Reported
);
1863 void CycleCollectedJSRuntime::SetLargeAllocationFailure(OOMState aNewState
) {
1864 AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState
, aNewState
);
1867 void CycleCollectedJSRuntime::PrepareWaitingZonesForGC() {
1868 JSContext
* cx
= CycleCollectedJSContext::Get()->Context();
1869 if (mZonesWaitingForGC
.Count() == 0) {
1870 JS::PrepareForFullGC(cx
);
1872 for (const auto& key
: mZonesWaitingForGC
) {
1873 JS::PrepareZoneForGC(cx
, key
);
1875 mZonesWaitingForGC
.Clear();
1880 void CycleCollectedJSRuntime::OnZoneDestroyed(JS::GCContext
* aGcx
,
1882 // Remove the zone from the set of zones waiting for GC, if present. This can
1883 // happen if a zone is added to the set during an incremental GC in which it
1884 // is later destroyed.
1885 CycleCollectedJSRuntime
* runtime
= Get();
1886 runtime
->mZonesWaitingForGC
.Remove(aZone
);
1889 void CycleCollectedJSRuntime::EnvironmentPreparer::invoke(
1890 JS::HandleObject global
, js::ScriptEnvironmentPreparer::Closure
& closure
) {
1891 MOZ_ASSERT(JS_IsGlobalObject(global
));
1892 nsIGlobalObject
* nativeGlobal
= xpc::NativeGlobal(global
);
1894 // Not much we can do if we simply don't have a usable global here...
1895 NS_ENSURE_TRUE_VOID(nativeGlobal
&& nativeGlobal
->HasJSGlobal());
1897 AutoEntryScript
aes(nativeGlobal
, "JS-engine-initiated execution");
1899 MOZ_ASSERT(!JS_IsExceptionPending(aes
.cx()));
1901 DebugOnly
<bool> ok
= closure(aes
.cx());
1903 MOZ_ASSERT_IF(ok
, !JS_IsExceptionPending(aes
.cx()));
1905 // The AutoEntryScript will check for JS_IsExceptionPending on the
1906 // JSContext and report it as needed as it comes off the stack.
1910 CycleCollectedJSRuntime
* CycleCollectedJSRuntime::Get() {
1911 auto context
= CycleCollectedJSContext::Get();
1913 return context
->Runtime();
1918 #ifdef MOZ_JS_DEV_ERROR_INTERCEPTOR
1921 extern void DumpValue(const JS::Value
& val
);
1924 void CycleCollectedJSRuntime::ErrorInterceptor::Shutdown(JSRuntime
* rt
) {
1925 JS_SetErrorInterceptorCallback(rt
, nullptr);
1926 mThrownError
.reset();
1930 void CycleCollectedJSRuntime::ErrorInterceptor::interceptError(
1931 JSContext
* cx
, JS::HandleValue exn
) {
1933 // We already have an error, we don't need anything more.
1937 if (!nsContentUtils::ThreadsafeIsSystemCaller(cx
)) {
1938 // We are only interested in chrome code.
1942 const auto type
= JS_GetErrorType(exn
);
1944 // This is not one of the primitive error types.
1949 case JSExnType::JSEXN_REFERENCEERR
:
1950 case JSExnType::JSEXN_SYNTAXERR
:
1953 // Not one of the errors we are interested in.
1954 // Note that we are not interested in instances of `TypeError`
1955 // for the time being, as DOM (ab)uses this constructor to represent
1956 // all sorts of errors that are not even remotely related to type
1957 // errors (e.g. some network errors).
1958 // If we ever have a mechanism to differentiate between DOM-thrown
1959 // and SpiderMonkey-thrown instances of `TypeError`, we should
1960 // consider watching for `TypeError` here.
1964 // Now copy the details of the exception locally.
1965 // While copying the details of an exception could be expensive, in most runs,
1966 // this will be done at most once during the execution of the process, so the
1967 // total cost should be reasonable.
1969 ErrorDetails details
;
1970 details
.mType
= *type
;
1971 // If `exn` isn't an exception object, `ExtractErrorValues` could end up
1972 // calling `toString()`, which could in turn end up throwing an error. While
1973 // this should work, we want to avoid that complex use case. Fortunately, we
1974 // have already checked above that `exn` is an exception object, so nothing
1975 // such should happen.
1976 nsContentUtils::ExtractErrorValues(cx
, exn
, details
.mFilename
, &details
.mLine
,
1977 &details
.mColumn
, details
.mMessage
);
1979 JS::UniqueChars buf
=
1980 JS::FormatStackDump(cx
, /* showArgs = */ false, /* showLocals = */ false,
1981 /* showThisProps = */ false);
1982 CopyUTF8toUTF16(mozilla::MakeStringSpan(buf
.get()), details
.mStack
);
1984 mThrownError
.emplace(std::move(details
));
1987 void CycleCollectedJSRuntime::ClearRecentDevError() {
1988 mErrorInterceptor
.mThrownError
.reset();
1991 bool CycleCollectedJSRuntime::GetRecentDevError(
1992 JSContext
* cx
, JS::MutableHandle
<JS::Value
> error
) {
1993 if (!mErrorInterceptor
.mThrownError
) {
1997 // Create a copy of the exception.
1998 JS::RootedObject
obj(cx
, JS_NewPlainObject(cx
));
2003 JS::RootedValue
message(cx
);
2004 JS::RootedValue
filename(cx
);
2005 JS::RootedValue
stack(cx
);
2006 if (!ToJSValue(cx
, mErrorInterceptor
.mThrownError
->mMessage
, &message
) ||
2007 !ToJSValue(cx
, mErrorInterceptor
.mThrownError
->mFilename
, &filename
) ||
2008 !ToJSValue(cx
, mErrorInterceptor
.mThrownError
->mStack
, &stack
)) {
2012 // Build the object.
2013 const auto FLAGS
= JSPROP_READONLY
| JSPROP_ENUMERATE
| JSPROP_PERMANENT
;
2014 if (!JS_DefineProperty(cx
, obj
, "message", message
, FLAGS
) ||
2015 !JS_DefineProperty(cx
, obj
, "fileName", filename
, FLAGS
) ||
2016 !JS_DefineProperty(cx
, obj
, "lineNumber",
2017 mErrorInterceptor
.mThrownError
->mLine
, FLAGS
) ||
2018 !JS_DefineProperty(cx
, obj
, "stack", stack
, FLAGS
)) {
2023 error
.setObject(*obj
);
2026 #endif // MOZ_JS_DEV_ERROR_INTERCEPTOR
2028 #undef MOZ_JS_DEV_ERROR_INTERCEPTOR