Backed out 5 changesets (bug 1212624) for breaking stuff.
authorSteve Fink <sfink@mozilla.com>
Wed, 14 Oct 2015 16:49:12 -0700
changeset 302952 7c0c549a1a102fca547166ab79d91a62ab76a7d4
parent 302951 cf5ffa45a4a28e40a98d393dcb13b31601f60ea4
child 302953 de3d8a5f653738926bea21fc41f9166773919496
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1212624
milestone44.0a1
backs outcf5ffa45a4a28e40a98d393dcb13b31601f60ea4
0d7a968d2d6453fb32fe84055efda343690c0427
379edefa8e4713bc7953dc1a1fd34c69451eed60
f73fca35daadf048671f3222c1fa803696956c76
4f499d30a0e08892a49d38cbf5cfe1cfd94b9b20
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
Backed out 5 changesets (bug 1212624) for breaking stuff. Backed out changeset cf5ffa45a4a2 Backed out changeset 0d7a968d2d64 (bug 1212624) Backed out changeset 379edefa8e47 (bug 1212624) Backed out changeset f73fca35daad (bug 1212624) Backed out changeset 4f499d30a0e0 (bug 1212624)
js/src/gc/Marking.cpp
js/src/gc/RootMarking.cpp
js/src/gc/Zone.cpp
js/src/gc/Zone.h
js/src/jsweakmap.cpp
js/src/jsweakmap.h
js/src/vm/Debugger.cpp
js/src/vm/Debugger.h
js/src/vm/UnboxedObject.cpp
js/src/vm/WeakMapPtr.cpp
mfbt/LinkedList.h
mfbt/tests/TestLinkedList.cpp
mfbt/tests/moz.build
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1770,17 +1770,17 @@ GCMarker::enterWeakMarkingMode()
         return;
 
     // During weak marking mode, we maintain a table mapping weak keys to
     // entries in known-live weakmaps.
     if (weakMapAction() == ExpandWeakMaps) {
         tag_ = TracerKindTag::WeakMarking;
 
         for (GCZoneGroupIter zone(runtime()); !zone.done(); zone.next()) {
-            for (WeakMapBase* m : zone->gcWeakMapList) {
+            for (WeakMapBase* m = zone->gcWeakMapList; m; m = m->next) {
                 if (m->marked)
                     m->markEphemeronEntries(this);
             }
         }
     }
 }
 
 void
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -215,17 +215,17 @@ struct PersistentRootedMarker
     typedef PersistentRooted<T> Element;
     typedef mozilla::LinkedList<Element> List;
     typedef void (*MarkFunc)(JSTracer* trc, T* ref, const char* name);
 
     template <TraceFunction<T> TraceFn = TraceNullableRoot>
     static void
     markChain(JSTracer* trc, List& list, const char* name)
     {
-        for (Element* r : list)
+        for (Element* r = list.getFirst(); r; r = r->getNext())
             TraceFn(trc, r->address(), name);
     }
 };
 
 } // namespace gc
 } // namespace js
 
 void
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -22,16 +22,17 @@ using namespace js::gc;
 
 Zone * const Zone::NotOnList = reinterpret_cast<Zone*>(1);
 
 JS::Zone::Zone(JSRuntime* rt)
   : JS::shadow::Zone(rt, &rt->gc.marker),
     debuggers(nullptr),
     arenas(rt),
     types(this),
+    gcWeakMapList(nullptr),
     compartments(),
     gcGrayRoots(),
     gcMallocBytes(0),
     gcMallocGCTriggered(false),
     usage(&rt->gc.usage),
     gcDelayBytes(0),
     data(nullptr),
     isSystem(false),
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -278,18 +278,18 @@ struct Zone : public JS::shadow::Zone,
             oomUnsafe.crash("Zone::enqueueForPromotionToTenuredLogging");
     }
     void logPromotionsToTenured();
 
     js::gc::ArenaLists arenas;
 
     js::TypeZone types;
 
