Backed out 3 changesets (bug 1290551, bug 1290550) for Windows (at least) Marionette crashes in AssertNoRootsTracer::onChild
authorPhil Ringnalda <philringnalda@gmail.com>
Thu, 18 Aug 2016 19:30:33 -0700
changeset 310169 888a3be7adce0b75ebe340df28038ee3e7ae729a
parent 310168 1170f54cad96273c30a4062361b8a6041322b8d9
child 310170 748308e48c83a96aa8e18dad709e082669eaf5b3
push id20348
push userryanvm@gmail.com
push dateFri, 19 Aug 2016 13:56:01 +0000
treeherderfx-team@8dfc2fdb7ae3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1290551, 1290550
milestone51.0a1
backs out3ffbd71ee977bb81cdc0d0354f183a33c43a9c1c
77bda0e9caba1f7ed88ced9d289db9b67e7aec12
3763a23b6353eac5801220a94facd551b0192834
Backed out 3 changesets (bug 1290551, bug 1290550) for Windows (at least) Marionette crashes in AssertNoRootsTracer::onChild CLOSED TREE Backed out changeset 3ffbd71ee977 (bug 1290551) Backed out changeset 77bda0e9caba (bug 1290551) Backed out changeset 3763a23b6353 (bug 1290550)
js/src/gc/GCRuntime.h
js/src/gc/Iteration.cpp
js/src/gc/Nursery.cpp
js/src/gc/RootMarking.cpp
js/src/gc/Verifier.cpp
js/src/jit/Ion.cpp
js/src/jsatom.cpp
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsgc.cpp
js/src/jspubtd.h
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
js/src/vm/ScopeObject.cpp
js/src/vm/ScopeObject.h
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -648,18 +648,18 @@ class GCRuntime
 
     void runDebugGC();
     inline void poke();
 
     enum TraceOrMarkRuntime {
         TraceRuntime,
         MarkRuntime
     };
-    void traceRuntime(JSTracer* trc, AutoLockForExclusiveAccess& lock);
-    void traceRuntimeForMinorGC(JSTracer* trc, AutoLockForExclusiveAccess& lock);
+    void markRuntime(JSTracer* trc, TraceOrMarkRuntime traceOrMark,
+                     AutoLockForExclusiveAccess& lock);
 
     void notifyDidPaint();
     void shrinkBuffers();
     void onOutOfMallocMemory();
     void onOutOfMallocMemory(const AutoLockGC& lock);
 
 #ifdef JS_GC_ZEAL
     const void* addressOfZealModeBits() { return &zealModeBits; }
@@ -941,19 +941,16 @@ class GCRuntime
     void incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason reason,
                                  AutoLockForExclusiveAccess& lock);
 
     void pushZealSelectedObjects();
     void purgeRuntime(AutoLockForExclusiveAccess& lock);
     MOZ_MUST_USE bool beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAccess& lock);
     bool shouldPreserveJITCode(JSCompartment* comp, int64_t currentTime,
                                JS::gcreason::Reason reason);
-    void traceRuntimeForMajorGC(JSTracer* trc, AutoLockForExclusiveAccess& lock);
-    void traceRuntimeCommon(JSTracer* trc, TraceOrMarkRuntime traceOrMark,
-                            AutoLockForExclusiveAccess& lock);
     void bufferGrayRoots();
     void markCompartments();
     IncrementalProgress drainMarkStack(SliceBudget& sliceBudget, gcstats::Phase phase);
     template <class CompartmentIterT> void markWeakReferences(gcstats::Phase phase);
     void markWeakReferencesInCurrentGroup(gcstats::Phase phase);
     template <class ZoneIterT, class CompartmentIterT> void markGrayReferences(gcstats::Phase phase);
     void markBufferedGrayRoots(JS::Zone* zone);
     void markGrayReferencesInCurrentGroup(gcstats::Phase phase);
