Bug 790338 - Make weakmap list per-compartment rather than per-runtime r=billm
authorJon Coppeard <jcoppeard@mozilla.com>
Fri, 02 Nov 2012 18:03:59 +0000
changeset 114210 1bdaf0020ab9c4466a7ddaf9e516a0f544304ca7
parent 114209 344fff01645bd689299effa8d3017f2860e4ca18
child 114211 4c0098a5bd92e81efa77e48716a06ab52bb31b3e
push id18631
push userjcoppeard@mozilla.com
push dateTue, 27 Nov 2012 14:29:04 +0000
treeherdermozilla-inbound@26172ff887ae [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm
bugs790338
milestone20.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 790338 - Make weakmap list per-compartment rather than per-runtime r=billm
accessible/src/msaa/nsAccessNodeWrap.cpp
accessible/src/msaa/nsAccessNodeWrap.h
js/src/jsapi.cpp
js/src/jscntxt.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsgc.cpp
js/src/jsweakmap.cpp
js/src/jsweakmap.h
js/src/vm/Debugger.h
js/src/vm/ScopeObject.cpp
js/src/vm/ScopeObject.h
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -782,17 +782,16 @@ JSRuntime::JSRuntime(JSUseHelperThreads 
     gcHighFrequencyHighLimitBytes(500 * 1024 * 1024),
     gcHighFrequencyHeapGrowthMax(3.0),
     gcHighFrequencyHeapGrowthMin(1.5),
     gcLowFrequencyHeapGrowth(1.5),
     gcDynamicHeapGrowth(false),
     gcDynamicMarkSlice(false),
     gcShouldCleanUpEverything(false),
     gcIsNeeded(0),
-    gcWeakMapList(NULL),
     gcStats(thisFromCtor()),
     gcNumber(0),
     gcStartNumber(0),
     gcIsFull(false),
     gcTriggerReason(gcreason::NO_REASON),
     gcStrictCompartmentChecking(false),
     gcDisableStrictProxyCheckingCount(0),
     gcIncrementalState(gc::NO_INCREMENTAL),
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -651,17 +651,16 @@ struct JSRuntime : js::RuntimeFriendFiel
 
     /*
      * These flags must be kept separate so that a thread requesting a
      * compartment GC doesn't cancel another thread's concurrent request for a
      * full GC.
      */
     volatile uintptr_t  gcIsNeeded;
 
-    js::WeakMapBase     *gcWeakMapList;
     js::gcstats::Statistics gcStats;
 
     /* Incremented on every GC slice. */
     uint64_t            gcNumber;
 
     /* The gcNumber at the time of the most recent GC's first slice. */
     uint64_t            gcStartNumber;
 
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -69,16 +69,17 @@ JSCompartment::JSCompartment(JSRuntime *
     maybeAlive(true),
     lastAnimationTime(0),
     regExps(rt),
     propertyTree(thisForCtor()),
     gcMallocAndFreeBytes(0),
     gcTriggerMallocAndFreeBytes(0),
     gcIncomingGrayPointers(NULL),
     gcLiveArrayBuffers(NULL),
+    gcWeakMapList(NULL),
     gcMallocBytes(0),
     debugModeBits(rt->debugMode ? DebugFromC : 0),
     watchpointMap(NULL),
     scriptCountsMap(NULL),
     debugScriptMap(NULL),
     debugScopes(NULL)
 #ifdef JS_ION
     , ionCompartment_(NULL)
@@ -91,20 +92,17 @@ JSCompartment::~JSCompartment()
 {
 #ifdef JS_ION
     js_delete(ionCompartment_);
 #endif
 
     js_delete(watchpointMap);
     js_delete(scriptCountsMap);
     js_delete(debugScriptMap);
-    if (debugScopes) {
-        debugScopes->finalize(rt);
-        js_delete(debugScopes);
-    }
+    js_delete(debugScopes);
 }
 
 bool
 JSCompartment::init(JSContext *cx)
 {
     /*
      * As a hack, we clear our timezone cache every time we create a new
      * compartment.  This ensures that the cache is always relatively fresh, but
@@ -618,16 +616,19 @@ JSCompartment::sweep(FreeOp *fop, bool r
          * JIT code increments activeUseCount for any RegExpShared used by jit
          * code for the lifetime of the JIT script. Thus, we must perform
          * sweeping after clearing jit code.
          */
         regExps.sweep(rt);
 
         if (debugScopes)
             debugScopes->sweep(rt);
+
+        /* Finalize unreachable (key,value) pairs in all weak maps. */
+        WeakMapBase::sweepCompartment(this);
     }
 
     if (!activeAnalysis && !gcPreserveCode) {
         JS_ASSERT(!types.constrainedOutputs);
         gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_DISCARD_ANALYSIS);
 
         /*
          * Clear the analysis pool, but don't release its data yet. While
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -362,16 +362,19 @@ struct JSCompartment : public js::gc::Gr
      * debugger wrapper objects.  The list link is either in the second extra
      * slot for the former, or a special slot for the latter.
      */
     js::RawObject                gcIncomingGrayPointers;
 
     /* Linked list of live array buffers with >1 view. */
     JSObject                     *gcLiveArrayBuffers;
 
+    /* Linked list of live weakmaps in this compartment. */
+    js::WeakMapBase              *gcWeakMapList;
+
   private:
     /*
      * Malloc counter to measure memory pressure for GC scheduling. It runs from
      * gcMaxMallocBytes down to zero. This counter should be used only when it's
      * not possible to know the size of a free.
      */
     ptrdiff_t                    gcMallocBytes;
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -3378,19 +3378,16 @@ BeginMarkPhase(JSRuntime *rt)
             c->discardJitCode(rt->defaultFreeOp(), false);
         }
     }
 
     GCMarker *gcmarker = &rt->gcMarker;
 
     rt->gcStartNumber = rt->gcNumber;
 
-    /* Reset weak map list. */
-    WeakMapBase::resetWeakMapList(rt);
-
     /*
      * We must purge the runtime at the beginning of an incremental GC. The
      * danger if we purge later is that the snapshot invariant of incremental
      * GC will be broken, as follows. If some object is reachable only through
      * some cache (say the dtoaCache) then it will not be part of the snapshot.
      * If we purge after root marking, then the mutator could obtain a pointer
      * to the object and start using it. This object might never be marked, so
      * a GC hazard would exist.
@@ -3401,20 +3398,24 @@ BeginMarkPhase(JSRuntime *rt)
     }
 
     /*
      * Mark phase.
      */
     gcstats::AutoPhase ap1(rt->gcStats, gcstats::PHASE_MARK);
     gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_MARK_ROOTS);
 