-    /* Live weakmaps in this zone. */
-    mozilla::LinkedList<js::WeakMapBase> gcWeakMapList;
+    /* Linked list of live weakmaps in this zone. */
+    js::WeakMapBase* gcWeakMapList;
 
     // The set of compartments in this zone.
     typedef js::Vector<JSCompartment*, 1, js::SystemAllocPolicy> CompartmentVector;
     CompartmentVector compartments;
 
     // This zone's gray roots.
     typedef js::Vector<js::gc::Cell*, 0, js::SystemAllocPolicy> GrayRootVector;
     GrayRootVector gcGrayRoots;
--- a/js/src/jsweakmap.cpp
+++ b/js/src/jsweakmap.cpp
@@ -20,24 +20,28 @@
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::gc;
 
 WeakMapBase::WeakMapBase(JSObject* memOf, Zone* zone)
   : memberOf(memOf),
     zone(zone),
+    next(WeakMapNotInList),
     marked(false)
 {
     MOZ_ASSERT_IF(memberOf, memberOf->compartment()->zone() == zone);
 }
 
 WeakMapBase::~WeakMapBase()
 {
     MOZ_ASSERT(CurrentThreadIsGCSweeping() || CurrentThreadIsHandlingInitFailure());
+    MOZ_ASSERT_IF(CurrentThreadIsGCSweeping(), !isInList());
+    if (isInList())
+        removeWeakMapFromList(this);
 }
 
 void
 WeakMapBase::trace(JSTracer* tracer)
 {
     MOZ_ASSERT(isInList());
     if (tracer->isMarkingTracer()) {
         marked = true;
@@ -62,90 +66,94 @@ WeakMapBase::trace(JSTracer* tracer)
         if (tracer->weakMapAction() == TraceWeakMapKeysValues)
             nonMarkingTraceKeys(tracer);
     }
 }
 
 void
 WeakMapBase::unmarkZone(JS::Zone* zone)
 {
-    for (WeakMapBase* m : zone->gcWeakMapList)
+    for (WeakMapBase* m = zone->gcWeakMapList; m; m = m->next)
         m->marked = false;
 }
 
 void
 WeakMapBase::markAll(JS::Zone* zone, JSTracer* tracer)
 {
     MOZ_ASSERT(tracer->weakMapAction() != DoNotTraceWeakMaps);
-    for (WeakMapBase* m : zone->gcWeakMapList) {
+    for (WeakMapBase* m = zone->gcWeakMapList; m; m = m->next) {
         m->trace(tracer);
         if (m->memberOf)
             TraceEdge(tracer, &m->memberOf, "memberOf");
     }
 }
 
 bool
 WeakMapBase::markZoneIteratively(JS::Zone* zone, JSTracer* tracer)
 {
     bool markedAny = false;
-    for (WeakMapBase* m : zone->gcWeakMapList) {
+    for (WeakMapBase* m = zone->gcWeakMapList; m; m = m->next) {
         if (m->marked && m->markIteratively(tracer))
             markedAny = true;
     }
     return markedAny;
 }
 
 bool
 WeakMapBase::findInterZoneEdges(JS::Zone* zone)
 {
-    for (WeakMapBase* m : zone->gcWeakMapList) {
+    for (WeakMapBase* m = zone->gcWeakMapList; m; m = m->next) {
         if (!m->findZoneEdges())
             return false;
     }
     return true;
 }
 
 void
 WeakMapBase::sweepZone(JS::Zone* zone)
 {
-    for (WeakMapBase* m = zone->gcWeakMapList.getFirst(); m; ) {
-        WeakMapBase* next = m->getNext();
+    WeakMapBase** tailPtr = &zone->gcWeakMapList;
+    for (WeakMapBase* m = zone->gcWeakMapList; m; ) {
+        WeakMapBase* next = m->next;
         if (m->marked) {
             m->sweep();
+            *tailPtr = m;
+            tailPtr = &m->next;
         } else {
             /* Destroy the hash map now to catch any use after this point. */
             m->finish();
-            m->removeFrom(zone->gcWeakMapList);
+            m->next = WeakMapNotInList;
         }
         m = next;
     }
+    *tailPtr = nullptr;
 
 #ifdef DEBUG
-    for (WeakMapBase* m : zone->gcWeakMapList)
+    for (WeakMapBase* m = zone->gcWeakMapList; m; m = m->next)
         MOZ_ASSERT(m->isInList() && m->marked);
 #endif
 }
 
 void
 WeakMapBase::traceAllMappings(WeakMapTracer* tracer)
 {
     JSRuntime* rt = tracer->runtime;
     for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
-        for (WeakMapBase* m : zone->gcWeakMapList) {
+        for (WeakMapBase* m = zone->gcWeakMapList; m; m = m->next) {
             // The WeakMapTracer callback is not allowed to GC.
             JS::AutoSuppressGCAnalysis nogc;
             m->traceMappings(tracer);
         }
     }
 }
 
 bool
 WeakMapBase::saveZoneMarkedWeakMaps(JS::Zone* zone, WeakMapSet& markedWeakMaps)
 {
-    for (WeakMapBase* m : zone->gcWeakMapList) {
+    for (WeakMapBase* m = zone->gcWeakMapList; m; m = m->next) {
         if (m->marked && !markedWeakMaps.put(m))
             return false;
     }
     return true;
 }
 
 void
 WeakMapBase::restoreMarkedWeakMaps(WeakMapSet& markedWeakMaps)
@@ -153,16 +161,29 @@ WeakMapBase::restoreMarkedWeakMaps(WeakM
     for (WeakMapSet::Range r = markedWeakMaps.all(); !r.empty(); r.popFront()) {
         WeakMapBase* map = r.front();
         MOZ_ASSERT(map->zone->isGCMarking());
         MOZ_ASSERT(!map->marked);
         map->marked = true;
     }
 }
 
+void
+WeakMapBase::removeWeakMapFromList(WeakMapBase* weakmap)
+{
+    JS::Zone* zone = weakmap->zone;
+    for (WeakMapBase** p = &zone->gcWeakMapList; *p; p = &(*p)->next) {
+        if (*p == weakmap) {
+            *p = (*p)->next;
+            weakmap->next = WeakMapNotInList;
+            break;
+        }
+    }
+}
+
 bool
 ObjectValueMap::findZoneEdges()
 {
     /*
      * For unmarked weakmap keys with delegates in a different zone, add a zone
      * edge to ensure that the delegate zone finishes marking before the key
      * zone.
      */
@@ -191,16 +212,21 @@ ObjectWeakMap::ObjectWeakMap(JSContext* 
 {}
 
 bool
 ObjectWeakMap::init()
 {
     return map.init();
 }
 
+ObjectWeakMap::~ObjectWeakMap()
+{
+    WeakMapBase::removeWeakMapFromList(&map);
+}
+
 JSObject*
 ObjectWeakMap::lookup(const JSObject* obj)
 {
     MOZ_ASSERT(map.initialized());
     if (ObjectValueMap::Ptr p = map.lookup(const_cast<JSObject*>(obj)))
         return &p->value().toObject();
     return nullptr;
 }
--- a/js/src/jsweakmap.h
+++ b/js/src/jsweakmap.h
@@ -2,17 +2,16 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * 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 "mozilla/LinkedList.h"
 #include "mozilla/Move.h"
 
 #include "jscompartment.h"
 #include "jsfriendapi.h"
 #include "jsobj.h"
 
 #include "gc/Marking.h"
 #include "gc/StoreBuffer.h"
@@ -31,22 +30,24 @@ class WeakMapBase;
 //     is collected. If an entry is not collected, it remains in the WeakMap and it has a
 //     strong reference to the value.
 //
 // You must call this table's 'trace' method when the object of which it is a part is
 // reached by the garbage collection tracer. Once a table is known to be live, the
 // implementation takes care of the iterative marking needed for weak tables and removing
 // table entries when collection is complete.
 
+// The value for the next pointer for maps not in the map list.
+static WeakMapBase * const WeakMapNotInList = reinterpret_cast<WeakMapBase*>(1);
+
 typedef HashSet<WeakMapBase*, DefaultHasher<WeakMapBase*>, SystemAllocPolicy> WeakMapSet;
 
 // Common base class for all WeakMap specializations. The collector uses this to call
 // their markIteratively and sweep methods.
-class WeakMapBase : public mozilla::LinkedListElement<WeakMapBase>
-{
+class WeakMapBase {
     friend void js::GCMarker::enterWeakMarkingMode();
 
   public:
     WeakMapBase(JSObject* memOf, JS::Zone* zone);
     virtual ~WeakMapBase();
 
     void trace(JSTracer* tracer);
 
@@ -69,22 +70,27 @@ class WeakMapBase : public mozilla::Link
 
     // Sweep the weak maps in a zone, removing dead weak maps and removing
     // entries of live weak maps whose keys are dead.
     static void sweepZone(JS::Zone* zone);
 
     // Trace all delayed weak map bindings. Used by the cycle collector.
     static void traceAllMappings(WeakMapTracer* tracer);
 
+    bool isInList() { return next != WeakMapNotInList; }
+
     // Save information about which weak maps are marked for a zone.
     static bool saveZoneMarkedWeakMaps(JS::Zone* zone, WeakMapSet& markedWeakMaps);
 
     // Restore information about which weak maps are marked for many zones.
     static void restoreMarkedWeakMaps(WeakMapSet& markedWeakMaps);
 
+    // Remove a weakmap from its zone's weakmaps list.
+    static void removeWeakMapFromList(WeakMapBase* weakmap);
+
     // Any weakmap key types that want to participate in the non-iterative
     // ephemeron marking must override this method.
     virtual void maybeMarkEntry(JSTracer* trc, gc::Cell* markedCell, JS::GCCellPtr l) = 0;
 
     virtual void markEphemeronEntries(JSTracer* trc) = 0;
 
   protected:
     // Instance member functions called by the above. Instantiations of WeakMap override
@@ -98,16 +104,21 @@ class WeakMapBase : public mozilla::Link
     virtual void finish() = 0;
 
     // Object that this weak map is part of, if any.
     HeapPtrObject memberOf;
 
     // Zone containing this weak map.
     JS::Zone* zone;
 
+    // Link in a list of all WeakMaps in a Zone, headed by
+    // JS::Zone::gcWeakMapList. The last element of the list has nullptr as its
+    // next. Maps not in the list have WeakMapNotInList as their next.
+    WeakMapBase* next;
+
     // Whether this object has been traced during garbage collection.
     bool marked;
 };
 
 template <typename T>
 static T extractUnbarriered(WriteBarrieredBase<T> v)
 {
     return v.get();
@@ -131,17 +142,18 @@ class WeakMap : public HashMap<Key, Valu
     typedef typename Base::AddPtr AddPtr;
 
     explicit WeakMap(JSContext* cx, JSObject* memOf = nullptr)
         : Base(cx->runtime()), WeakMapBase(memOf, cx->compartment()->zone()) { }
 
     bool init(uint32_t len = 16) {
         if (!Base::init(len))
             return false;
-        zone->gcWeakMapList.insertFront(this);
+        next = zone->gcWeakMapList;
+        zone->gcWeakMapList = this;
         marked = JS::IsIncrementalGCInProgress(zone->runtimeFromMainThread());
         return true;
     }
 
     // Overwritten to add a read barrier to prevent an incorrectly gray value
     // from escaping the weak map. See the comment before UnmarkGrayChildren in
     // gc/Marking.cpp
     Ptr lookup(const Lookup& l) const {
@@ -160,19 +172,16 @@ class WeakMap : public HashMap<Key, Valu
 
     Ptr lookupWithDefault(const Key& k, const Value& defaultValue) {
         Ptr p = Base::lookupWithDefault(k, defaultValue);
         if (p)
             exposeGCThingToActiveJS(p->value());
         return p;
     }
 
-    // Resolve ambiguity with LinkedListElement<>::remove.
-    using Base::remove;
-
     // The WeakMap and some part of the key are marked. If the entry is marked
     // according to the exact semantics of this WeakMap, then mark the value.
     // (For a standard WeakMap, the entry is marked if either the key its
     // delegate is marked.)
     void maybeMarkEntry(JSTracer* trc, gc::Cell* markedCell, JS::GCCellPtr origKey) override
     {
         MOZ_ASSERT(marked);
 
@@ -404,16 +413,17 @@ class ObjectWeakMap
 {
   private:
     ObjectValueMap map;
     typedef gc::HashKeyRef<ObjectValueMap, JSObject*> StoreBufferRef;
 
   public:
     explicit ObjectWeakMap(JSContext* cx);
     bool init();
+    ~ObjectWeakMap();
 
     JSObject* lookup(const JSObject* obj);
     bool add(JSContext* cx, JSObject* obj, JSObject* target);
     void clear();
 
     void trace(JSTracer* trc);
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) {
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2434,17 +2434,17 @@ Debugger::markCrossCompartmentEdges(JSTr
  */
 /* static */ void
 Debugger::markIncomingCrossCompartmentEdges(JSTracer* trc)
 {
     JSRuntime* rt = trc->runtime();
     gc::State state = rt->gc.state();
     MOZ_ASSERT(state == gc::MARK_ROOTS || state == gc::COMPACT);
 
-    for (Debugger* dbg : rt->debuggerList) {
+    for (Debugger* dbg = rt->debuggerList.getFirst(); dbg; dbg = dbg->getNext()) {
         Zone* zone = dbg->object->zone();
         if ((state == gc::MARK_ROOTS && !zone->isCollecting()) ||
             (state == gc::COMPACT && !zone->isGCCompacting()))
         {
             dbg->markCrossCompartmentEdges(trc);
         }
     }
 }
@@ -2530,17 +2530,17 @@ Debugger::markAllIteratively(GCMarker* t
  * Mark all debugger-owned GC things unconditionally. This is used by the minor
  * GC: the minor GC cannot apply the weak constraints of the full GC because it
  * visits only part of the heap.
  */
 /* static */ void
 Debugger::markAll(JSTracer* trc)
 {
     JSRuntime* rt = trc->runtime();
-    for (Debugger* dbg : rt->debuggerList) {
+    for (Debugger* dbg = rt->debuggerList.getFirst(); dbg; dbg = dbg->getNext()) {
         WeakGlobalObjectSet& debuggees = dbg->debuggees;
         for (WeakGlobalObjectSet::Enum e(debuggees); !e.empty(); e.popFront()) {
             GlobalObject* global = e.front();
             TraceManuallyBarrieredEdge(trc, &global, "Global Object");
             if (global != e.front())
                 e.rekeyFront(ReadBarrieredGlobalObject(global));
         }
 
@@ -2602,17 +2602,17 @@ Debugger::trace(JSTracer* trc)
     environments.trace(trc);
 }
 
 /* static */ void
 Debugger::sweepAll(FreeOp* fop)
 {
     JSRuntime* rt = fop->runtime();
 
-    for (Debugger* dbg : rt->debuggerList) {
+    for (Debugger* dbg = rt->debuggerList.getFirst(); dbg; dbg = dbg->getNext()) {
         if (IsAboutToBeFinalized(&dbg->object)) {
             /*
              * dbg is being GC'd. Detach it from its debuggees. The debuggee
              * might be GC'd too. Since detaching requires access to both
              * objects, this must be done before finalize time.
              */
             for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront())
                 dbg->removeDebuggeeGlobal(fop, e.front(), &e);
@@ -2633,17 +2633,20 @@ Debugger::detachAllDebuggersFromGlobal(F
 Debugger::findZoneEdges(Zone* zone, js::gc::ComponentFinder<Zone>& finder)
 {
     /*
      * For debugger cross compartment wrappers, add edges in the opposite
      * direction to those already added by JSCompartment::findOutgoingEdges.
      * This ensure that debuggers and their debuggees are finalized in the same
      * group.
      */
-    for (Debugger* dbg : zone->runtimeFromMainThread()->debuggerList) {
+    for (Debugger* dbg = zone->runtimeFromMainThread()->debuggerList.getFirst();
+         dbg;
+         dbg = dbg->getNext())
+    {
         Zone* w = dbg->object->zone();
         if (w == zone || !w->isGCMarking())
             continue;
         if (dbg->debuggeeZones.has(zone) ||
             dbg->scripts.hasKeyInZone(zone) ||
             dbg->sources.hasKeyInZone(zone) ||
             dbg->objects.hasKeyInZone(zone) ||
             dbg->environments.hasKeyInZone(zone))
@@ -8471,17 +8474,17 @@ FireOnGarbageCollectionHook(JSContext* c
     AutoObjectVector triggered(cx);
 
     {
         // We had better not GC (and potentially get a dangling Debugger
         // pointer) while finding all Debuggers observing a debuggee that
         // participated in this GC.
         AutoCheckCannotGC noGC;
 
-        for (Debugger* dbg : cx->runtime()->debuggerList) {
+        for (Debugger* dbg = cx->runtime()->debuggerList.getFirst(); dbg; dbg = dbg->getNext()) {
             if (dbg->enabled &&
                 dbg->observedGC(data->majorGCNumber()) &&
                 dbg->getHook(Debugger::OnGarbageCollection))
             {
                 if (!triggered.append(dbg->object)) {
                     JS_ReportOutOfMemory(cx);
                     return false;
                 }
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -88,16 +88,26 @@ class DebuggerWeakMap : private WeakMap<
     typedef WeakMap<Key, Value, DefaultHasher<Key> > Base;
 
     explicit DebuggerWeakMap(JSContext* cx)
         : Base(cx),
           zoneCounts(cx->runtime()),
           compartment(cx->compartment())
     { }
 
+    ~DebuggerWeakMap() {
+        // If our owning Debugger fails construction after already initializing
+        // this DebuggerWeakMap, we need to make sure that we aren't in the
+        // compartment's weak map list anymore. Normally, when we are destroyed
+        // because the GC finds us unreachable, the GC takes care of removing us
+        // from this list.
+        if (WeakMapBase::isInList())
+            WeakMapBase::removeWeakMapFromList(this);
+    }
+
   public:
     /* Expose those parts of HashMap public interface that are used by Debugger methods. */
 
     typedef typename Base::Entry Entry;
     typedef typename Base::Ptr Ptr;
     typedef typename Base::AddPtr AddPtr;
     typedef typename Base::Range Range;
     typedef typename Base::Enum Enum;
@@ -195,17 +205,16 @@ class DebuggerWeakMap : private WeakMap<
 typedef JSObject Env;
 
 class Debugger : private mozilla::LinkedListElement<Debugger>
 {
     friend class Breakpoint;
     friend class DebuggerMemory;
     friend class SavedStacks;
     friend class mozilla::LinkedListElement<Debugger>;
-    friend class mozilla::LinkedList<Debugger>;
     friend bool (::JS_DefineDebuggerObject)(JSContext* cx, JS::HandleObject obj);
     friend bool (::JS::dbg::IsDebugger)(JSObject&);
     friend bool (::JS::dbg::GetDebuggeeGlobals)(JSContext*, JSObject&, AutoObjectVector&);
     friend void JS::dbg::onNewPromise(JSContext* cx, HandleObject promise);
     friend void JS::dbg::onPromiseSettled(JSContext* cx, HandleObject promise);
     friend bool JS::dbg::FireOnGarbageCollectionHook(JSContext* cx,
                                                      JS::dbg::GarbageCollectionEvent::Ptr&& data);
 
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -1775,17 +1775,20 @@ ComputePlainObjectLayout(ExclusiveContex
     uint32_t offset = 0;
 
     // Search for an existing unboxed layout which is a subset of this one.
     // If there are multiple such layouts, use the largest one. If we're able
     // to find such a layout, use the same property offsets for the shared
     // properties, which will allow us to generate better code if the objects
     // have a subtype/supertype relation and are accessed at common sites.
     UnboxedLayout* bestExisting = nullptr;
-    for (UnboxedLayout* existing : cx->compartment()->unboxedLayouts) {
+    for (UnboxedLayout* existing = cx->compartment()->unboxedLayouts.getFirst();
+         existing;
+         existing = existing->getNext())
+    {
         if (PropertiesAreSuperset(properties, existing)) {
             if (!bestExisting ||
                 existing->properties().length() > bestExisting->properties().length())
             {
                 bestExisting = existing;
             }
         }
     }
--- a/js/src/vm/WeakMapPtr.cpp
+++ b/js/src/vm/WeakMapPtr.cpp
@@ -48,17 +48,22 @@ struct Utils
 
 } /* namespace */
 
 template <typename K, typename V>
 void
 JS::WeakMapPtr<K, V>::destroy()
 {
     MOZ_ASSERT(initialized());
-    js_delete(Utils<K, V>::cast(ptr));
+    auto map = Utils<K, V>::cast(ptr);
+    // If this destruction happens mid-GC, we might be in the compartment's list
+    // of known live weakmaps. If we are, remove ourselves before deleting.
+    if (map->isInList())
+        WeakMapBase::removeWeakMapFromList(map);
+    js_delete(map);
     ptr = nullptr;
 }
 
 template <typename K, typename V>
 bool
 JS::WeakMapPtr<K, V>::init(JSContext* cx)
 {
     MOZ_ASSERT(!initialized());
--- a/mfbt/LinkedList.h
+++ b/mfbt/LinkedList.h
@@ -294,36 +294,16 @@ private:
 
 template<typename T>
 class LinkedList
 {
 private:
   LinkedListElement<T> sentinel;
 
 public:
-  class Iterator {
-    T* mCurrent;
-
-  public:
-    explicit Iterator(T* aCurrent) : mCurrent(aCurrent) {}
-
-    T* operator *() const {
-      return mCurrent;
-    }
-
-    const Iterator& operator++() {
-      mCurrent = mCurrent->getNext();
-      return *this;
-    }
-
-    bool operator!=(Iterator& aOther) const {
-      return mCurrent != aOther.mCurrent;
-    }
-  };
-
   LinkedList() : sentinel(LinkedListElement<T>::NODE_KIND_SENTINEL) { }
 
   LinkedList(LinkedList<T>&& aOther)
     : sentinel(mozilla::Move(aOther.sentinel))
   { }
 
   ~LinkedList() { MOZ_ASSERT(isEmpty()); }
 
@@ -399,28 +379,16 @@ public:
   void clear()
   {
     while (popFirst()) {
       continue;
     }
   }
 
   /*
-   * Allow range-based iteration:
-   *
-   *     for (MyElementType* elt : myList) { ... }
-   */
-  Iterator begin() {
-    return Iterator(getFirst());
-  }
-  Iterator end() {
-    return Iterator(nullptr);
-  }
-
-  /*
    * Measures the memory consumption of the list excluding |this|.  Note that
    * it only measures the list elements themselves.  If the list elements
    * contain pointers to other memory blocks, those blocks must be measured
    * separately during a subsequent iteration over the list.
    */
   size_t sizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
   {
     size_t n = 0;
deleted file mode 100644
--- a/mfbt/tests/TestLinkedList.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * 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/. */
-
-#include "mozilla/Assertions.h"
-#include "mozilla/LinkedList.h"
-
-using mozilla::LinkedList;
-using mozilla::LinkedListElement;
-
-struct SomeClass : public LinkedListElement<SomeClass> {
-  unsigned int mValue;
-  explicit SomeClass(int aValue) : mValue(aValue) {}
-  void incr() { ++mValue; }
-};
-
-struct PrivateClass : private LinkedListElement<PrivateClass> {
-  friend class LinkedList<PrivateClass>;
-  friend class LinkedListElement<PrivateClass>;
-};
-
-template <size_t N>
-static void
-CheckListValues(LinkedList<SomeClass>& list, unsigned int (&values)[N])
-{
-  size_t count = 0;
-  for (SomeClass* x : list) {
-    MOZ_RELEASE_ASSERT(x->mValue == values[count]);
-    ++count;
-  }
-  MOZ_RELEASE_ASSERT(count == N);
-}
-
-static void
-TestList()
-{
-  LinkedList<SomeClass> list;
-
-  SomeClass one(1), two(2), three(3);
-  
-  MOZ_RELEASE_ASSERT(list.isEmpty());
-  MOZ_RELEASE_ASSERT(!list.getFirst());
-  MOZ_RELEASE_ASSERT(!list.getLast());
-  MOZ_RELEASE_ASSERT(!list.popFirst());
-  MOZ_RELEASE_ASSERT(!list.popLast());
-
-  for (SomeClass* x : list) {
-    MOZ_RELEASE_ASSERT(x);
-    MOZ_RELEASE_ASSERT(false);
-  }
-
-  list.insertFront(&one);
-  { unsigned int check[] { 1 }; CheckListValues(list, check); }
-
-  MOZ_RELEASE_ASSERT(one.isInList());
-  MOZ_RELEASE_ASSERT(!two.isInList());
-  MOZ_RELEASE_ASSERT(!three.isInList());
-  
-  MOZ_RELEASE_ASSERT(!list.isEmpty());
-  MOZ_RELEASE_ASSERT(list.getFirst()->mValue == 1);
-  MOZ_RELEASE_ASSERT(list.getLast()->mValue == 1);
-
-  list.insertFront(&two);
-  { unsigned int check[] { 2, 1 }; CheckListValues(list, check); }
-
-  MOZ_RELEASE_ASSERT(list.getFirst()->mValue == 2);
-  MOZ_RELEASE_ASSERT(list.getLast()->mValue == 1);
-
-  list.insertBack(&three);
-  { unsigned int check[] { 2, 1, 3 }; CheckListValues(list, check); }
-
-  MOZ_RELEASE_ASSERT(list.getFirst()->mValue == 2);
-  MOZ_RELEASE_ASSERT(list.getLast()->mValue == 3);
-
-  one.removeFrom(list);
-  { unsigned int check[] { 2, 3 }; CheckListValues(list, check); }
-
-  three.setPrevious(&one);
-  { unsigned int check[] { 2, 1, 3 }; CheckListValues(list, check); }
-
-  three.removeFrom(list);
-  { unsigned int check[] { 2, 1 }; CheckListValues(list, check); }
-
-  two.setPrevious(&three);
-  { unsigned int check[] { 3, 2, 1 }; CheckListValues(list, check); }
-
-  three.removeFrom(list);
-  { unsigned int check[] { 2, 1 }; CheckListValues(list, check); }
-
-  two.setNext(&three);
-  { unsigned int check[] { 2, 3, 1 }; CheckListValues(list, check); }
-
-  one.remove();
-  { unsigned int check[] { 2, 3 }; CheckListValues(list, check); }
-
-  two.remove();
-  { unsigned int check[] { 3 }; CheckListValues(list, check); }
-
-  three.setPrevious(&two);
-  { unsigned int check[] { 2, 3 }; CheckListValues(list, check); }
-
-  three.remove();
-  { unsigned int check[] { 2 }; CheckListValues(list, check); }
-
-  two.remove();
-
-  list.insertBack(&three);
-  { unsigned int check[] { 3 }; CheckListValues(list, check); }
-
-  list.insertFront(&two);
-  { unsigned int check[] { 2, 3 }; CheckListValues(list, check); }
-
-  for (SomeClass* x : list) {
-    x->incr();
-  }
-
-  MOZ_RELEASE_ASSERT(list.getFirst() == &two);
-  MOZ_RELEASE_ASSERT(list.getLast() == &three);
-  MOZ_RELEASE_ASSERT(list.getFirst()->mValue == 3);
-  MOZ_RELEASE_ASSERT(list.getLast()->mValue == 4);
-}
-
-static void
-TestPrivate()
-{
-  LinkedList<PrivateClass> list;
-  PrivateClass one, two;
-  list.insertBack(&one);
-  list.insertBack(&two);
-
-  size_t count = 0;
-  for (PrivateClass* p : list) {
-    MOZ_RELEASE_ASSERT(p, "cannot have null elements in list");
-    count++;
-  }
-  MOZ_RELEASE_ASSERT(count == 2);
-}
-
-int
-main()
-{
-  TestList();
-  TestPrivate();
-  return 0;
-}
--- a/mfbt/tests/moz.build
+++ b/mfbt/tests/moz.build
@@ -17,17 +17,16 @@ CppUnitTests([
     'TestEndian',
     'TestEnumSet',
     'TestFastBernoulliTrial',
     'TestFloatingPoint',
     'TestFunction',
     'TestIntegerPrintfMacros',
     'TestIntegerRange',
     'TestJSONWriter',
-    'TestLinkedList',
     'TestMacroArgs',
     'TestMacroForEach',
     'TestMathAlgorithms',
     'TestMaybe',
     'TestPair',
     'TestRefPtr',
     'TestRollingMean',
     'TestScopeExit',