--- a/js/src/gc/Iteration.cpp
+++ b/js/src/gc/Iteration.cpp
@@ -12,16 +12,28 @@
 #include "vm/Runtime.h"
 
 #include "jscntxtinlines.h"
 #include "jsgcinlines.h"
 
 using namespace js;
 using namespace js::gc;
 
+void
+js::TraceRuntime(JSTracer* trc)
+{
+    MOZ_ASSERT(!trc->isMarkingTracer());
+
+    JSRuntime* rt = trc->runtime();
+    rt->gc.evictNursery();
+    AutoPrepareForTracing prep(rt->contextFromMainThread(), WithAtoms);
+    gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_TRACE_HEAP);
+    rt->gc.markRuntime(trc, GCRuntime::TraceRuntime, prep.session().lock);
+}
+
 static void
 IterateCompartmentsArenasCells(JSContext* cx, Zone* zone, void* data,
                                JSIterateCompartmentCallback compartmentCallback,
                                IterateArenaCallback arenaCallback,
                                IterateCellCallback cellCallback)
 {
     for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
         (*compartmentCallback)(cx, data, comp);
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -670,17 +670,17 @@ js::Nursery::doCollection(JSRuntime* rt,
     sb.traceWholeCells(mover);
     maybeEndProfile(ProfileKey::TraceWholeCells);
 
     maybeStartProfile(ProfileKey::TraceGenericEntries);
     sb.traceGenericEntries(&mover);
     maybeEndProfile(ProfileKey::TraceGenericEntries);
 
     maybeStartProfile(ProfileKey::MarkRuntime);
-    rt->gc.traceRuntimeForMinorGC(&mover, session.lock);
+    rt->gc.markRuntime(&mover, GCRuntime::TraceRuntime, session.lock);
     maybeEndProfile(ProfileKey::MarkRuntime);
 
     maybeStartProfile(ProfileKey::MarkDebugger);
     {
         gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_MARK_ROOTS);
         Debugger::markAll(&mover);
     }
     maybeEndProfile(ProfileKey::MarkDebugger);
@@ -780,20 +780,16 @@ js::Nursery::freeMallocedBuffers()
         freeMallocedBuffersTask->runFromMainThread(runtime());
 
     MOZ_ASSERT(mallocedBuffers.empty());
 }
 
 void
 js::Nursery::waitBackgroundFreeEnd()
 {
-    // We may finishRoots before nursery init if runtime init fails.
-    if (!isEnabled())
-        return;
-
     MOZ_ASSERT(freeMallocedBuffersTask);
     freeMallocedBuffersTask->join();
 }
 
 void
 js::Nursery::sweep()
 {
     /* Sweep unique id's in all in-use chunks. */
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -218,18 +218,17 @@ AutoGCRooter::trace(JSTracer* trc)
     MOZ_ASSERT(tag_ >= 0);
     if (Value* vp = static_cast<AutoArrayRooter*>(this)->array)
         TraceRootRange(trc, tag_, vp, "JS::AutoArrayRooter.array");
 }
 
 /* static */ void
 AutoGCRooter::traceAll(JSTracer* trc)
 {
-    for (AutoGCRooter* gcr = trc->runtime()->contextFromMainThread()->roots.autoGCRooters_; gcr; gcr = gcr->down)
-        gcr->trace(trc);
+    traceAllInContext(trc->runtime()->contextFromMainThread(), trc);
 }
 
 /* static */ void
 AutoGCRooter::traceAllWrappers(JSTracer* trc)
 {
     JSContext* cx = trc->runtime()->contextFromMainThread();
 
     for (AutoGCRooter* gcr = cx->roots.autoGCRooters_; gcr; gcr = gcr->down) {
@@ -267,60 +266,30 @@ PropertyDescriptor::trace(JSTracer* trc)
     if ((attrs & JSPROP_SETTER) && setter) {
         JSObject* tmp = JS_FUNC_TO_DATA_PTR(JSObject*, setter);
         TraceRoot(trc, &tmp, "Descriptor::set");
         setter = JS_DATA_TO_FUNC_PTR(JSSetterOp, tmp);
     }
 }
 
 void
-js::gc::GCRuntime::traceRuntimeForMajorGC(JSTracer* trc, AutoLockForExclusiveAccess& lock)
-{
-    traceRuntimeCommon(trc, MarkRuntime, lock);
-}
-
-void
-js::gc::GCRuntime::traceRuntimeForMinorGC(JSTracer* trc, AutoLockForExclusiveAccess& lock)
-{
-    traceRuntimeCommon(trc, TraceRuntime, lock);
-}
-
-void
-js::TraceRuntime(JSTracer* trc)
-{
-    MOZ_ASSERT(!trc->isMarkingTracer());
-
-    JSRuntime* rt = trc->runtime();
-    rt->gc.evictNursery();
-    AutoPrepareForTracing prep(rt->contextFromMainThread(), WithAtoms);
-    gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_TRACE_HEAP);
-    rt->gc.traceRuntime(trc, prep.session().lock);
-}
-
-void
-js::gc::GCRuntime::traceRuntime(JSTracer* trc, AutoLockForExclusiveAccess& lock)
-{
-    traceRuntimeCommon(trc, TraceRuntime, lock);
-}
-
-void
-js::gc::GCRuntime::traceRuntimeCommon(JSTracer* trc, TraceOrMarkRuntime traceOrMark,
-                                      AutoLockForExclusiveAccess& lock)
+js::gc::GCRuntime::markRuntime(JSTracer* trc, TraceOrMarkRuntime traceOrMark,
+                               AutoLockForExclusiveAccess& lock)
 {
     gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_ROOTS);
 