-    /* Unmark everything in the compartments being collected. */
-    for (GCCompartmentsIter c(rt); !c.done(); c.next())
+    for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
+        /* Unmark everything in the compartments being collected. */
         c->arenas.unmarkAll();
 
+        /* Reset weak map list for the compartments being collected. */
+        WeakMapBase::resetCompartmentWeakMapList(c);
+    }
+
     MarkRuntime(gcmarker);
 
     /*
      * This code ensures that if a compartment is "dead", then it will be
      * collected in this GC. A compartment is considered dead if its maybeAlive
      * flag is false. The maybeAlive flag is set if:
      *   (1) the compartment has incoming cross-compartment edges, or
      *   (2) an object in the compartment was marked during root marking, either
@@ -3461,26 +3462,36 @@ BeginMarkPhase(JSRuntime *rt)
 
     for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
         if (!c->maybeAlive)
             c->scheduledForDestruction = true;
     }
     rt->gcFoundBlackGrayEdges = false;
 }
 
+bool
+MarkWeakMapsIteratively(JSRuntime *rt)
+{
+    bool markedAny = false;
+    GCMarker *gcmarker = &rt->gcMarker;
+    for (GCCompartmentGroupIter c(rt); !c.done(); c.next())
+        markedAny |= WeakMapBase::markCompartmentIteratively(c, gcmarker);
+    return markedAny;
+}
+
 void
 MarkWeakReferences(JSRuntime *rt, gcstats::Phase phase)
 {
     GCMarker *gcmarker = &rt->gcMarker;
     JS_ASSERT(gcmarker->isDrained());
 
     gcstats::AutoPhase ap(rt->gcStats, phase);
 
     while (WatchpointMap::markAllIteratively(gcmarker) ||
-           WeakMapBase::markAllIteratively(gcmarker) ||
+           MarkWeakMapsIteratively(rt) ||
            Debugger::markAllIteratively(gcmarker))
     {
         SliceBudget budget;
         gcmarker->drainMarkStack(budget);
     }
     JS_ASSERT(gcmarker->isDrained());
 }
 
@@ -3532,33 +3543,34 @@ ValidateIncrementalMarking(JSRuntime *rt
         if (!entry)
             return;
 
         memcpy(entry, bitmap->bitmap, sizeof(bitmap->bitmap));
         if (!map.putNew(r.front(), entry))
             return;
     }
 
-    /* Save the existing weakmaps. */
+    /* Save and reset the lists of live weakmaps for the compartments we are collecting. */
     WeakMapVector weakmaps;
