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

Generated by: LCOV version 1.13-14-ga5dd952