+    MOZ_ASSERT(traceOrMark == TraceRuntime || traceOrMark == MarkRuntime);
+
     MOZ_ASSERT(!rt->mainThread.suppressGC);
 
-    // Trace incoming CCW edges.
     if (traceOrMark == MarkRuntime) {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_CCWS);
         JSCompartment::traceIncomingCrossCompartmentEdgesForZoneGC(trc);
     }
 
-    // Trace C stack roots.
     {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_ROOTERS);
 
         AutoGCRooter::traceAll(trc);
 
         if (!rt->isBeingDestroyed()) {
             MarkExactStackRoots(rt, trc);
             rt->markSelfHostingGlobal(trc);
@@ -329,49 +298,41 @@ js::gc::GCRuntime::traceRuntimeCommon(JS
         for (RootRange r = rootsHash.all(); !r.empty(); r.popFront()) {
             const RootEntry& entry = r.front();
             TraceRoot(trc, entry.key(), entry.value());
         }
 
         MarkPersistentRooted(rt, trc);
     }
 
-    // Trace the atoms Compartment.
     if (!rt->isBeingDestroyed() && !rt->isHeapMinorCollecting()) {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_RUNTIME_DATA);
 
         if (traceOrMark == TraceRuntime || rt->atomsCompartment(lock)->zone()->isCollecting()) {
             MarkPermanentAtoms(trc);
             MarkAtoms(trc, lock);
             MarkWellKnownSymbols(trc);
             jit::JitRuntime::Mark(trc, lock);
         }
     }
 
-    // This table is weak in major GC's.
     if (rt->isHeapMinorCollecting())
         jit::JitRuntime::MarkJitcodeGlobalTableUnconditionally(trc);
 
-    // Trace anything in the single context. Note that this is actually the
-    // same struct as the JSRuntime, but is still split for historical reasons.
     rt->contextFromMainThread()->mark(trc);
 
-    // Trace all compartment roots, but not the compartment itself; it is
-    // marked via the parent pointer if traceRoots actually traces anything.
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
         c->traceRoots(trc, traceOrMark);
 
-    // Trace JS stack roots.
     MarkInterpreterActivations(rt, trc);
+
     jit::MarkJitActivations(rt, trc);
 
-    // Trace SPS.
     rt->spsProfiler.trace(trc);
 