-    if (!WeakMapBase::saveWeakMapList(rt, weakmaps))
-        return;
+    for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
+        if (!WeakMapBase::saveCompartmentWeakMapList(c, weakmaps))
+            return;
+    }
+    for (GCCompartmentsIter c(rt); !c.done(); c.next())
+        WeakMapBase::resetCompartmentWeakMapList(c);
 
     /*
      * After this point, the function should run to completion, so we shouldn't
      * do anything fallible.
      */
 
     /* Re-do all the marking, but non-incrementally. */
     js::gc::State state = rt->gcIncrementalState;
     rt->gcIncrementalState = MARK_ROOTS;
 
-    /* As we're re-doing marking, we need to reset the weak map list. */
-    WeakMapBase::resetWeakMapList(rt);
-
     JS_ASSERT(gcmarker->isDrained());
     gcmarker->reset();
 
     for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront())
         r.front()->bitmap.clear();
 
     MarkRuntime(gcmarker, true);
 
@@ -3610,19 +3622,20 @@ ValidateIncrementalMarking(JSRuntime *rt
 
                 thing += Arena::thingSize(kind);
             }
         }
 
         memcpy(bitmap->bitmap, incBitmap.bitmap, sizeof(incBitmap.bitmap));
     }
 
-    /* Restore the weak map list. */
-    WeakMapBase::resetWeakMapList(rt);
-    WeakMapBase::restoreWeakMapList(rt, weakmaps);
+    /* Restore the weak map lists. */
+    for (GCCompartmentsIter c(rt); !c.done(); c.next())
+        WeakMapBase::resetCompartmentWeakMapList(c);
+    WeakMapBase::restoreCompartmentWeakMapLists(weakmaps);
 
     rt->gcIncrementalState = state;
 }
 
 #endif
 
 static void
 DropStringWrappers(JSRuntime *rt)
@@ -3928,19 +3941,16 @@ BeginSweepingCompartmentGroup(JSRuntime 
             rt->gcFinalizeCallback(&fop, JSFINALIZE_GROUP_START, !rt->gcIsFull /* unused */);
     }
 
     if (sweepingAtoms) {
         gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP_ATOMS);
         SweepAtoms(rt);
     }
 
-    /* Finalize unreachable (key,value) pairs in all weak maps. */
-    WeakMapBase::sweepAll(&rt->gcMarker);
-
     /* Prune out dead views from ArrayBuffer's view lists. */
     for (GCCompartmentGroupIter c(rt); !c.done(); c.next())
         ArrayBufferObject::sweep(c);
 
     /* Collect watch points associated with unreachable objects. */
     WatchpointMap::sweepAll(rt);
 
     /* Detach unreachable debuggers and global objects from each other. */
--- a/js/src/jsweakmap.cpp
+++ b/js/src/jsweakmap.cpp
@@ -16,86 +16,100 @@
 #include "gc/Marking.h"
 #include "vm/GlobalObject.h"
 
 #include "jsgcinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 
+WeakMapBase::WeakMapBase(JSObject *memOf, JSCompartment *c)
+  : memberOf(memOf),
+    compartment(c),
+    next(WeakMapNotInList)
+{
+    JS_ASSERT_IF(memberOf, memberOf->compartment() == c);
+}
+
+WeakMapBase::~WeakMapBase()
+{
+    JS_ASSERT(next == WeakMapNotInList);
+}
+
 bool
