LCOV - code coverage report
Current view: top level - xpcom/base - CycleCollectedJSRuntime.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 167 644 25.9 %
Date: 2017-07-14 16:53:18 Functions: 30 101 29.7 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : // We're dividing JS objects into 3 categories:
       8             : //
       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.
      12             : //
      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.
      17             : //
      18             : // 3. all other roots held by C++ objects that participate in cycle
      19             : //    collection, held by us (see TraceNativeGrayRoots). Roots from this
      20             : //    category are considered grey in the cycle collector; whether or not
      21             : //    they are collected depends on the objects that hold them.
      22             : //
      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.
      25             : //
      26             : // During garbage collection we switch to an additional mark color (gray)
      27             : // when tracing inside TraceNativeGrayRoots. This allows us to walk those
      28             : // roots later on and add all objects reachable only from them to the
      29             : // cycle collector.
      30             : //
      31             : // Phases:
      32             : //
      33             : // 1. marking of the roots in category 1 by having the JS GC do its marking
      34             : // 2. marking of the roots in category 2 by having the JS GC call us back
      35             : //    (via JS_SetExtraGCRootsTracer) and running TraceNativeBlackRoots
      36             : // 3. marking of the roots in category 3 by TraceNativeGrayRoots using an
      37             : //    additional color (gray).
      38             : // 4. end of GC, GC can sweep its heap
      39             : //
      40             : // At some later point, when the cycle collector runs:
      41             : //
      42             : // 5. walk gray objects and add them to the cycle collector, cycle collect
      43             : //
      44             : // JS objects that are part of cycles the cycle collector breaks will be
      45             : // collected by the next JS GC.
      46             : //
      47             : // If WantAllTraces() is false the cycle collector will not traverse roots
      48             : // from category 1 or any JS objects held by them. Any JS objects they hold
      49             : // will already be marked by the JS GC and will thus be colored black
      50             : // themselves. Any C++ objects they hold will have a missing (untraversed)
      51             : // edge from the JS object to the C++ object and so it will be marked black
      52             : // too. This decreases the number of objects that the cycle collector has to
      53             : // deal with.
      54             : // To improve debugging, if WantAllTraces() is true all JS objects are
      55             : // traversed.
      56             : 
      57             : #include "mozilla/CycleCollectedJSRuntime.h"
      58             : #include <algorithm>
      59             : #include "mozilla/ArrayUtils.h"
      60             : #include "mozilla/AutoRestore.h"
      61             : #include "mozilla/CycleCollectedJSContext.h"
      62             : #include "mozilla/Move.h"
      63             : #include "mozilla/MemoryReporting.h"
      64             : #include "mozilla/Sprintf.h"
      65             : #include "mozilla/Telemetry.h"
      66             : #include "mozilla/TimelineConsumers.h"
      67             : #include "mozilla/TimelineMarker.h"
      68             : #include "mozilla/Unused.h"
      69             : #include "mozilla/DebuggerOnGCRunnable.h"
      70             : #include "mozilla/dom/DOMJSClass.h"
      71             : #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
      72             : #include "mozilla/dom/Promise.h"
      73             : #include "mozilla/dom/PromiseBinding.h"
      74             : #include "mozilla/dom/PromiseDebugging.h"
      75             : #include "mozilla/dom/ScriptSettings.h"
      76             : #include "jsprf.h"
      77             : #include "js/Debug.h"
      78             : #include "js/GCAPI.h"
      79             : #include "nsContentUtils.h"
      80             : #include "nsCycleCollectionNoteRootCallback.h"
      81             : #include "nsCycleCollectionParticipant.h"
      82             : #include "nsCycleCollector.h"
      83             : #include "nsDOMJSUtils.h"
      84             : #include "nsJSUtils.h"
      85             : #include "nsWrapperCache.h"
      86             : #include "nsStringBuffer.h"
      87             : #include "GeckoProfiler.h"
      88             : #include "ProfilerMarkerPayload.h"
      89             : 
      90             : #ifdef MOZ_CRASHREPORTER
      91             : #include "nsExceptionHandler.h"
      92             : #endif
      93             : 
      94             : #include "nsIException.h"
      95             : #include "nsIPlatformInfo.h"
      96             : #include "nsThread.h"
      97             : #include "nsThreadUtils.h"
      98             : #include "xpcpublic.h"
      99             : 
     100             : using namespace mozilla;
     101             : using namespace mozilla::dom;
     102             : 
     103             : namespace mozilla {
     104             : 
     105             : struct DeferredFinalizeFunctionHolder
     106             : {
     107             :   DeferredFinalizeFunction run;
     108             :   void* data;
     109             : };
     110             : 
     111             : class IncrementalFinalizeRunnable : public Runnable
     112             : {
     113             :   typedef AutoTArray<DeferredFinalizeFunctionHolder, 16> DeferredFinalizeArray;
     114             :   typedef CycleCollectedJSRuntime::DeferredFinalizerTable DeferredFinalizerTable;
     115             : 
     116             :   CycleCollectedJSRuntime* mRuntime;
     117             :   DeferredFinalizeArray mDeferredFinalizeFunctions;
     118             :   uint32_t mFinalizeFunctionToRun;
     119             :   bool mReleasing;
     120             : 
     121             :   static const PRTime SliceMillis = 5; /* ms */
     122             : 
     123             : public:
     124             :   IncrementalFinalizeRunnable(CycleCollectedJSRuntime* aRt,
     125             :                               DeferredFinalizerTable& aFinalizerTable);
     126             :   virtual ~IncrementalFinalizeRunnable();
     127             : 
     128             :   void ReleaseNow(bool aLimited);
     129             : 
     130             :   NS_DECL_NSIRUNNABLE
     131             : };
     132             : 
     133             : } // namespace mozilla
     134             : 
     135             : struct NoteWeakMapChildrenTracer : public JS::CallbackTracer
     136             : {
     137           0 :   NoteWeakMapChildrenTracer(JSRuntime* aRt,
     138             :                             nsCycleCollectionNoteRootCallback& aCb)
     139           0 :     : JS::CallbackTracer(aRt), mCb(aCb), mTracedAny(false), mMap(nullptr),
     140           0 :       mKey(nullptr), mKeyDelegate(nullptr)
     141             :   {
     142           0 :   }
     143             :   void onChild(const JS::GCCellPtr& aThing) override;
     144             :   nsCycleCollectionNoteRootCallback& mCb;
     145             :   bool mTracedAny;
     146             :   JSObject* mMap;
     147             :   JS::GCCellPtr mKey;
     148             :   JSObject* mKeyDelegate;
     149             : };
     150             : 
     151             : void
     152           0 : NoteWeakMapChildrenTracer::onChild(const JS::GCCellPtr& aThing)
     153             : {
     154           0 :   if (aThing.is<JSString>()) {
     155           0 :     return;
     156             :   }
     157             : 
     158           0 :   if (!JS::GCThingIsMarkedGray(aThing) && !mCb.WantAllTraces()) {
     159           0 :     return;
     160             :   }
     161             : 
     162           0 :   if (AddToCCKind(aThing.kind())) {
     163           0 :     mCb.NoteWeakMapping(mMap, mKey, mKeyDelegate, aThing);
     164           0 :     mTracedAny = true;
     165             :   } else {
     166           0 :     JS::TraceChildren(this, aThing);
     167             :   }
     168             : }
     169             : 
     170             : struct NoteWeakMapsTracer : public js::WeakMapTracer
     171             : {
     172           0 :   NoteWeakMapsTracer(JSRuntime* aRt, nsCycleCollectionNoteRootCallback& aCccb)
     173           0 :     : js::WeakMapTracer(aRt), mCb(aCccb), mChildTracer(aRt, aCccb)
     174             :   {
     175           0 :   }
     176             :   void trace(JSObject* aMap, JS::GCCellPtr aKey, JS::GCCellPtr aValue) override;
     177             :   nsCycleCollectionNoteRootCallback& mCb;
     178             :   NoteWeakMapChildrenTracer mChildTracer;
     179             : };
     180             : 
     181             : void
     182           0 : NoteWeakMapsTracer::trace(JSObject* aMap, JS::GCCellPtr aKey,
     183             :                           JS::GCCellPtr aValue)
     184             : {
     185             :   // If nothing that could be held alive by this entry is marked gray, return.
     186           0 :   if ((!aKey || !JS::GCThingIsMarkedGray(aKey)) &&
     187           0 :       MOZ_LIKELY(!mCb.WantAllTraces())) {
     188           0 :     if (!aValue || !JS::GCThingIsMarkedGray(aValue) || aValue.is<JSString>()) {
     189           0 :       return;
     190             :     }
     191             :   }
     192             : 
     193             :   // The cycle collector can only properly reason about weak maps if it can
     194             :   // reason about the liveness of their keys, which in turn requires that
     195             :   // the key can be represented in the cycle collector graph.  All existing
     196             :   // uses of weak maps use either objects or scripts as keys, which are okay.
     197           0 :   MOZ_ASSERT(AddToCCKind(aKey.kind()));
     198             : 
     199             :   // As an emergency fallback for non-debug builds, if the key is not
     200             :   // representable in the cycle collector graph, we treat it as marked.  This
     201             :   // can cause leaks, but is preferable to ignoring the binding, which could
     202             :   // cause the cycle collector to free live objects.
     203           0 :   if (!AddToCCKind(aKey.kind())) {
     204           0 :     aKey = nullptr;
     205             :   }
     206             : 
     207           0 :   JSObject* kdelegate = nullptr;
     208           0 :   if (aKey.is<JSObject>()) {
     209           0 :     kdelegate = js::GetWeakmapKeyDelegate(&aKey.as<JSObject>());
     210             :   }
     211             : 
     212           0 :   if (AddToCCKind(aValue.kind())) {
     213           0 :     mCb.NoteWeakMapping(aMap, aKey, kdelegate, aValue);
     214             :   } else {
     215           0 :     mChildTracer.mTracedAny = false;
     216           0 :     mChildTracer.mMap = aMap;
     217           0 :     mChildTracer.mKey = aKey;
     218           0 :     mChildTracer.mKeyDelegate = kdelegate;
     219             : 
     220           0 :     if (!aValue.is<JSString>()) {
     221           0 :       JS::TraceChildren(&mChildTracer, aValue);
     222             :     }
     223             : 
     224             :     // The delegate could hold alive the key, so report something to the CC
     225             :     // if we haven't already.
     226           0 :     if (!mChildTracer.mTracedAny &&
     227           0 :         aKey && JS::GCThingIsMarkedGray(aKey) && kdelegate) {
     228           0 :       mCb.NoteWeakMapping(aMap, aKey, kdelegate, nullptr);
     229             :     }
     230             :   }
     231             : }
     232             : 
     233             : // Report whether the key or value of a weak mapping entry are gray but need to
     234             : // be marked black.
     235             : static void
     236           0 : ShouldWeakMappingEntryBeBlack(JSObject* aMap, JS::GCCellPtr aKey, JS::GCCellPtr aValue,
     237             :                               bool* aKeyShouldBeBlack, bool* aValueShouldBeBlack)
     238             : {
     239           0 :   *aKeyShouldBeBlack = false;
     240           0 :   *aValueShouldBeBlack = false;
     241             : 
     242             :   // If nothing that could be held alive by this entry is marked gray, return.
     243           0 :   bool keyMightNeedMarking = aKey && JS::GCThingIsMarkedGray(aKey);
     244           0 :   bool valueMightNeedMarking = aValue && JS::GCThingIsMarkedGray(aValue) &&
     245           0 :     aValue.kind() != JS::TraceKind::String;
     246           0 :   if (!keyMightNeedMarking && !valueMightNeedMarking) {
     247           0 :     return;
     248             :   }
     249             : 
     250           0 :   if (!AddToCCKind(aKey.kind())) {
     251           0 :     aKey = nullptr;
     252             :   }
     253             : 
     254           0 :   if (keyMightNeedMarking && aKey.is<JSObject>()) {
     255           0 :     JSObject* kdelegate = js::GetWeakmapKeyDelegate(&aKey.as<JSObject>());
     256           0 :     if (kdelegate && !JS::ObjectIsMarkedGray(kdelegate) &&
     257           0 :         (!aMap || !JS::ObjectIsMarkedGray(aMap)))
     258             :     {
     259           0 :       *aKeyShouldBeBlack = true;
     260             :     }
     261             :   }
     262             : 
     263           0 :   if (aValue && JS::GCThingIsMarkedGray(aValue) &&
     264           0 :       (!aKey || !JS::GCThingIsMarkedGray(aKey)) &&
     265           0 :       (!aMap || !JS::ObjectIsMarkedGray(aMap)) &&
     266           0 :       aValue.kind() != JS::TraceKind::Shape) {
     267           0 :     *aValueShouldBeBlack = true;
     268             :   }
     269             : }
     270             : 
     271             : struct FixWeakMappingGrayBitsTracer : public js::WeakMapTracer
     272             : {
     273           0 :   explicit FixWeakMappingGrayBitsTracer(JSRuntime* aRt)
     274           0 :     : js::WeakMapTracer(aRt)
     275             :   {
     276           0 :   }
     277             : 
     278             :   void
     279           0 :   FixAll()
     280             :   {
     281           0 :     do {
     282           0 :       mAnyMarked = false;
     283           0 :       js::TraceWeakMaps(this);
     284           0 :     } while (mAnyMarked);
     285           0 :   }
     286             : 
     287           0 :   void trace(JSObject* aMap, JS::GCCellPtr aKey, JS::GCCellPtr aValue) override
     288             :   {
     289             :     bool keyShouldBeBlack;
     290             :     bool valueShouldBeBlack;
     291             :     ShouldWeakMappingEntryBeBlack(aMap, aKey, aValue,
     292           0 :                                   &keyShouldBeBlack, &valueShouldBeBlack);
     293           0 :     if (keyShouldBeBlack && JS::UnmarkGrayGCThingRecursively(aKey)) {
     294           0 :       mAnyMarked = true;
     295             :     }
     296             : 
     297           0 :     if (valueShouldBeBlack && JS::UnmarkGrayGCThingRecursively(aValue)) {
     298           0 :       mAnyMarked = true;
     299             :     }
     300           0 :   }
     301             : 
     302             :   MOZ_INIT_OUTSIDE_CTOR bool mAnyMarked;
     303             : };
     304             : 
     305             : #ifdef DEBUG
     306             : // Check whether weak maps are marked correctly according to the logic above.
     307             : struct CheckWeakMappingGrayBitsTracer : public js::WeakMapTracer
     308             : {
     309           0 :   explicit CheckWeakMappingGrayBitsTracer(JSRuntime* aRt)
     310           0 :     : js::WeakMapTracer(aRt), mFailed(false)
     311             :   {
     312           0 :   }
     313             : 
     314             :   static bool
     315           0 :   Check(JSRuntime* aRt)
     316             :   {
     317           0 :     CheckWeakMappingGrayBitsTracer tracer(aRt);
     318           0 :     js::TraceWeakMaps(&tracer);
     319           0 :     return !tracer.mFailed;
     320             :   }
     321             : 
     322           0 :   void trace(JSObject* aMap, JS::GCCellPtr aKey, JS::GCCellPtr aValue) override
     323             :   {
     324             :     bool keyShouldBeBlack;
     325             :     bool valueShouldBeBlack;
     326             :     ShouldWeakMappingEntryBeBlack(aMap, aKey, aValue,
     327           0 :                                   &keyShouldBeBlack, &valueShouldBeBlack);
     328             : 
     329           0 :     if (keyShouldBeBlack) {
     330           0 :       fprintf(stderr, "Weak mapping key %p of map %p should be black\n",
     331           0 :               aKey.asCell(), aMap);
     332           0 :       mFailed = true;
     333             :     }
     334             : 
     335           0 :     if (valueShouldBeBlack) {
     336           0 :       fprintf(stderr, "Weak mapping value %p of map %p should be black\n",
     337           0 :               aValue.asCell(), aMap);
     338           0 :       mFailed = true;
     339             :     }
     340           0 :   }
     341             : 
     342             :   bool mFailed;
     343             : };
     344             : #endif // DEBUG
     345             : 
     346             : static void
     347           0 : CheckParticipatesInCycleCollection(JS::GCCellPtr aThing, const char* aName,
     348             :                                    void* aClosure)
     349             : {
     350           0 :   bool* cycleCollectionEnabled = static_cast<bool*>(aClosure);
     351             : 
     352           0 :   if (*cycleCollectionEnabled) {
     353           0 :     return;
     354             :   }
     355             : 
     356           0 :   if (AddToCCKind(aThing.kind()) && JS::GCThingIsMarkedGray(aThing)) {
     357           0 :     *cycleCollectionEnabled = true;
     358             :   }
     359             : }
     360             : 
     361             : NS_IMETHODIMP
     362           0 : JSGCThingParticipant::TraverseNative(void* aPtr,
     363             :                                      nsCycleCollectionTraversalCallback& aCb)
     364             : {
     365             :   auto runtime = reinterpret_cast<CycleCollectedJSRuntime*>(
     366             :     reinterpret_cast<char*>(this) - offsetof(CycleCollectedJSRuntime,
     367           0 :                                              mGCThingCycleCollectorGlobal));
     368             : 
     369           0 :   JS::GCCellPtr cellPtr(aPtr, JS::GCThingTraceKind(aPtr));
     370           0 :   runtime->TraverseGCThing(CycleCollectedJSRuntime::TRAVERSE_FULL, cellPtr, aCb);
     371           0 :   return NS_OK;
     372             : }
     373             : 
     374             : // NB: This is only used to initialize the participant in
     375             : // CycleCollectedJSRuntime. It should never be used directly.
     376             : static JSGCThingParticipant sGCThingCycleCollectorGlobal;
     377             : 
     378             : NS_IMETHODIMP
     379           0 : JSZoneParticipant::TraverseNative(void* aPtr,
     380             :                                   nsCycleCollectionTraversalCallback& aCb)
     381             : {
     382             :   auto runtime = reinterpret_cast<CycleCollectedJSRuntime*>(
     383             :     reinterpret_cast<char*>(this) - offsetof(CycleCollectedJSRuntime,
     384           0 :                                              mJSZoneCycleCollectorGlobal));
     385             : 
     386           0 :   MOZ_ASSERT(!aCb.WantAllTraces());
     387           0 :   JS::Zone* zone = static_cast<JS::Zone*>(aPtr);
     388             : 
     389           0 :   runtime->TraverseZone(zone, aCb);
     390           0 :   return NS_OK;
     391             : }
     392             : 
     393             : struct TraversalTracer : public JS::CallbackTracer
     394             : {
     395           0 :   TraversalTracer(JSRuntime* aRt, nsCycleCollectionTraversalCallback& aCb)
     396           0 :     : JS::CallbackTracer(aRt, DoNotTraceWeakMaps), mCb(aCb)
     397             :   {
     398           0 :   }
     399             :   void onChild(const JS::GCCellPtr& aThing) override;
     400             :   nsCycleCollectionTraversalCallback& mCb;
     401             : };
     402             : 
     403             : void
     404           0 : TraversalTracer::onChild(const JS::GCCellPtr& aThing)
     405             : {
     406             :   // Don't traverse non-gray objects, unless we want all traces.
     407           0 :   if (!JS::GCThingIsMarkedGray(aThing) && !mCb.WantAllTraces()) {
     408           0 :     return;
     409             :   }
     410             : 
     411             :   /*
     412             :    * This function needs to be careful to avoid stack overflow. Normally, when
     413             :    * AddToCCKind is true, the recursion terminates immediately as we just add
     414             :    * |thing| to the CC graph. So overflow is only possible when there are long
     415             :    * or cyclic chains of non-AddToCCKind GC things. Places where this can occur
     416             :    * use special APIs to handle such chains iteratively.
     417             :    */
     418           0 :   if (AddToCCKind(aThing.kind())) {
     419           0 :     if (MOZ_UNLIKELY(mCb.WantDebugInfo())) {
     420             :       char buffer[200];
     421           0 :       getTracingEdgeName(buffer, sizeof(buffer));
     422           0 :       mCb.NoteNextEdgeName(buffer);
     423             :     }
     424           0 :     mCb.NoteJSChild(aThing);
     425           0 :   } else if (aThing.is<js::Shape>()) {
     426             :     // The maximum depth of traversal when tracing a Shape is unbounded, due to
     427             :     // the parent pointers on the shape.
     428           0 :     JS_TraceShapeCycleCollectorChildren(this, aThing);
     429           0 :   } else if (aThing.is<js::ObjectGroup>()) {
     430             :     // The maximum depth of traversal when tracing an ObjectGroup is unbounded,
     431             :     // due to information attached to the groups which can lead other groups to
     432             :     // be traced.
     433           0 :     JS_TraceObjectGroupCycleCollectorChildren(this, aThing);
     434           0 :   } else if (!aThing.is<JSString>()) {
     435           0 :     JS::TraceChildren(this, aThing);
     436             :   }
     437             : }
     438             : 
     439             : static void
     440           0 : NoteJSChildGrayWrapperShim(void* aData, JS::GCCellPtr aThing)
     441             : {
     442           0 :   TraversalTracer* trc = static_cast<TraversalTracer*>(aData);
     443           0 :   trc->onChild(aThing);
     444           0 : }
     445             : 
     446             : /*
     447             :  * The cycle collection participant for a Zone is intended to produce the same
     448             :  * results as if all of the gray GCthings in a zone were merged into a single node,
     449             :  * except for self-edges. This avoids the overhead of representing all of the GCthings in
     450             :  * the zone in the cycle collector graph, which should be much faster if many of
     451             :  * the GCthings in the zone are gray.
     452             :  *
     453             :  * Zone merging should not always be used, because it is a conservative
     454             :  * approximation of the true cycle collector graph that can incorrectly identify some
     455             :  * garbage objects as being live. For instance, consider two cycles that pass through a
     456             :  * zone, where one is garbage and the other is live. If we merge the entire
     457             :  * zone, the cycle collector will think that both are alive.
     458             :  *
     459             :  * We don't have to worry about losing track of a garbage cycle, because any such garbage
     460             :  * cycle incorrectly identified as live must contain at least one C++ to JS edge, and
     461             :  * XPConnect will always add the C++ object to the CC graph. (This is in contrast to pure
     462             :  * C++ garbage cycles, which must always be properly identified, because we clear the
     463             :  * purple buffer during every CC, which may contain the last reference to a garbage
     464             :  * cycle.)
     465             :  */
     466             : 
     467             : // NB: This is only used to initialize the participant in
     468             : // CycleCollectedJSRuntime. It should never be used directly.
     469             : static const JSZoneParticipant sJSZoneCycleCollectorGlobal;
     470             : 
     471             : static
     472          21 : void JSObjectsTenuredCb(JSContext* aContext, void* aData)
     473             : {
     474          21 :   static_cast<CycleCollectedJSRuntime*>(aData)->JSObjectsTenured();
     475          21 : }
     476             : 
     477             : bool
     478        1445 : mozilla::GetBuildId(JS::BuildIdCharVector* aBuildID)
     479             : {
     480        2890 :   nsCOMPtr<nsIPlatformInfo> info = do_GetService("@mozilla.org/xre/app-info;1");
     481        1445 :   if (!info) {
     482           0 :     return false;
     483             :   }
     484             : 
     485        2890 :   nsCString buildID;
     486        1445 :   nsresult rv = info->GetPlatformBuildID(buildID);
     487        1445 :   NS_ENSURE_SUCCESS(rv, false);
     488             : 
     489        1445 :   if (!aBuildID->resize(buildID.Length())) {
     490           0 :     return false;
     491             :   }
     492             : 
     493       21675 :   for (size_t i = 0; i < buildID.Length(); i++) {
     494       20230 :     (*aBuildID)[i] = buildID[i];
     495             :   }
     496             : 
     497        1445 :   return true;
     498             : }
     499             : 
     500             : static void
     501           0 : MozCrashWarningReporter(JSContext*, JSErrorReport*)
     502             : {
     503           0 :   MOZ_CRASH("Why is someone touching JSAPI without an AutoJSAPI?");
     504             : }
     505             : 
     506           4 : CycleCollectedJSRuntime::CycleCollectedJSRuntime(JSContext* aCx)
     507             :   : mGCThingCycleCollectorGlobal(sGCThingCycleCollectorGlobal)
     508             :   , mJSZoneCycleCollectorGlobal(sJSZoneCycleCollectorGlobal)
     509           4 :   , mJSRuntime(JS_GetRuntime(aCx))
     510             :   , mPrevGCSliceCallback(nullptr)
     511             :   , mPrevGCNurseryCollectionCallback(nullptr)
     512             :   , mJSHolderMap(256)
     513             :   , mOutOfMemoryState(OOMState::OK)
     514           8 :   , mLargeAllocationFailureState(OOMState::OK)
     515             : {
     516           4 :   MOZ_COUNT_CTOR(CycleCollectedJSRuntime);
     517           4 :   MOZ_ASSERT(aCx);
     518           4 :   MOZ_ASSERT(mJSRuntime);
     519             : 
     520           4 :   if (!JS_AddExtraGCRootsTracer(aCx, TraceBlackJS, this)) {
     521           0 :     MOZ_CRASH("JS_AddExtraGCRootsTracer failed");
     522             :   }
     523           4 :   JS_SetGrayGCRootsTracer(aCx, TraceGrayJS, this);
     524           4 :   JS_SetGCCallback(aCx, GCCallback, this);
     525           4 :   mPrevGCSliceCallback = JS::SetGCSliceCallback(aCx, GCSliceCallback);
     526             : 
     527           4 :   if (NS_IsMainThread()) {
     528             :     // We would like to support all threads here, but the way timeline consumers
     529             :     // are set up currently, you can either add a marker for one specific
     530             :     // docshell, or for every consumer globally. We would like to add a marker
     531             :     // for every consumer observing anything on this thread, but that is not
     532             :     // currently possible. For now, add global markers only when we are on the
     533             :     // main thread, since the UI for this tracing data only displays data
     534             :     // relevant to the main-thread.
     535           3 :     mPrevGCNurseryCollectionCallback = JS::SetGCNurseryCollectionCallback(
     536             :       aCx, GCNurseryCollectionCallback);
     537             :   }
     538             : 
     539           4 :   JS_SetObjectsTenuredCallback(aCx, JSObjectsTenuredCb, this);
     540           4 :   JS::SetOutOfMemoryCallback(aCx, OutOfMemoryCallback, this);
     541           4 :   JS_SetExternalStringSizeofCallback(aCx, SizeofExternalStringCallback);
     542           4 :   JS::SetBuildIdOp(aCx, GetBuildId);
     543           4 :   JS::SetWarningReporter(aCx, MozCrashWarningReporter);
     544             : #ifdef MOZ_CRASHREPORTER
     545             :     js::AutoEnterOOMUnsafeRegion::setAnnotateOOMAllocationSizeCallback(
     546           4 :             CrashReporter::AnnotateOOMAllocationSize);
     547             : #endif
     548             : 
     549             :   static js::DOMCallbacks DOMcallbacks = {
     550             :     InstanceClassHasProtoAtDepth
     551             :   };
     552           4 :   SetDOMCallbacks(aCx, &DOMcallbacks);
     553           4 :   js::SetScriptEnvironmentPreparer(aCx, &mEnvironmentPreparer);
     554             : 
     555           4 :   JS::dbg::SetDebuggerMallocSizeOf(aCx, moz_malloc_size_of);
     556           4 : }
     557             : 
     558             : void
     559           0 : CycleCollectedJSRuntime::Shutdown(JSContext* cx)
     560             : {
     561           0 :   JS_RemoveExtraGCRootsTracer(cx, TraceBlackJS, this);
     562           0 :   JS_RemoveExtraGCRootsTracer(cx, TraceGrayJS, this);
     563           0 : }
     564             : 
     565           0 : CycleCollectedJSRuntime::~CycleCollectedJSRuntime()
     566             : {
     567           0 :   MOZ_COUNT_DTOR(CycleCollectedJSRuntime);
     568           0 :   MOZ_ASSERT(!mDeferredFinalizerTable.Count());
     569           0 : }
     570             : 
     571             : void
     572           4 : CycleCollectedJSRuntime::AddContext(CycleCollectedJSContext* aContext)
     573             : {
     574           4 :   mContexts.insertBack(aContext);
     575           4 : }
     576             : 
     577             : void
     578           0 : CycleCollectedJSRuntime::RemoveContext(CycleCollectedJSContext* aContext)
     579             : {
     580           0 :   aContext->removeFrom(mContexts);
     581           0 : }
     582             : 
     583             : size_t
     584           0 : CycleCollectedJSRuntime::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
     585             : {
     586           0 :   size_t n = 0;
     587             : 
     588             :   // We're deliberately not measuring anything hanging off the entries in
     589             :   // mJSHolders.
     590           0 :   n += mJSHolders.SizeOfExcludingThis(aMallocSizeOf);
     591           0 :   n += mJSHolderMap.ShallowSizeOfExcludingThis(aMallocSizeOf);
     592             : 
     593           0 :   return n;
     594             : }
     595             : 
     596             : void
     597           0 : CycleCollectedJSRuntime::UnmarkSkippableJSHolders()
     598             : {
     599           0 :   for (auto iter = mJSHolders.Iter(); !iter.Done(); iter.Next()) {
     600           0 :     void* holder = iter.Get().mHolder;
     601           0 :     nsScriptObjectTracer* tracer = iter.Get().mTracer;
     602           0 :     tracer->CanSkip(holder, true);
     603             :   }
     604           0 : }
     605             : 
     606             : void
     607           0 : CycleCollectedJSRuntime::DescribeGCThing(bool aIsMarked, JS::GCCellPtr aThing,
     608             :                                          nsCycleCollectionTraversalCallback& aCb) const
     609             : {
     610           0 :   if (!aCb.WantDebugInfo()) {
     611           0 :     aCb.DescribeGCedNode(aIsMarked, "JS Object");
     612           0 :     return;
     613             :   }
     614             : 
     615             :   char name[72];
     616           0 :   uint64_t compartmentAddress = 0;
     617           0 :   if (aThing.is<JSObject>()) {
     618           0 :     JSObject* obj = &aThing.as<JSObject>();
     619           0 :     compartmentAddress = (uint64_t)js::GetObjectCompartment(obj);
     620           0 :     const js::Class* clasp = js::GetObjectClass(obj);
     621             : 
     622             :     // Give the subclass a chance to do something
     623           0 :     if (DescribeCustomObjects(obj, clasp, name)) {
     624             :       // Nothing else to do!
     625           0 :     } else if (js::IsFunctionObject(obj)) {
     626           0 :       JSFunction* fun = JS_GetObjectFunction(obj);
     627           0 :       JSString* str = JS_GetFunctionDisplayId(fun);
     628           0 :       if (str) {
     629           0 :         JSFlatString* flat = JS_ASSERT_STRING_IS_FLAT(str);
     630           0 :         nsAutoString chars;
     631           0 :         AssignJSFlatString(chars, flat);
     632           0 :         NS_ConvertUTF16toUTF8 fname(chars);
     633           0 :         SprintfLiteral(name, "JS Object (Function - %s)", fname.get());
     634             :       } else {
     635           0 :         SprintfLiteral(name, "JS Object (Function)");
     636             :       }
     637             :     } else {
     638           0 :       SprintfLiteral(name, "JS Object (%s)", clasp->name);
     639             :     }
     640             :   } else {
     641           0 :     SprintfLiteral(name, "JS %s", JS::GCTraceKindToAscii(aThing.kind()));
     642             :   }
     643             : 
     644             :   // Disable printing global for objects while we figure out ObjShrink fallout.
     645           0 :   aCb.DescribeGCedNode(aIsMarked, name, compartmentAddress);
     646             : }
     647             : 
     648             : void
     649           0 : CycleCollectedJSRuntime::NoteGCThingJSChildren(JS::GCCellPtr aThing,
     650             :                                                nsCycleCollectionTraversalCallback& aCb) const
     651             : {
     652           0 :   TraversalTracer trc(mJSRuntime, aCb);
     653           0 :   JS::TraceChildren(&trc, aThing);
     654           0 : }
     655             : 
     656             : void
     657           0 : CycleCollectedJSRuntime::NoteGCThingXPCOMChildren(const js::Class* aClasp,
     658             :                                                   JSObject* aObj,
     659             :                                                   nsCycleCollectionTraversalCallback& aCb) const
     660             : {
     661           0 :   MOZ_ASSERT(aClasp);
     662           0 :   MOZ_ASSERT(aClasp == js::GetObjectClass(aObj));
     663             : 
     664           0 :   if (NoteCustomGCThingXPCOMChildren(aClasp, aObj, aCb)) {
     665             :     // Nothing else to do!
     666           0 :     return;
     667             :   }
     668             :   // XXX This test does seem fragile, we should probably whitelist classes
     669             :   //     that do hold a strong reference, but that might not be possible.
     670           0 :   else if (aClasp->flags & JSCLASS_HAS_PRIVATE &&
     671           0 :            aClasp->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) {
     672           0 :     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "js::GetObjectPrivate(obj)");
     673           0 :     aCb.NoteXPCOMChild(static_cast<nsISupports*>(js::GetObjectPrivate(aObj)));
     674             :   } else {
     675           0 :     const DOMJSClass* domClass = GetDOMClass(aObj);
     676           0 :     if (domClass) {
     677           0 :       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "UnwrapDOMObject(obj)");
     678             :       // It's possible that our object is an unforgeable holder object, in
     679             :       // which case it doesn't actually have a C++ DOM object associated with
     680             :       // it.  Use UnwrapPossiblyNotInitializedDOMObject, which produces null in
     681             :       // that case, since NoteXPCOMChild/NoteNativeChild are null-safe.
     682           0 :       if (domClass->mDOMObjectIsISupports) {
     683           0 :         aCb.NoteXPCOMChild(UnwrapPossiblyNotInitializedDOMObject<nsISupports>(aObj));
     684           0 :       } else if (domClass->mParticipant) {
     685           0 :         aCb.NoteNativeChild(UnwrapPossiblyNotInitializedDOMObject<void>(aObj),
     686           0 :                             domClass->mParticipant);
     687             :       }
     688             :     }
     689             :   }
     690             : }
     691             : 
     692             : void
     693           0 : CycleCollectedJSRuntime::TraverseGCThing(TraverseSelect aTs, JS::GCCellPtr aThing,
     694             :                                          nsCycleCollectionTraversalCallback& aCb)
     695             : {
     696           0 :   bool isMarkedGray = JS::GCThingIsMarkedGray(aThing);
     697             : 
     698           0 :   if (aTs == TRAVERSE_FULL) {
     699           0 :     DescribeGCThing(!isMarkedGray, aThing, aCb);
     700             :   }
     701             : 
     702             :   // If this object is alive, then all of its children are alive. For JS objects,
     703             :   // the black-gray invariant ensures the children are also marked black. For C++
     704             :   // objects, the ref count from this object will keep them alive. Thus we don't
     705             :   // need to trace our children, unless we are debugging using WantAllTraces.
     706           0 :   if (!isMarkedGray && !aCb.WantAllTraces()) {
     707           0 :     return;
     708             :   }
     709             : 
     710           0 :   if (aTs == TRAVERSE_FULL) {
     711           0 :     NoteGCThingJSChildren(aThing, aCb);
     712             :   }
     713             : 
     714           0 :   if (aThing.is<JSObject>()) {
     715           0 :     JSObject* obj = &aThing.as<JSObject>();
     716           0 :     NoteGCThingXPCOMChildren(js::GetObjectClass(obj), obj, aCb);
     717             :   }
     718             : }
     719             : 
     720             : struct TraverseObjectShimClosure
     721             : {
     722             :   nsCycleCollectionTraversalCallback& cb;
     723             :   CycleCollectedJSRuntime* self;
     724             : };
     725             : 
     726             : void
     727           0 : CycleCollectedJSRuntime::TraverseZone(JS::Zone* aZone,
     728             :                                       nsCycleCollectionTraversalCallback& aCb)
     729             : {
     730             :   /*
     731             :    * We treat the zone as being gray. We handle non-gray GCthings in the
     732             :    * zone by not reporting their children to the CC. The black-gray invariant
     733             :    * ensures that any JS children will also be non-gray, and thus don't need to be
     734             :    * added to the graph. For C++ children, not representing the edge from the
     735             :    * non-gray JS GCthings to the C++ object will keep the child alive.
     736             :    *
     737             :    * We don't allow zone merging in a WantAllTraces CC, because then these
     738             :    * assumptions don't hold.
     739             :    */
     740           0 :   aCb.DescribeGCedNode(false, "JS Zone");
     741             : 
     742             :   /*
     743             :    * Every JS child of everything in the zone is either in the zone
     744             :    * or is a cross-compartment wrapper. In the former case, we don't need to
     745             :    * represent these edges in the CC graph because JS objects are not ref counted.
     746             :    * In the latter case, the JS engine keeps a map of these wrappers, which we
     747             :    * iterate over. Edges between compartments in the same zone will add
     748             :    * unnecessary loop edges to the graph (bug 842137).
     749             :    */
     750           0 :   TraversalTracer trc(mJSRuntime, aCb);
     751           0 :   js::VisitGrayWrapperTargets(aZone, NoteJSChildGrayWrapperShim, &trc);
     752             : 
     753             :   /*
     754             :    * To find C++ children of things in the zone, we scan every JS Object in
     755             :    * the zone. Only JS Objects can have C++ children.
     756             :    */
     757           0 :   TraverseObjectShimClosure closure = { aCb, this };
     758           0 :   js::IterateGrayObjects(aZone, TraverseObjectShim, &closure);
     759           0 : }
     760             : 
     761             : /* static */ void
     762           0 : CycleCollectedJSRuntime::TraverseObjectShim(void* aData, JS::GCCellPtr aThing)
     763             : {
     764             :   TraverseObjectShimClosure* closure =
     765           0 :     static_cast<TraverseObjectShimClosure*>(aData);
     766             : 
     767           0 :   MOZ_ASSERT(aThing.is<JSObject>());
     768           0 :   closure->self->TraverseGCThing(CycleCollectedJSRuntime::TRAVERSE_CPP,
     769           0 :                                  aThing, closure->cb);
     770           0 : }
     771             : 
     772             : void
     773           0 : CycleCollectedJSRuntime::TraverseNativeRoots(nsCycleCollectionNoteRootCallback& aCb)
     774             : {
     775             :   // NB: This is here just to preserve the existing XPConnect order. I doubt it
     776             :   // would hurt to do this after the JS holders.
     777           0 :   TraverseAdditionalNativeRoots(aCb);
     778             : 
     779           0 :   for (auto iter = mJSHolders.Iter(); !iter.Done(); iter.Next()) {
     780           0 :     void* holder = iter.Get().mHolder;
     781           0 :     nsScriptObjectTracer* tracer = iter.Get().mTracer;
     782             : 
     783           0 :     bool noteRoot = false;
     784           0 :     if (MOZ_UNLIKELY(aCb.WantAllTraces())) {
     785           0 :       noteRoot = true;
     786             :     } else {
     787             :       tracer->Trace(holder,
     788           0 :                     TraceCallbackFunc(CheckParticipatesInCycleCollection),
     789           0 :                     &noteRoot);
     790             :     }
     791             : 
     792           0 :     if (noteRoot) {
     793           0 :       aCb.NoteNativeRoot(holder, tracer);
     794             :     }
     795             :   }
     796           0 : }
     797             : 
     798             : /* static */ void
     799           1 : CycleCollectedJSRuntime::TraceBlackJS(JSTracer* aTracer, void* aData)
     800             : {
     801           1 :   CycleCollectedJSRuntime* self = static_cast<CycleCollectedJSRuntime*>(aData);
     802             : 
     803           1 :   self->TraceNativeBlackRoots(aTracer);
     804           1 : }
     805             : 
     806             : /* static */ void
     807           1 : CycleCollectedJSRuntime::TraceGrayJS(JSTracer* aTracer, void* aData)
     808             : {
     809           1 :   CycleCollectedJSRuntime* self = static_cast<CycleCollectedJSRuntime*>(aData);
     810             : 
     811             :   // Mark these roots as gray so the CC can walk them later.
     812           1 :   self->TraceNativeGrayRoots(aTracer);
     813           1 : }
     814             : 
     815             : /* static */ void
     816           1 : CycleCollectedJSRuntime::GCCallback(JSContext* aContext,
     817             :                                     JSGCStatus aStatus,
     818             :                                     void* aData)
     819             : {
     820           1 :   CycleCollectedJSRuntime* self = static_cast<CycleCollectedJSRuntime*>(aData);
     821             : 
     822           1 :   MOZ_ASSERT(CycleCollectedJSContext::Get()->Context() == aContext);
     823           1 :   MOZ_ASSERT(CycleCollectedJSContext::Get()->Runtime() == self);
     824             : 
     825           1 :   self->OnGC(aContext, aStatus);
     826           1 : }
     827             : 
     828             : /* static */ void
     829           7 : CycleCollectedJSRuntime::GCSliceCallback(JSContext* aContext,
     830             :                                          JS::GCProgress aProgress,
     831             :                                          const JS::GCDescription& aDesc)
     832             : {
     833           7 :   CycleCollectedJSRuntime* self = CycleCollectedJSRuntime::Get();
     834           7 :   MOZ_ASSERT(CycleCollectedJSContext::Get()->Context() == aContext);
     835             : 
     836           7 :   if (profiler_is_active()) {
     837           0 :     if (aProgress == JS::GC_CYCLE_END) {
     838           0 :       profiler_add_marker(
     839             :         "GCMajor",
     840           0 :         MakeUnique<GCMajorMarkerPayload>(aDesc.startTime(aContext),
     841           0 :                                          aDesc.endTime(aContext),
     842           0 :                                          aDesc.summaryToJSON(aContext)));
     843           0 :     } else if (aProgress == JS::GC_SLICE_END) {
     844           0 :       profiler_add_marker(
     845             :         "GCSlice",
     846           0 :         MakeUnique<GCSliceMarkerPayload>(aDesc.lastSliceStart(aContext),
     847           0 :                                          aDesc.lastSliceEnd(aContext),
     848           0 :                                          aDesc.sliceToJSON(aContext)));
     849             :     }
     850             :   }
     851             : 
     852           7 :   if (aProgress == JS::GC_CYCLE_END &&
     853           0 :       JS::dbg::FireOnGarbageCollectionHookRequired(aContext)) {
     854           0 :     JS::gcreason::Reason reason = aDesc.reason_;
     855             :     Unused <<
     856           0 :       NS_WARN_IF(NS_FAILED(DebuggerOnGCRunnable::Enqueue(aContext, aDesc)) &&
     857             :                  reason != JS::gcreason::SHUTDOWN_CC &&
     858             :                  reason != JS::gcreason::DESTROY_RUNTIME &&
     859             :                  reason != JS::gcreason::XPCONNECT_SHUTDOWN);
     860             :   }
     861             : 
     862           7 :   if (self->mPrevGCSliceCallback) {
     863           0 :     self->mPrevGCSliceCallback(aContext, aProgress, aDesc);
     864             :   }
     865           7 : }
     866             : 
     867           0 : class MinorGCMarker : public TimelineMarker
     868             : {
     869             : private:
     870             :   JS::gcreason::Reason mReason;
     871             : 
     872             : public:
     873           0 :   MinorGCMarker(MarkerTracingType aTracingType,
     874             :                 JS::gcreason::Reason aReason)
     875           0 :     : TimelineMarker("MinorGC",
     876             :                      aTracingType,
     877             :                      MarkerStackRequest::NO_STACK)
     878           0 :     , mReason(aReason)
     879             :     {
     880           0 :       MOZ_ASSERT(aTracingType == MarkerTracingType::START ||
     881             :                  aTracingType == MarkerTracingType::END);
     882           0 :     }
     883             : 
     884           0 :   MinorGCMarker(JS::GCNurseryProgress aProgress,
     885             :                 JS::gcreason::Reason aReason)
     886           0 :     : TimelineMarker("MinorGC",
     887             :                      aProgress == JS::GCNurseryProgress::GC_NURSERY_COLLECTION_START
     888             :                        ? MarkerTracingType::START
     889             :                        : MarkerTracingType::END,
     890             :                      MarkerStackRequest::NO_STACK)
     891           0 :     , mReason(aReason)
     892           0 :   { }
     893             : 
     894             :   virtual void
     895           0 :   AddDetails(JSContext* aCx,
     896             :              dom::ProfileTimelineMarker& aMarker) override
     897             :   {
     898           0 :     TimelineMarker::AddDetails(aCx, aMarker);
     899             : 
     900           0 :     if (GetTracingType() == MarkerTracingType::START) {
     901           0 :       auto reason = JS::gcreason::ExplainReason(mReason);
     902           0 :       aMarker.mCauseName.Construct(NS_ConvertUTF8toUTF16(reason));
     903             :     }
     904           0 :   }
     905             : 
     906             :   virtual UniquePtr<AbstractTimelineMarker>
     907           0 :   Clone() override
     908             :   {
     909           0 :     auto clone = MakeUnique<MinorGCMarker>(GetTracingType(), mReason);
     910           0 :     clone->SetCustomTime(GetTime());
     911           0 :     return UniquePtr<AbstractTimelineMarker>(Move(clone));
     912             :   }
     913             : };
     914             : 
     915             : /* static */ void
     916          48 : CycleCollectedJSRuntime::GCNurseryCollectionCallback(JSContext* aContext,
     917             :                                                      JS::GCNurseryProgress aProgress,
     918             :                                                      JS::gcreason::Reason aReason)
     919             : {
     920          48 :   CycleCollectedJSRuntime* self = CycleCollectedJSRuntime::Get();
     921          48 :   MOZ_ASSERT(CycleCollectedJSContext::Get()->Context() == aContext);
     922          48 :   MOZ_ASSERT(NS_IsMainThread());
     923             : 
     924          96 :   RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
     925          48 :   if (timelines && !timelines->IsEmpty()) {
     926             :     UniquePtr<AbstractTimelineMarker> abstractMarker(
     927           0 :       MakeUnique<MinorGCMarker>(aProgress, aReason));
     928           0 :     timelines->AddMarkerForAllObservedDocShells(abstractMarker);
     929             :   }
     930             : 
     931          48 :   if (aProgress == JS::GCNurseryProgress::GC_NURSERY_COLLECTION_START) {
     932          24 :     self->mLatestNurseryCollectionStart = TimeStamp::Now();
     933          48 :   } else if ((aProgress == JS::GCNurseryProgress::GC_NURSERY_COLLECTION_END) &&
     934          24 :              profiler_is_active())
     935             :   {
     936           0 :     profiler_add_marker(
     937             :       "GCMinor",
     938           0 :       MakeUnique<GCMinorMarkerPayload>(self->mLatestNurseryCollectionStart,
     939           0 :                                        TimeStamp::Now(),
     940           0 :                                        JS::MinorGcToJSON(aContext)));
     941             :   }
     942             : 
     943          48 :   if (self->mPrevGCNurseryCollectionCallback) {
     944           0 :     self->mPrevGCNurseryCollectionCallback(aContext, aProgress, aReason);
     945             :   }
     946          48 : }
     947             : 
     948             : 
     949             : /* static */ void
     950           0 : CycleCollectedJSRuntime::OutOfMemoryCallback(JSContext* aContext,
     951             :                                              void* aData)
     952             : {
     953           0 :   CycleCollectedJSRuntime* self = static_cast<CycleCollectedJSRuntime*>(aData);
     954             : 
     955           0 :   MOZ_ASSERT(CycleCollectedJSContext::Get()->Context() == aContext);
     956           0 :   MOZ_ASSERT(CycleCollectedJSContext::Get()->Runtime() == self);
     957             : 
     958           0 :   self->OnOutOfMemory();
     959           0 : }
     960             : 
     961             : /* static */ size_t
     962           0 : CycleCollectedJSRuntime::SizeofExternalStringCallback(JSString* aStr,
     963             :                                                       MallocSizeOf aMallocSizeOf)
     964             : {
     965             :   // We promised the JS engine we would not GC.  Enforce that:
     966           0 :   JS::AutoCheckCannotGC autoCannotGC;
     967             : 
     968           0 :   if (!XPCStringConvert::IsDOMString(aStr)) {
     969             :     // Might be a literal or something we don't understand.  Just claim 0.
     970           0 :     return 0;
     971             :   }
     972             : 
     973           0 :   const char16_t* chars = JS_GetTwoByteExternalStringChars(aStr);
     974           0 :   const nsStringBuffer* buf = nsStringBuffer::FromData((void*)chars);
     975             :   // We want sizeof including this, because the entire string buffer is owned by
     976             :   // the external string.  But only report here if we're unshared; if we're
     977             :   // shared then we don't know who really owns this data.
     978           0 :   return buf->SizeOfIncludingThisIfUnshared(aMallocSizeOf);
     979             : }
     980             : 
     981        3441 : struct JsGcTracer : public TraceCallbacks
     982             : {
     983           9 :   virtual void Trace(JS::Heap<JS::Value>* aPtr, const char* aName,
     984             :                      void* aClosure) const override
     985             :   {
     986           9 :     JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
     987           9 :   }
     988           0 :   virtual void Trace(JS::Heap<jsid>* aPtr, const char* aName,
     989             :                      void* aClosure) const override
     990             :   {
     991           0 :     JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
     992           0 :   }
     993        4093 :   virtual void Trace(JS::Heap<JSObject*>* aPtr, const char* aName,
     994             :                      void* aClosure) const override
     995             :   {
     996        4093 :     JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
     997        4093 :   }
     998         252 :   virtual void Trace(JSObject** aPtr, const char* aName,
     999             :                      void* aClosure) const override
    1000             :   {
    1001         252 :     js::UnsafeTraceManuallyBarrieredEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
    1002         252 :   }
    1003         472 :   virtual void Trace(JS::TenuredHeap<JSObject*>* aPtr, const char* aName,
    1004             :                      void* aClosure) const override
    1005             :   {
    1006         472 :     JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
    1007         472 :   }
    1008           0 :   virtual void Trace(JS::Heap<JSString*>* aPtr, const char* aName,
    1009             :                      void* aClosure) const override
    1010             :   {
    1011           0 :     JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
    1012           0 :   }
    1013          39 :   virtual void Trace(JS::Heap<JSScript*>* aPtr, const char* aName,
    1014             :                      void* aClosure) const override
    1015             :   {
    1016          39 :     JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
    1017          39 :   }
    1018           0 :   virtual void Trace(JS::Heap<JSFunction*>* aPtr, const char* aName,
    1019             :                      void* aClosure) const override
    1020             :   {
    1021           0 :     JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
    1022           0 :   }
    1023             : };
    1024             : 
    1025             : void
    1026          47 : mozilla::TraceScriptHolder(nsISupports* aHolder, JSTracer* aTracer)
    1027             : {
    1028          47 :   nsXPCOMCycleCollectionParticipant* participant = nullptr;
    1029          47 :   CallQueryInterface(aHolder, &participant);
    1030          47 :   participant->Trace(aHolder, JsGcTracer(), aTracer);
    1031          47 : }
    1032             : 
    1033             : void
    1034           1 : CycleCollectedJSRuntime::TraceNativeGrayRoots(JSTracer* aTracer)
    1035             : {
    1036             :   // NB: This is here just to preserve the existing XPConnect order. I doubt it
    1037             :   // would hurt to do this after the JS holders.
    1038           1 :   TraceAdditionalNativeGrayRoots(aTracer);
    1039             : 
    1040        3395 :   for (auto iter = mJSHolders.Iter(); !iter.Done(); iter.Next()) {
    1041        3394 :     void* holder = iter.Get().mHolder;
    1042        3394 :     nsScriptObjectTracer* tracer = iter.Get().mTracer;
    1043        3394 :     tracer->Trace(holder, JsGcTracer(), aTracer);
    1044             :   }
    1045           1 : }
    1046             : 
    1047             : void
    1048        3461 : CycleCollectedJSRuntime::AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer)
    1049             : {
    1050        3461 :   JSHolderInfo* info = nullptr;
    1051        3461 :   if (mJSHolderMap.Get(aHolder, &info)) {
    1052           9 :     MOZ_ASSERT(info->mHolder == aHolder);
    1053           9 :     info->mTracer = aTracer;
    1054           9 :     return;
    1055             :   }
    1056             : 
    1057        3452 :   mJSHolders.InfallibleAppend(JSHolderInfo {aHolder, aTracer});
    1058        3452 :   mJSHolderMap.Put(aHolder, &mJSHolders.GetLast());
    1059             : }
    1060             : 
    1061         267 : struct ClearJSHolder : public TraceCallbacks
    1062             : {
    1063           1 :   virtual void Trace(JS::Heap<JS::Value>* aPtr, const char*, void*) const override
    1064             :   {
    1065           1 :     aPtr->setUndefined();
    1066           1 :   }
    1067             : 
    1068           0 :   virtual void Trace(JS::Heap<jsid>* aPtr, const char*, void*) const override
    1069             :   {
    1070           0 :     *aPtr = JSID_VOID;
    1071           0 :   }
    1072             : 
    1073         493 :   virtual void Trace(JS::Heap<JSObject*>* aPtr, const char*, void*) const override
    1074             :   {
    1075         493 :     *aPtr = nullptr;
    1076         493 :   }
    1077             : 
    1078           0 :   virtual void Trace(JSObject** aPtr, const char* aName,
    1079             :                      void* aClosure) const override
    1080             :   {
    1081           0 :     *aPtr = nullptr;
    1082           0 :   }
    1083             : 
    1084         237 :   virtual void Trace(JS::TenuredHeap<JSObject*>* aPtr, const char*, void*) const override
    1085             :   {
    1086         237 :     *aPtr = nullptr;
    1087         237 :   }
    1088             : 
    1089           0 :   virtual void Trace(JS::Heap<JSString*>* aPtr, const char*, void*) const override
    1090             :   {
    1091           0 :     *aPtr = nullptr;
    1092           0 :   }
    1093             : 
    1094           0 :   virtual void Trace(JS::Heap<JSScript*>* aPtr, const char*, void*) const override
    1095             :   {
    1096           0 :     *aPtr = nullptr;
    1097           0 :   }
    1098             : 
    1099           0 :   virtual void Trace(JS::Heap<JSFunction*>* aPtr, const char*, void*) const override
    1100             :   {
    1101           0 :     *aPtr = nullptr;
    1102           0 :   }
    1103             : };
    1104             : 
    1105             : void
    1106         281 : CycleCollectedJSRuntime::RemoveJSHolder(void* aHolder)
    1107             : {
    1108         281 :   JSHolderInfo* info = nullptr;
    1109         281 :   if (mJSHolderMap.Get(aHolder, &info)) {
    1110         267 :     MOZ_ASSERT(info->mHolder == aHolder);
    1111         267 :     info->mTracer->Trace(aHolder, ClearJSHolder(), nullptr);
    1112             : 
    1113         267 :     JSHolderInfo* lastInfo = &mJSHolders.GetLast();
    1114         267 :     if (info != lastInfo) {
    1115         267 :       *info = *lastInfo;
    1116         267 :       mJSHolderMap.Put(info->mHolder, info);
    1117             :     }
    1118             : 
    1119         267 :     mJSHolders.PopLast();
    1120         267 :     mJSHolderMap.Remove(aHolder);
    1121             :   }
    1122         281 : }
    1123             : 
    1124             : #ifdef DEBUG
    1125             : bool
    1126           0 : CycleCollectedJSRuntime::IsJSHolder(void* aHolder)
    1127             : {
    1128           0 :   return mJSHolderMap.Get(aHolder, nullptr);
    1129             : }
    1130             : 
    1131             : static void
    1132           0 : AssertNoGcThing(JS::GCCellPtr aGCThing, const char* aName, void* aClosure)
    1133             : {
    1134           0 :   MOZ_ASSERT(!aGCThing);
    1135           0 : }
    1136             : 
    1137             : void
    1138           0 : CycleCollectedJSRuntime::AssertNoObjectsToTrace(void* aPossibleJSHolder)
    1139             : {
    1140           0 :   JSHolderInfo* info = nullptr;
    1141           0 :   if (!mJSHolderMap.Get(aPossibleJSHolder, &info)) {
    1142           0 :     return;
    1143             :   }
    1144             : 
    1145           0 :   MOZ_ASSERT(info->mHolder == aPossibleJSHolder);
    1146           0 :   info->mTracer->Trace(aPossibleJSHolder, TraceCallbackFunc(AssertNoGcThing), nullptr);
    1147             : }
    1148             : #endif
    1149             : 
    1150             : nsCycleCollectionParticipant*
    1151           0 : CycleCollectedJSRuntime::GCThingParticipant()
    1152             : {
    1153           0 :   return &mGCThingCycleCollectorGlobal;
    1154             : }
    1155             : 
    1156             : nsCycleCollectionParticipant*
    1157           0 : CycleCollectedJSRuntime::ZoneParticipant()
    1158             : {
    1159           0 :   return &mJSZoneCycleCollectorGlobal;
    1160             : }
    1161             : 
    1162             : nsresult
    1163           0 : CycleCollectedJSRuntime::TraverseRoots(nsCycleCollectionNoteRootCallback& aCb)
    1164             : {
    1165           0 :   TraverseNativeRoots(aCb);
    1166             : 
    1167           0 :   NoteWeakMapsTracer trc(mJSRuntime, aCb);
    1168           0 :   js::TraceWeakMaps(&trc);
    1169             : 
    1170           0 :   return NS_OK;
    1171             : }
    1172             : 
    1173             : bool
    1174           0 : CycleCollectedJSRuntime::UsefulToMergeZones() const
    1175             : {
    1176           0 :   return false;
    1177             : }
    1178             : 
    1179             : void
    1180           0 : CycleCollectedJSRuntime::FixWeakMappingGrayBits() const
    1181             : {
    1182           0 :   MOZ_ASSERT(!JS::IsIncrementalGCInProgress(mJSRuntime),
    1183             :              "Don't call FixWeakMappingGrayBits during a GC.");
    1184           0 :   FixWeakMappingGrayBitsTracer fixer(mJSRuntime);
    1185           0 :   fixer.FixAll();
    1186           0 : }
    1187             : 
    1188             : void
    1189           0 : CycleCollectedJSRuntime::CheckGrayBits() const
    1190             : {
    1191           0 :   MOZ_ASSERT(!JS::IsIncrementalGCInProgress(mJSRuntime),
    1192             :              "Don't call CheckGrayBits during a GC.");
    1193             : 
    1194             : #ifndef ANDROID
    1195             :   // Bug 1346874 - The gray state check is expensive. Android tests are already
    1196             :   // slow enough that this check can easily push them over the threshold to a
    1197             :   // timeout.
    1198             : 
    1199           0 :   MOZ_ASSERT(js::CheckGrayMarkingState(mJSRuntime));
    1200           0 :   MOZ_ASSERT(CheckWeakMappingGrayBitsTracer::Check(mJSRuntime));
    1201             : #endif
    1202           0 : }
    1203             : 
    1204             : bool
    1205           0 : CycleCollectedJSRuntime::AreGCGrayBitsValid() const
    1206             : {
    1207           0 :   return js::AreGCGrayBitsValid(mJSRuntime);
    1208             : }
    1209             : 
    1210             : void
    1211           0 : CycleCollectedJSRuntime::GarbageCollect(uint32_t aReason) const
    1212             : {
    1213           0 :   MOZ_ASSERT(aReason < JS::gcreason::NUM_REASONS);
    1214           0 :   JS::gcreason::Reason gcreason = static_cast<JS::gcreason::Reason>(aReason);
    1215             : 
    1216           0 :   JSContext* cx = CycleCollectedJSContext::Get()->Context();
    1217           0 :   JS::PrepareForFullGC(cx);
    1218           0 :   JS::GCForReason(cx, GC_NORMAL, gcreason);
    1219           0 : }
    1220             : 
    1221             : void
    1222          21 : CycleCollectedJSRuntime::JSObjectsTenured()
    1223             : {
    1224          82 :   for (auto iter = mNurseryObjects.Iter(); !iter.Done(); iter.Next()) {
    1225          61 :     nsWrapperCache* cache = iter.Get();
    1226          61 :     JSObject* wrapper = cache->GetWrapperMaybeDead();
    1227          61 :     MOZ_DIAGNOSTIC_ASSERT(wrapper);
    1228          61 :     if (!JS::ObjectIsTenured(wrapper)) {
    1229          58 :       MOZ_ASSERT(!cache->PreservingWrapper());
    1230          58 :       const JSClass* jsClass = js::GetObjectJSClass(wrapper);
    1231          58 :       jsClass->doFinalize(nullptr, wrapper);
    1232             :     }
    1233             :   }
    1234             : 
    1235             : #ifdef DEBUG
    1236          21 : for (auto iter = mPreservedNurseryObjects.Iter(); !iter.Done(); iter.Next()) {
    1237           0 :   MOZ_ASSERT(JS::ObjectIsTenured(iter.Get().get()));
    1238             : }
    1239             : #endif
    1240             : 
    1241          21 :   mNurseryObjects.Clear();
    1242          21 :   mPreservedNurseryObjects.Clear();
    1243          21 : }
    1244             : 
    1245             : void
    1246          77 : CycleCollectedJSRuntime::NurseryWrapperAdded(nsWrapperCache* aCache)
    1247             : {
    1248          77 :   MOZ_ASSERT(aCache);
    1249          77 :   MOZ_ASSERT(aCache->GetWrapperMaybeDead());
    1250          77 :   MOZ_ASSERT(!JS::ObjectIsTenured(aCache->GetWrapperMaybeDead()));
    1251          77 :   mNurseryObjects.InfallibleAppend(aCache);
    1252          77 : }
    1253             : 
    1254             : void
    1255           0 : CycleCollectedJSRuntime::NurseryWrapperPreserved(JSObject* aWrapper)
    1256             : {
    1257           0 :   mPreservedNurseryObjects.InfallibleAppend(
    1258           0 :     JS::PersistentRooted<JSObject*>(mJSRuntime, aWrapper));
    1259           0 : }
    1260             : 
    1261             : void
    1262          58 : CycleCollectedJSRuntime::DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
    1263             :                                           DeferredFinalizeFunction aFunc,
    1264             :                                           void* aThing)
    1265             : {
    1266         116 :   if (auto entry = mDeferredFinalizerTable.LookupForAdd(aFunc)) {
    1267          57 :     aAppendFunc(entry.Data(), aThing);
    1268             :   } else {
    1269             :     entry.OrInsert(
    1270           2 :       [aAppendFunc, aThing] () { return aAppendFunc(nullptr, aThing); });
    1271             :   }
    1272          58 : }
    1273             : 
    1274             : void
    1275          58 : CycleCollectedJSRuntime::DeferredFinalize(nsISupports* aSupports)
    1276             : {
    1277             :   typedef DeferredFinalizerImpl<nsISupports> Impl;
    1278             :   DeferredFinalize(Impl::AppendDeferredFinalizePointer, Impl::DeferredFinalize,
    1279          58 :                    aSupports);
    1280          58 : }
    1281             : 
    1282             : void
    1283           0 : CycleCollectedJSRuntime::DumpJSHeap(FILE* aFile)
    1284             : {
    1285           0 :   JSContext* cx = CycleCollectedJSContext::Get()->Context();
    1286           0 :   js::DumpHeap(cx, aFile, js::CollectNurseryBeforeDump);
    1287           0 : }
    1288             : 
    1289           0 : IncrementalFinalizeRunnable::IncrementalFinalizeRunnable(CycleCollectedJSRuntime* aRt,
    1290           0 :                                                          DeferredFinalizerTable& aFinalizers)
    1291             :   : Runnable("IncrementalFinalizeRunnable")
    1292             :   , mRuntime(aRt)
    1293             :   , mFinalizeFunctionToRun(0)
    1294           0 :   , mReleasing(false)
    1295             : {
    1296           0 :   for (auto iter = aFinalizers.Iter(); !iter.Done(); iter.Next()) {
    1297           0 :     DeferredFinalizeFunction& function = iter.Key();
    1298           0 :     void*& data = iter.Data();
    1299             : 
    1300             :     DeferredFinalizeFunctionHolder* holder =
    1301           0 :       mDeferredFinalizeFunctions.AppendElement();
    1302           0 :     holder->run = function;
    1303           0 :     holder->data = data;
    1304             : 
    1305           0 :     iter.Remove();
    1306             :   }
    1307           0 : }
    1308             : 
    1309           0 : IncrementalFinalizeRunnable::~IncrementalFinalizeRunnable()
    1310             : {
    1311           0 :   MOZ_ASSERT(this != mRuntime->mFinalizeRunnable);
    1312           0 : }
    1313             : 
    1314             : void
    1315           0 : IncrementalFinalizeRunnable::ReleaseNow(bool aLimited)
    1316             : {
    1317           0 :   if (mReleasing) {
    1318           0 :     NS_WARNING("Re-entering ReleaseNow");
    1319           0 :     return;
    1320             :   }
    1321             :   {
    1322           0 :     mozilla::AutoRestore<bool> ar(mReleasing);
    1323           0 :     mReleasing = true;
    1324           0 :     MOZ_ASSERT(mDeferredFinalizeFunctions.Length() != 0,
    1325             :                "We should have at least ReleaseSliceNow to run");
    1326           0 :     MOZ_ASSERT(mFinalizeFunctionToRun < mDeferredFinalizeFunctions.Length(),
    1327             :                "No more finalizers to run?");
    1328             : 
    1329           0 :     TimeDuration sliceTime = TimeDuration::FromMilliseconds(SliceMillis);
    1330           0 :     TimeStamp started = TimeStamp::Now();
    1331           0 :     bool timeout = false;
    1332           0 :     do {
    1333             :       const DeferredFinalizeFunctionHolder& function =
    1334           0 :         mDeferredFinalizeFunctions[mFinalizeFunctionToRun];
    1335           0 :       if (aLimited) {
    1336           0 :         bool done = false;
    1337           0 :         while (!timeout && !done) {
    1338             :           /*
    1339             :            * We don't want to read the clock too often, so we try to
    1340             :            * release slices of 100 items.
    1341             :            */
    1342           0 :           done = function.run(100, function.data);
    1343           0 :           timeout = TimeStamp::Now() - started >= sliceTime;
    1344             :         }
    1345           0 :         if (done) {
    1346           0 :           ++mFinalizeFunctionToRun;
    1347             :         }
    1348           0 :         if (timeout) {
    1349           0 :           break;
    1350             :         }
    1351             :       } else {
    1352           0 :         while (!function.run(UINT32_MAX, function.data));
    1353           0 :         ++mFinalizeFunctionToRun;
    1354             :       }
    1355           0 :     } while (mFinalizeFunctionToRun < mDeferredFinalizeFunctions.Length());
    1356             :   }
    1357             : 
    1358           0 :   if (mFinalizeFunctionToRun == mDeferredFinalizeFunctions.Length()) {
    1359           0 :     MOZ_ASSERT(mRuntime->mFinalizeRunnable == this);
    1360           0 :     mDeferredFinalizeFunctions.Clear();
    1361             :     // NB: This may delete this!
    1362           0 :     mRuntime->mFinalizeRunnable = nullptr;
    1363             :   }
    1364             : }
    1365             : 
    1366             : NS_IMETHODIMP
    1367           0 : IncrementalFinalizeRunnable::Run()
    1368             : {
    1369           0 :   if (mRuntime->mFinalizeRunnable != this) {
    1370             :     /* These items were already processed synchronously in JSGC_END. */
    1371           0 :     MOZ_ASSERT(!mDeferredFinalizeFunctions.Length());
    1372           0 :     return NS_OK;
    1373             :   }
    1374             : 
    1375           0 :   TimeStamp start = TimeStamp::Now();
    1376           0 :   ReleaseNow(true);
    1377             : 
    1378           0 :   if (mDeferredFinalizeFunctions.Length()) {
    1379           0 :     nsresult rv = NS_DispatchToCurrentThread(this);
    1380           0 :     if (NS_FAILED(rv)) {
    1381           0 :       ReleaseNow(false);
    1382             :     }
    1383             :   }
    1384             : 
    1385           0 :   uint32_t duration = (uint32_t)((TimeStamp::Now() - start).ToMilliseconds());
    1386           0 :   Telemetry::Accumulate(Telemetry::DEFERRED_FINALIZE_ASYNC, duration);
    1387             : 
    1388           0 :   return NS_OK;
    1389             : }
    1390             : 
    1391             : void
    1392           0 : CycleCollectedJSRuntime::FinalizeDeferredThings(CycleCollectedJSContext::DeferredFinalizeType aType)
    1393             : {
    1394             :   /*
    1395             :    * If the previous GC created a runnable to finalize objects
    1396             :    * incrementally, and if it hasn't finished yet, finish it now. We
    1397             :    * don't want these to build up. We also don't want to allow any
    1398             :    * existing incremental finalize runnables to run after a
    1399             :    * non-incremental GC, since they are often used to detect leaks.
    1400             :    */
    1401           0 :   if (mFinalizeRunnable) {
    1402           0 :     mFinalizeRunnable->ReleaseNow(false);
    1403           0 :     if (mFinalizeRunnable) {
    1404             :       // If we re-entered ReleaseNow, we couldn't delete mFinalizeRunnable and
    1405             :       // we need to just continue processing it.
    1406           0 :       return;
    1407             :     }
    1408             :   }
    1409             : 
    1410           0 :   if (mDeferredFinalizerTable.Count() == 0) {
    1411           0 :     return;
    1412             :   }
    1413             : 
    1414             :   mFinalizeRunnable = new IncrementalFinalizeRunnable(this,
    1415           0 :                                                       mDeferredFinalizerTable);
    1416             : 
    1417             :   // Everything should be gone now.
    1418           0 :   MOZ_ASSERT(mDeferredFinalizerTable.Count() == 0);
    1419             : 
    1420           0 :   if (aType == CycleCollectedJSContext::FinalizeIncrementally) {
    1421           0 :     NS_DispatchToCurrentThread(mFinalizeRunnable);
    1422             :   } else {
    1423           0 :     mFinalizeRunnable->ReleaseNow(false);
    1424           0 :     MOZ_ASSERT(!mFinalizeRunnable);
    1425             :   }
    1426             : }
    1427             : 
    1428             : void
    1429           0 : CycleCollectedJSRuntime::AnnotateAndSetOutOfMemory(OOMState* aStatePtr,
    1430             :                                                    OOMState aNewState)
    1431             : {
    1432           0 :   *aStatePtr = aNewState;
    1433             : #ifdef MOZ_CRASHREPORTER
    1434           0 :   CrashReporter::AnnotateCrashReport(aStatePtr == &mOutOfMemoryState
    1435           0 :                                      ? NS_LITERAL_CSTRING("JSOutOfMemory")
    1436           0 :                                      : NS_LITERAL_CSTRING("JSLargeAllocationFailure"),
    1437             :                                      aNewState == OOMState::Reporting
    1438           0 :                                      ? NS_LITERAL_CSTRING("Reporting")
    1439             :                                      : aNewState == OOMState::Reported
    1440           0 :                                      ? NS_LITERAL_CSTRING("Reported")
    1441           0 :                                      : NS_LITERAL_CSTRING("Recovered"));
    1442             : #endif
    1443           0 : }
    1444             : 
    1445             : void
    1446           1 : CycleCollectedJSRuntime::OnGC(JSContext* aContext,
    1447             :                               JSGCStatus aStatus)
    1448             : {
    1449           1 :   switch (aStatus) {
    1450             :     case JSGC_BEGIN:
    1451           1 :       nsCycleCollector_prepareForGarbageCollection();
    1452           1 :       mZonesWaitingForGC.Clear();
    1453           1 :       break;
    1454             :     case JSGC_END: {
    1455             : #ifdef MOZ_CRASHREPORTER
    1456           0 :       if (mOutOfMemoryState == OOMState::Reported) {
    1457           0 :         AnnotateAndSetOutOfMemory(&mOutOfMemoryState, OOMState::Recovered);
    1458             :       }
    1459           0 :       if (mLargeAllocationFailureState == OOMState::Reported) {
    1460           0 :         AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState, OOMState::Recovered);
    1461             :       }
    1462             : #endif
    1463             : 
    1464             :       // Do any deferred finalization of native objects. Normally we do this
    1465             :       // incrementally for an incremental GC, and immediately for a
    1466             :       // non-incremental GC, on the basis that the type of GC reflects how
    1467             :       // urgently resources should be destroyed. However under some circumstances
    1468             :       // (such as in js::InternalCallOrConstruct) we can end up running a
    1469             :       // non-incremental GC when there is a pending exception, and the finalizers
    1470             :       // are not set up to handle that. In that case, just run them later, after
    1471             :       // we've returned to the event loop.
    1472           0 :       bool finalizeIncrementally = JS::WasIncrementalGC(mJSRuntime) || JS_IsExceptionPending(aContext);
    1473           0 :       FinalizeDeferredThings(finalizeIncrementally
    1474             :                              ? CycleCollectedJSContext::FinalizeIncrementally
    1475           0 :                              : CycleCollectedJSContext::FinalizeNow);
    1476             : 
    1477           0 :       break;
    1478             :     }
    1479             :     default:
    1480           0 :       MOZ_CRASH();
    1481             :   }
    1482             : 
    1483           1 :   CustomGCCallback(aStatus);
    1484           1 : }
    1485             : 
    1486             : void
    1487           0 : CycleCollectedJSRuntime::OnOutOfMemory()
    1488             : {
    1489           0 :   AnnotateAndSetOutOfMemory(&mOutOfMemoryState, OOMState::Reporting);
    1490           0 :   CustomOutOfMemoryCallback();
    1491           0 :   AnnotateAndSetOutOfMemory(&mOutOfMemoryState, OOMState::Reported);
    1492           0 : }
    1493             : 
    1494             : void
    1495           0 : CycleCollectedJSRuntime::SetLargeAllocationFailure(OOMState aNewState)
    1496             : {
    1497           0 :   AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState, aNewState);
    1498           0 : }
    1499             : 
    1500             : void
    1501           0 : CycleCollectedJSRuntime::PrepareWaitingZonesForGC()
    1502             : {
    1503           0 :   JSContext* cx = CycleCollectedJSContext::Get()->Context();
    1504           0 :   if (mZonesWaitingForGC.Count() == 0) {
    1505           0 :     JS::PrepareForFullGC(cx);
    1506             :   } else {
    1507           0 :     for (auto iter = mZonesWaitingForGC.Iter(); !iter.Done(); iter.Next()) {
    1508           0 :       JS::PrepareZoneForGC(iter.Get()->GetKey());
    1509             :     }
    1510           0 :     mZonesWaitingForGC.Clear();
    1511             :   }
    1512           0 : }
    1513             : 
    1514             : void
    1515           0 : CycleCollectedJSRuntime::EnvironmentPreparer::invoke(JS::HandleObject scope,
    1516             :                                                      js::ScriptEnvironmentPreparer::Closure& closure)
    1517             : {
    1518           0 :   nsIGlobalObject* global = xpc::NativeGlobal(scope);
    1519             : 
    1520             :   // Not much we can do if we simply don't have a usable global here...
    1521           0 :   NS_ENSURE_TRUE_VOID(global && global->GetGlobalJSObject());
    1522             : 
    1523           0 :   AutoEntryScript aes(global, "JS-engine-initiated execution");
    1524             : 
    1525           0 :   MOZ_ASSERT(!JS_IsExceptionPending(aes.cx()));
    1526             : 
    1527           0 :   DebugOnly<bool> ok = closure(aes.cx());
    1528             : 
    1529           0 :   MOZ_ASSERT_IF(ok, !JS_IsExceptionPending(aes.cx()));
    1530             : 
    1531             :   // The AutoEntryScript will check for JS_IsExceptionPending on the
    1532             :   // JSContext and report it as needed as it comes off the stack.
    1533             : }
    1534             : 
    1535             : /* static */ CycleCollectedJSRuntime*
    1536        3946 : CycleCollectedJSRuntime::Get()
    1537             : {
    1538        3946 :   auto context = CycleCollectedJSContext::Get();
    1539        3946 :   if (context) {
    1540        3946 :     return context->Runtime();
    1541             :   }
    1542           0 :   return nullptr;
    1543             : }

Generated by: LCOV version 1.13