-    // Trace the embeddings black and gray roots.
     if (!rt->isHeapMinorCollecting()) {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_EMBEDDING);
 
         /*
          * The embedding can register additional roots here.
          *
          * We don't need to trace these in a minor GC because all pointers into
          * the nursery should be in the store buffer, and we want to avoid the
@@ -385,61 +346,16 @@ js::gc::GCRuntime::traceRuntimeCommon(JS
         /* During GC, we don't mark gray roots at this stage. */
         if (JSTraceDataOp op = grayRootTracer.op) {
             if (traceOrMark == TraceRuntime)
                 (*op)(trc, grayRootTracer.data);
         }
     }
 }
 
-#ifdef DEBUG
-class AssertNoRootsTracer : public JS::CallbackTracer
-{
-    void onChild(const JS::GCCellPtr& thing) override {
-        MOZ_CRASH("There should not be any roots after finishRoots");
-    }
-
-  public:
-    AssertNoRootsTracer(JSRuntime* rt, WeakMapTraceKind weakTraceKind)
-      : JS::CallbackTracer(rt, weakTraceKind)
-    {}
-};
-#endif // DEBUG
-
-void
-js::gc::GCRuntime::finishRoots()
-{
-    rt->finishAtoms();
-
-    if (rootsHash.initialized())
-        rootsHash.clear();
-
-    rt->contextFromMainThread()->roots.finishPersistentRoots();
-
-    rt->finishSelfHosting();
-
-    for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
-        c->finishRoots();
-
-#ifdef DEBUG
-    // The nsWrapperCache may not be empty before our shutdown GC, so we have
-    // to skip that table when verifying that we are fully unrooted.
-    auto prior = grayRootTracer;
-
-    AssertNoRootsTracer trc(rt, TraceWeakMapKeysValues);
-    AutoPrepareForTracing prep(rt->contextFromMainThread(), WithAtoms);
-    gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_TRACE_HEAP);
-    traceRuntime(&trc, prep.session().lock);
-
-    // Restore the wrapper tracing so that we leak instead of leaving dangling
-    // pointers.
-    grayRootTracer = prior;
-#endif // DEBUG
-}
-
 // Append traced things to a buffer on the zone for use later in the GC.
 // See the comment in GCRuntime.h above grayBufferState for details.
 class BufferGrayRootsTracer : public JS::CallbackTracer
 {
     // Set to false if we OOM while buffering gray roots.
     bool bufferingGrayRootsFailed;
 
     void onChild(const JS::GCCellPtr& thing) override;
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -201,17 +201,17 @@ gc::GCRuntime::startVerifyPreBarriers()
         goto oom;
 
     /* Create the root node. */
     trc->curnode = MakeNode(trc, nullptr, JS::TraceKind(0));
 
     incrementalState = State::MarkRoots;
 
     /* Make all the roots be edges emanating from the root node. */
-    traceRuntime(trc, prep.session().lock);
+    markRuntime(trc, TraceRuntime, prep.session().lock);
 
     VerifyNode* node;
     node = trc->curnode;
     if (trc->edgeptr == trc->term)
         goto oom;
 
     /* For each edge, make a node for it if one doesn't already exist. */
     while ((char*)node < trc->edgeptr) {
@@ -502,19 +502,19 @@ CheckHeapTracer::onChild(const JS::GCCel
     WorkItem item(thing, contextName(), parentIndex);
     if (!stack.append(item))
         oom = true;
 }
 
 bool
 CheckHeapTracer::check(AutoLockForExclusiveAccess& lock)
 {
-    // The analysis thinks that traceRuntime might GC by calling a GC callback.
+    // The analysis thinks that markRuntime might GC by calling a GC callback.
     JS::AutoSuppressGCAnalysis nogc;
-    rt->gc.traceRuntime(this, lock);
+    rt->gc.markRuntime(this, GCRuntime::TraceRuntime, lock);
 
     while (!stack.empty()) {
         WorkItem item = stack.back();
         if (item.processed) {
             stack.popBack();
         } else {
             parentIndex = stack.length() - 1;
             TraceChildren(this, item.thing);
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -608,22 +608,16 @@ jit::LazyLinkTopActivation(JSContext* cx
 
     return calleeScript->baselineOrIonRawPointer();
 }
 
 /* static */ void
 JitRuntime::Mark(JSTracer* trc, AutoLockForExclusiveAccess& lock)
 {
     MOZ_ASSERT(!trc->runtime()->isHeapMinorCollecting());
-
-    // Shared stubs are allocated in the atoms compartment, so do not iterate
-    // them after the atoms heap after it has been "finished."
-    if (trc->runtime()->atomsAreFinished())
-        return;
-
     Zone* zone = trc->runtime()->atomsCompartment(lock)->zone();
     for (auto i = zone->cellIter<JitCode>(); !i.done(); i.next()) {
         JitCode* code = i;
         TraceRoot(trc, &code, "wrapper");
     }
 }
 
 /* static */ void
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -195,20 +195,16 @@ JSRuntime::finishAtoms()
     wellKnownSymbols = nullptr;
     emptyString = nullptr;
 }
 
 void
 js::MarkAtoms(JSTracer* trc, AutoLockForExclusiveAccess& lock)
 {
     JSRuntime* rt = trc->runtime();
-
-    if (rt->atomsAreFinished())
-        return;
-
     for (AtomSet::Enum e(rt->atoms(lock)); !e.empty(); e.popFront()) {
         const AtomStateEntry& entry = e.front();
         if (!entry.isPinned())
             continue;
 
         JSAtom* atom = entry.asPtrUnbarriered();
         TraceRoot(trc, &atom, "interned_atom");
         MOZ_ASSERT(entry.asPtrUnbarriered() == atom);
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -698,37 +698,16 @@ JSCompartment::traceRoots(JSTracer* trc,
 
     if (nonSyntacticLexicalScopes_)
         nonSyntacticLexicalScopes_->trace(trc);
 
     wasm.trace(trc);
 }
 
 void
-JSCompartment::finishRoots()
-{
-    if (watchpointMap)
-        watchpointMap->clear();
-
-    if (debugScopes)
-        debugScopes->finish();
-
-    if (lazyArrayBuffers)
-        lazyArrayBuffers->clear();
-
-    if (objectMetadataTable)
-        objectMetadataTable->clear();
-
-    clearScriptCounts();
-
-    if (nonSyntacticLexicalScopes_)
-        nonSyntacticLexicalScopes_->clear();
-}
-
-void
 JSCompartment::sweepAfterMinorGC()
 {
     globalWriteBarriered = 0;
 
     if (innerViews.needsSweepAfterMinorGC())
         innerViews.sweepAfterMinorGC();
 }
 
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -597,20 +597,16 @@ struct JSCompartment
      */
     void trace(JSTracer* trc);
     /*
      * This method traces JSCompartment-owned GC roots that are considered live
      * regardless of whether the JSCompartment itself is still live.
      */
     void traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime traceOrMark);
     /*
-     * This method clears out tables of roots in preparation for the final GC.
-     */
-    void finishRoots();
-    /*
      * These methods mark pointers that cross compartment boundaries. They are
      * called in per-zone GCs to prevent the wrappers' outgoing edges from
      * dangling (full GCs naturally follow pointers across compartments) and
      * when compacting to update cross-compartment pointers.
      */
     void traceOutgoingCrossCompartmentWrappers(JSTracer* trc);
     static void traceIncomingCrossCompartmentEdgesForZoneGC(JSTracer* trc);
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1111,16 +1111,25 @@ GCRuntime::finish()
     FreeChunkPool(rt, availableChunks_);
     FreeChunkPool(rt, emptyChunks_);
 
     FinishTrace();
 
     nursery.printTotalProfileTimes();
 }
 
+void
+GCRuntime::finishRoots()
+{
+    if (rootsHash.initialized())
+        rootsHash.clear();
+
+    rt->contextFromMainThread()->roots.finishPersistentRoots();
+}
+
 bool
 GCRuntime::setParameter(JSGCParamKey key, uint32_t value, AutoLockGC& lock)
 {
     switch (key) {
       case JSGC_MAX_MALLOC_BYTES:
         setMaxMallocBytes(value);
         for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next())
             zone->setGCMaxMallocBytes(maxMallocBytesAllocated() * 0.9);
@@ -2517,17 +2526,17 @@ GCRuntime::updatePointersToRelocatedCell
 
     // Iterate through all cells that can contain relocatable pointers to update
     // them. Since updating each cell is independent we try to parallelize this
     // as much as possible.
     updateAllCellPointers(&trc, zone);
 
     // Mark roots to update them.
     {
-        traceRuntimeForMajorGC(&trc, lock);
+        markRuntime(&trc, MarkRuntime, lock);
 
         gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_ROOTS);
         Debugger::markAll(&trc);
         Debugger::markIncomingCrossCompartmentEdges(&trc);
 
         WeakMapBase::markAll(zone, &trc);
         for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) {
             c->trace(&trc);
@@ -3879,17 +3888,17 @@ GCRuntime::beginMarkPhase(JS::gcreason::
         }
 
         for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
             /* Unmark all weak maps in the zones being collected. */
             WeakMapBase::unmarkZone(zone);
         }
     }
 
-    traceRuntimeForMajorGC(gcmarker, lock);
+    markRuntime(gcmarker, MarkRuntime, lock);
 
     gcstats::AutoPhase ap2(stats, gcstats::PHASE_MARK_ROOTS);
 
     if (isIncremental) {
         gcstats::AutoPhase ap3(stats, gcstats::PHASE_BUFFER_GRAY_ROOTS);
         bufferGrayRoots();
     }
 
@@ -4164,17 +4173,17 @@ js::gc::MarkingValidator::nonIncremental
 
             MOZ_ASSERT(gcmarker->isDrained());
             gcmarker->reset();
 
             for (auto chunk = gc->allNonEmptyChunks(); !chunk.done(); chunk.next())
                 chunk->bitmap.clear();
         }
 
-        gc->traceRuntimeForMajorGC(gcmarker, lock);
+        gc->markRuntime(gcmarker, GCRuntime::MarkRuntime, lock);
 
         gc->incrementalState = State::Mark;
         auto unlimited = SliceBudget::unlimited();
         MOZ_RELEASE_ASSERT(gc->marker.drainMarkStack(unlimited));
     }
 
     gc->incrementalState = State::Sweep;
     {
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -191,16 +191,23 @@ class JS_PUBLIC_API(AutoGCRooter)
         *stackTop = down;
     }
 
     /* Implemented in gc/RootMarking.cpp. */
     inline void trace(JSTracer* trc);
     static void traceAll(JSTracer* trc);
     static void traceAllWrappers(JSTracer* trc);
 
+    /* T must be a context type */
+    template<typename T>
+    static void traceAllInContext(T* cx, JSTracer* trc) {
+        for (AutoGCRooter* gcr = cx->roots.autoGCRooters_; gcr; gcr = gcr->down)
+            gcr->trace(trc);
+    }
+
   protected:
     AutoGCRooter * const down;
 
     /*
      * Discriminates actual subclass of this being used.  If non-negative, the
      * subclass roots an array of values of the length stored in this field.
      * If negative, meaning is indicated by the corresponding value in the enum
      * below.  Any other negative value indicates some deeper problem such as
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -380,16 +380,32 @@ JSRuntime::destroyRuntime()
          * parse tasks. Waiting for AsmJS and compression tasks is done
          * synchronously (on the main thread or during parse tasks), so no
          * explicit canceling is needed for these.
          */
         for (CompartmentsIter comp(this, SkipAtoms); !comp.done(); comp.next())
             CancelOffThreadIonCompile(comp, nullptr);
         CancelOffThreadParses(this);
 
+        /* Clear debugging state to remove GC roots. */
+        for (CompartmentsIter comp(this, SkipAtoms); !comp.done(); comp.next()) {
+            if (WatchpointMap* wpmap = comp->watchpointMap)
+                wpmap->clear();
+        }
+
+        /*
+         * Clear script counts map, to remove the strong reference on the
+         * JSScript key.
+         */
+        for (CompartmentsIter comp(this, SkipAtoms); !comp.done(); comp.next())
+            comp->clearScriptCounts();
+
+        /* Clear atoms to remove GC roots and heap allocations. */
+        finishAtoms();
+
         /* Remove persistent GC roots. */
         gc.finishRoots();
 
         /*
          * Flag us as being destroyed. This allows the GC to free things like
          * interned atoms and Ion trampolines.
          */
         beingDestroyed_ = true;
@@ -402,16 +418,22 @@ JSRuntime::destroyRuntime()
 
         JS::PrepareForFullGC(contextFromMainThread());
         gc.gc(GC_NORMAL, JS::gcreason::DESTROY_RUNTIME);
     }
 
     MOZ_ASSERT(ionLazyLinkListSize_ == 0);
     MOZ_ASSERT(ionLazyLinkList_.isEmpty());
 