-WeakMapBase::markAllIteratively(JSTracer *tracer)
+WeakMapBase::markCompartmentIteratively(JSCompartment *c, JSTracer *tracer)
 {
     bool markedAny = false;
-    JSRuntime *rt = tracer->runtime;
-    for (WeakMapBase *m = rt->gcWeakMapList; m; m = m->next) {
+    for (WeakMapBase *m = c->gcWeakMapList; m; m = m->next) {
         if (m->markIteratively(tracer))
             markedAny = true;
     }
     return markedAny;
 }
 
 void
-WeakMapBase::sweepAll(JSTracer *tracer)
+WeakMapBase::sweepCompartment(JSCompartment *c)
 {
-    JSRuntime *rt = tracer->runtime;
-    for (WeakMapBase *m = rt->gcWeakMapList; m; m = m->next)
-        m->sweep(tracer);
+    for (WeakMapBase *m = c->gcWeakMapList; m; m = m->next)
+        m->sweep();
 }
 
 void
 WeakMapBase::traceAllMappings(WeakMapTracer *tracer)
 {
     JSRuntime *rt = tracer->runtime;
-    for (WeakMapBase *m = rt->gcWeakMapList; m; m = m->next)
-        m->traceMappings(tracer);
+    for (CompartmentsIter c(rt); !c.done(); c.next()) {
+        for (WeakMapBase *m = c->gcWeakMapList; m; m = m->next)
+            m->traceMappings(tracer);
+    }
 }
 
 void
-WeakMapBase::resetWeakMapList(JSRuntime *rt)
+WeakMapBase::resetCompartmentWeakMapList(JSCompartment *c)
 {
     JS_ASSERT(WeakMapNotInList != NULL);
 
-    WeakMapBase *m = rt->gcWeakMapList;
-    rt->gcWeakMapList = NULL;
+    WeakMapBase *m = c->gcWeakMapList;
+    c->gcWeakMapList = NULL;
     while (m) {
         WeakMapBase *n = m->next;
         m->next = WeakMapNotInList;
         m = n;
     }
 }
 
 bool
-WeakMapBase::saveWeakMapList(JSRuntime *rt, WeakMapVector &vector)
+WeakMapBase::saveCompartmentWeakMapList(JSCompartment *c, WeakMapVector &vector)
 {
-    WeakMapBase *m = rt->gcWeakMapList;
+    WeakMapBase *m = c->gcWeakMapList;
     while (m) {
         if (!vector.append(m))
             return false;
         m = m->next;
     }
     return true;
 }
 
 void
-WeakMapBase::restoreWeakMapList(JSRuntime *rt, WeakMapVector &vector)
+WeakMapBase::restoreCompartmentWeakMapLists(WeakMapVector &vector)
 {
-    JS_ASSERT(!rt->gcWeakMapList);
     for (WeakMapBase **p = vector.begin(); p != vector.end(); p++) {
         WeakMapBase *m = *p;
         JS_ASSERT(m->next == WeakMapNotInList);
-        m->next = rt->gcWeakMapList;
-        rt->gcWeakMapList = m;
+        JSCompartment *c = m->compartment;
+        m->next = c->gcWeakMapList;
+        c->gcWeakMapList = m;
     }
 }
 
 void
-WeakMapBase::removeWeakMapFromList(JSRuntime *rt, WeakMapBase *weakmap)
+WeakMapBase::removeWeakMapFromList(WeakMapBase *weakmap)
 {
-    for (WeakMapBase **p = &rt->gcWeakMapList; *p; p = &(*p)->next) {
+    JSCompartment *c = weakmap->compartment;
+    for (WeakMapBase **p = &c->gcWeakMapList; *p; p = &(*p)->next) {
         if (*p == weakmap) {
             *p = (*p)->next;
             weakmap->next = WeakMapNotInList;
             break;
         }
     }
 }
 
--- a/js/src/jsweakmap.h
+++ b/js/src/jsweakmap.h
@@ -5,17 +5,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jsweakmap_h___
 #define jsweakmap_h___
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
-#include "jscntxt.h"
+#include "jscompartment.h"
 #include "jsobj.h"
 
 #include "gc/Marking.h"
 #include "js/HashTable.h"
 
 namespace js {
 
 // A subclass template of js::HashMap whose keys and values may be garbage-collected. When
@@ -36,102 +36,106 @@ namespace js {
 static WeakMapBase * const WeakMapNotInList = reinterpret_cast<WeakMapBase *>(1);
 
 typedef Vector<WeakMapBase *, 0, SystemAllocPolicy> WeakMapVector;
 
 // Common base class for all WeakMap specializations. The collector uses this to call
 // their markIteratively and sweep methods.
 class WeakMapBase {
   public:
-    WeakMapBase(JSObject *memOf) : memberOf(memOf), next(WeakMapNotInList) { }
-    virtual ~WeakMapBase() { }
+    WeakMapBase(JSObject *memOf, JSCompartment *c);
+    virtual ~WeakMapBase();
 
     void trace(JSTracer *tracer) {
         if (IS_GC_MARKING_TRACER(tracer)) {
             // We don't do anything with a WeakMap at trace time. Rather, we wait until as
             // many keys as possible have been marked, and add ourselves to the list of
             // known-live WeakMaps to be scanned in the iterative marking phase, by
             // markAllIteratively.
             JS_ASSERT(!tracer->eagerlyTraceWeakMaps);
 
             // Add ourselves to the list if we are not already in the list. We can already
             // be in the list if the weak map is marked more than once due delayed marking.
             if (next == WeakMapNotInList) {
-                JSRuntime *rt = tracer->runtime;
-                next = rt->gcWeakMapList;
-                rt->gcWeakMapList = this;
+                next = compartment->gcWeakMapList;
+                compartment->gcWeakMapList = this;
             }
         } else {
             // If we're not actually doing garbage collection, the keys won't be marked
             // nicely as needed by the true ephemeral marking algorithm --- custom tracers
             // such as the cycle collector must use their own means for cycle detection.
             // So here we do a conservative approximation: pretend all keys are live.
             if (tracer->eagerlyTraceWeakMaps)
                 nonMarkingTrace(tracer);
         }
     }
 
     // Garbage collector entry points.
 
-    // Check all weak maps that have been marked as live so far in this garbage
+    // Check all weak maps in a compartment that have been marked as live in this garbage
     // collection, and mark the values of all entries that have become strong references
     // to them. Return true if we marked any new values, indicating that we need to make
     // another pass. In other words, mark my marked maps' marked members' mid-collection.
-    static bool markAllIteratively(JSTracer *tracer);
+    static bool markCompartmentIteratively(JSCompartment *c, JSTracer *tracer);
 
-    // Remove entries whose keys are dead from all weak maps marked as live in this
-    // garbage collection.
-    static void sweepAll(JSTracer *tracer);
+    // Remove entries whose keys are dead from all weak maps in a compartment marked as
+    // live in this garbage collection.
+    static void sweepCompartment(JSCompartment *c);
 
     // Trace all delayed weak map bindings. Used by the cycle collector.
     static void traceAllMappings(WeakMapTracer *tracer);
 
     void check() { JS_ASSERT(next == WeakMapNotInList); }
 
-    // Remove everything from the live weak map list.
-    static void resetWeakMapList(JSRuntime *rt);
+    // Remove everything from the weak map list for a compartment.
+    static void resetCompartmentWeakMapList(JSCompartment *c);
+
+    // Save the live weak map list for a compartment, appending the data to a vector.
+    static bool saveCompartmentWeakMapList(JSCompartment *c, WeakMapVector &vector);
 
-    // Save and restore the live weak map list to a vector.
-    static bool saveWeakMapList(JSRuntime *rt, WeakMapVector &vector);
-    static void restoreWeakMapList(JSRuntime *rt, WeakMapVector &vector);
+    // Restore live weak map lists for multiple compartments from a vector.
+    static void restoreCompartmentWeakMapLists(WeakMapVector &vector);
 
-    // Remove a weakmap from the live weakmap list
-    static void removeWeakMapFromList(JSRuntime *rt, WeakMapBase *weakmap);
+    // Remove a weakmap from the live weakmaps list
+    static void removeWeakMapFromList(WeakMapBase *weakmap);
 
   protected:
     // Instance member functions called by the above. Instantiations of WeakMap override
     // these with definitions appropriate for their Key and Value types.
     virtual void nonMarkingTrace(JSTracer *tracer) = 0;
     virtual bool markIteratively(JSTracer *tracer) = 0;
-    virtual void sweep(JSTracer *tracer) = 0;
+    virtual void sweep() = 0;
     virtual void traceMappings(WeakMapTracer *tracer) = 0;
 
     // Object that this weak map is part of, if any.
     JSObject *memberOf;
 
+    // Compartment that this weak map is part of.
+    JSCompartment *compartment;
+
   private:
     // Link in a list of WeakMaps to mark iteratively and sweep in this garbage
-    // collection, headed by JSRuntime::gcWeakMapList. The last element of the list
-    // has NULL as its next. Maps not in the list have WeakMapNotInList as their
-    // next.  We must distinguish these cases to avoid creating infinite lists
-    // when a weak map gets traced twice due to delayed marking.
+    // collection, headed by JSCompartment::gcWeakMapList. The last element of
+    // the list has NULL as its next. Maps not in the list have WeakMapNotInList
+    // as their next.  We must distinguish these cases to avoid creating
+    // infinite lists when a weak map gets traced twice due to delayed marking.
     WeakMapBase *next;
 };
 
 template <class Key, class Value,
           class HashPolicy = DefaultHasher<Key> >
 class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, public WeakMapBase
 {
   public:
     typedef HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy> Base;
     typedef typename Base::Enum Enum;
     typedef typename Base::Range Range;
 
-    explicit WeakMap(JSRuntime *rt, JSObject *memOf=NULL) : Base(rt), WeakMapBase(memOf) { }
-    explicit WeakMap(JSContext *cx, JSObject *memOf=NULL) : Base(cx), WeakMapBase(memOf) { }
+    explicit WeakMap(JSContext *cx, JSObject *memOf=NULL)
+        : Base(cx), WeakMapBase(memOf, cx->compartment) { }
 
   private:
     bool markValue(JSTracer *trc, Value *x) {
         if (gc::IsMarked(x))
             return false;
         gc::Mark(trc, x, "WeakMap entry");
         JS_ASSERT(gc::IsMarked(x));
         return true;
@@ -175,17 +179,17 @@ class WeakMap : public HashMap<Key, Valu
                     e.rekeyFront(e.front().key);
                 gc::Mark(trc, &e.front().value, "WeakMap entry");
                 markedAny = true;
             }
         }
         return markedAny;
     }
 
-    void sweep(JSTracer *trc) {
+    void sweep() {
         /* Remove all entries whose keys remain unmarked. */
         for (Enum e(*this); !e.empty(); e.popFront()) {
             Key k(e.front().key);
             if (gc::IsAboutToBeFinalized(&k))
                 e.removeFront();
         }
         /*
          * Once we've swept, all remaining edges should stay within the
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -50,18 +50,16 @@ class DebuggerWeakMap : private WeakMap<
                     DefaultHasher<JSCompartment *>,
                     RuntimeAllocPolicy> CountMap;
 
     JSCompartment *valueCompartment;
     CountMap compartmentCounts;
 
   public:
     typedef WeakMap<Key, Value, DefaultHasher<Key> > Base;
-    explicit DebuggerWeakMap(JSRuntime *rt)
-        : Base(rt), valueCompartment(NULL), compartmentCounts(rt) { }
     explicit DebuggerWeakMap(JSContext *cx)
         : Base(cx), valueCompartment(NULL), compartmentCounts(cx) { }
 
   public:
     /* Expose those parts of HashMap public interface that are used by Debugger methods. */
 
     typedef typename Base::Ptr Ptr;
     typedef typename Base::AddPtr AddPtr;
@@ -123,17 +121,17 @@ class DebuggerWeakMap : private WeakMap<
         if (!p)
             return;
         JS_ASSERT(p->value > 0);
         finder.addEdgeTo(valueCompartment);
     }
 
   private:
     /* Override sweep method to also update our edge cache. */
-    void sweep(JSTracer *trc) {
+    void sweep() {
         for (Enum e(*static_cast<Base *>(this)); !e.empty(); e.popFront()) {
             Key k(e.front().key);
             Value v(e.front().value);
             if (gc::IsAboutToBeFinalized(&k)) {
                 e.removeFront();
                 decCompartmentCount(k->compartment());
             }
         }
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -1525,37 +1525,32 @@ DebugScopes::DebugScopes(JSContext *cx)
  : proxiedScopes(cx),
    missingScopes(cx),
    liveScopes(cx)
 {}
 
 DebugScopes::~DebugScopes()
 {
     JS_ASSERT(missingScopes.empty());
+    WeakMapBase::removeWeakMapFromList(&proxiedScopes);
 }
 
 bool
 DebugScopes::init()
 {
     if (!liveScopes.init() ||
         !proxiedScopes.init() ||
         !missingScopes.init())
     {
         return false;
     }
     return true;
 }
 
 void
-DebugScopes::finalize(JSRuntime *rt)
-{
-    WeakMapBase::removeWeakMapFromList(rt, &proxiedScopes);
-}
-
-void
 DebugScopes::mark(JSTracer *trc)
 {
     proxiedScopes.trace(trc);
 }
 
 void
 DebugScopes::sweep(JSRuntime *rt)
 {
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -563,18 +563,16 @@ class DebugScopes
                     DefaultHasher<ScopeObject *>,
                     RuntimeAllocPolicy> LiveScopeMap;
     LiveScopeMap liveScopes;
 
   public:
     DebugScopes(JSContext *c);
     ~DebugScopes();
 
-    void finalize(JSRuntime *rt);
-
   private:
     bool init();
 
     static DebugScopes *ensureCompartmentData(JSContext *cx);
 
   public:
     void mark(JSTracer *trc);
     void sweep(JSRuntime *rt);