+    /*
+     * Clear the self-hosted global and delete self-hosted classes *after*
+     * GC, as finalizers for objects check for clasp->finalize during GC.
+     */
+    finishSelfHosting();
+
     MOZ_ASSERT(!numExclusiveThreads);
     AutoLockForExclusiveAccess lock(this);
 
     /*
      * Even though all objects in the compartment are dead, we may have keep
      * some filenames around because of gcKeepAtoms.
      */
     FreeScriptData(this, lock);
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1041,17 +1041,16 @@ struct JSRuntime : public JS::shadow::Ru
     // allocated in the atomsCompartment. Reading or writing the symbol
     // registry requires the calling thread to have an ExclusiveContext and
     // hold a lock. Use AutoLockForExclusiveAccess.
     js::SymbolRegistry symbolRegistry_;
 
   public:
     bool initializeAtoms(JSContext* cx);
     void finishAtoms();
-    bool atomsAreFinished() const { return !atoms_; }
 
     void sweepAtoms();
 
     js::AtomSet& atoms(js::AutoLockForExclusiveAccess& lock) {
         return *atoms_;
     }
     JSCompartment* atomsCompartment(js::AutoLockForExclusiveAccess& lock) {
         return atomsCompartment_;
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -2547,22 +2547,16 @@ DebugScopes::sweep(JSRuntime* rt)
 
     /*
      * Scopes can be finalized when a debugger-synthesized ScopeObject is
      * no longer reachable via its DebugScopeObject.
      */
     liveScopes.sweep();
 }
 
-void
-DebugScopes::finish()
-{
-    proxiedScopes.clear();
-}
-
 #ifdef JSGC_HASH_TABLE_CHECKS
 void
 DebugScopes::checkHashTablesAfterMovingGC(JSRuntime* runtime)
 {
     /*
      * This is called at the end of StoreBuffer::mark() to check that our
      * postbarriers have worked and that no hashtable keys (or values) are left
      * pointing into the nursery.
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -1347,17 +1347,16 @@ class DebugScopes
   private:
     bool init();
 
     static DebugScopes* ensureCompartmentData(JSContext* cx);
 
   public:
     void mark(JSTracer* trc);
     void sweep(JSRuntime* rt);
-    void finish();
 #ifdef JS_GC_ZEAL
     void checkHashTablesAfterMovingGC(JSRuntime* rt);
 #endif
 
     static DebugScopeObject* hasDebugScope(JSContext* cx, ScopeObject& scope);
     static bool addDebugScope(JSContext* cx, Handle<ScopeObject*> scope,
                               Handle<DebugScopeObject*> debugScope);