Bug 1372524 - Refactor WeakCache implementation a little r=sfink
authorJon Coppeard <jcoppeard@mozilla.com>
Wed, 14 Jun 2017 10:35:16 +0100
changeset 363837 e1568e838bd5c5947cc2678c8deef8e47e45a762
parent 363836 a17ea8524638ebe22ec6d616552b4d46e71f2f43
child 363838 2599d6d9a4b1156f8bc35088bffed1fd6ad2ae2e
push id91425
push userjcoppeard@mozilla.com
push dateWed, 14 Jun 2017 09:42:36 +0000
treeherdermozilla-inbound@e1568e838bd5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1372524
milestone56.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 1372524 - Refactor WeakCache implementation a little r=sfink
js/public/SweepingAPI.h
js/src/gc/Zone.cpp
js/src/gc/Zone.h
js/src/jscompartment.cpp
js/src/jsgc.cpp
js/src/vm/ObjectGroup.cpp
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpShared.h
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
js/src/vm/Shape.cpp
js/src/vm/Shape.h
js/src/wasm/WasmJS.cpp
js/src/wasm/WasmTable.cpp
--- a/js/public/SweepingAPI.h
+++ b/js/public/SweepingAPI.h
@@ -5,67 +5,72 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef js_SweepingAPI_h
 #define js_SweepingAPI_h
 
 #include "js/HeapAPI.h"
 
 namespace JS {
-template <typename T> class WeakCache;
+namespace detail {
+class WeakCacheBase;
+} // namespace detail
 
 namespace shadow {
 JS_PUBLIC_API(void)
-RegisterWeakCache(JS::Zone* zone, JS::WeakCache<void*>* cachep);
+RegisterWeakCache(JS::Zone* zone, JS::detail::WeakCacheBase* cachep);
 JS_PUBLIC_API(void)
-RegisterWeakCache(JSRuntime* rt, JS::WeakCache<void*>* cachep);
+RegisterWeakCache(JSRuntime* rt, JS::detail::WeakCacheBase* cachep);
 } // namespace shadow
 
+namespace detail {
+class WeakCacheBase : public mozilla::LinkedListElement<WeakCacheBase>
+{
+    WeakCacheBase() = delete;
+    WeakCacheBase(const WeakCacheBase&) = delete;
+
+  public:
+    WeakCacheBase(Zone* zone) {
+        shadow::RegisterWeakCache(zone, this);
+    }
+    WeakCacheBase(JSRuntime* rt) {
+        shadow::RegisterWeakCache(rt, this);
+    }
+    WeakCacheBase(WeakCacheBase&& other) = default;
+    virtual ~WeakCacheBase() {}
+
+    virtual void sweep() = 0;
+};
+} // namespace detail
+
 // A WeakCache stores the given Sweepable container and links itself into a
 // list of such caches that are swept during each GC. A WeakCache can be
 // specific to a zone, or across a whole runtime, depending on which
 // constructor is used.
 template <typename T>
-class WeakCache : public js::MutableWrappedPtrOperations<T, WeakCache<T>>,
-                  private mozilla::LinkedListElement<WeakCache<T>>
+class WeakCache : protected detail::WeakCacheBase,
+                  public js::MutableWrappedPtrOperations<T, WeakCache<T>>
 {
-    friend class mozilla::LinkedListElement<WeakCache<T>>;
-    friend class mozilla::LinkedList<WeakCache<T>>;
-
-    WeakCache() = delete;
-    WeakCache(const WeakCache&) = delete;
-
-    using SweepFn = void (*)(T*);
-    SweepFn sweeper;
     T cache;
 
   public:
     using Type = T;
 
-    template <typename U>
-    WeakCache(Zone* zone, U&& initial)
-      : cache(mozilla::Forward<U>(initial))
-    {
-        sweeper = GCPolicy<T>::sweep;
-        shadow::RegisterWeakCache(zone, reinterpret_cast<WeakCache<void*>*>(this));
-    }
-    template <typename U>
-    WeakCache(JSRuntime* rt, U&& initial)
-      : cache(mozilla::Forward<U>(initial))
-    {
-        sweeper = GCPolicy<T>::sweep;
-        shadow::RegisterWeakCache(rt, reinterpret_cast<WeakCache<void*>*>(this));
-    }
-    WeakCache(WeakCache&& other)
-      : sweeper(other.sweeper),
-        cache(mozilla::Move(other.cache))
-    {
-    }
+    template <typename... Args>
+    WeakCache(Zone* zone, Args&&... args)
+      : WeakCacheBase(zone), cache(mozilla::Forward<Args>(args)...)
+    {}
+    template <typename... Args>
+    WeakCache(JSRuntime* rt, Args&&... args)
+      : WeakCacheBase(rt), cache(mozilla::Forward<Args>(args)...)
+    {}
 
     const T& get() const { return cache; }
     T& get() { return cache; }
 
-    void sweep() { sweeper(&cache); }
+    void sweep() override {
+        GCPolicy<T>::sweep(&cache);
+    }
 };
 
 } // namespace JS
 
 #endif // js_SweepingAPI_h
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -33,26 +33,26 @@ JS::Zone::Zone(JSRuntime* rt, ZoneGroup*
     types(this),
     gcWeakMapList_(group),
     compartments_(),
     gcGrayRoots_(group),
     gcWeakRefs_(group),
     weakCaches_(group),
     gcWeakKeys_(group, SystemAllocPolicy(), rt->randomHashCodeScrambler()),
     gcSweepGroupEdges_(group),
-    typeDescrObjects_(group, this, SystemAllocPolicy()),
+    typeDescrObjects_(group, this),
     markedAtoms_(group),
     atomCache_(group),
     externalStringCache_(group),
     usage(&rt->gc.usage),
     threshold(),
     gcDelayBytes(0),
     propertyTree_(group, this),
-    baseShapes_(group, this, BaseShapeSet()),
-    initialShapes_(group, this, InitialShapeSet()),
+    baseShapes_(group, this),
+    initialShapes_(group, this),
     data(group, nullptr),
     isSystem(group, false),
 #ifdef DEBUG
     gcLastSweepGroupIndex(group, 0),
 #endif
     jitZone_(group, nullptr),
     gcScheduled_(false),
     gcPreserveCode_(group, false),
@@ -461,12 +461,12 @@ ZoneList::removeFront()
 void
 ZoneList::clear()
 {
     while (!isEmpty())
         removeFront();
 }
 
 JS_PUBLIC_API(void)
-JS::shadow::RegisterWeakCache(JS::Zone* zone, WeakCache<void*>* cachep)
+JS::shadow::RegisterWeakCache(JS::Zone* zone, detail::WeakCacheBase* cachep)
 {
     zone->registerWeakCache(cachep);
 }
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -345,20 +345,20 @@ struct Zone : public JS::shadow::Zone,
     using WeakEdges = js::Vector<js::gc::TenuredCell**, 0, js::SystemAllocPolicy>;
   private:
     js::ZoneGroupOrGCTaskData<WeakEdges> gcWeakRefs_;
   public:
     WeakEdges& gcWeakRefs() { return gcWeakRefs_.ref(); }
 
   private:
     // List of non-ephemeron weak containers to sweep during beginSweepingSweepGroup.
-    js::ZoneGroupData<mozilla::LinkedList<WeakCache<void*>>> weakCaches_;
+    js::ZoneGroupData<mozilla::LinkedList<detail::WeakCacheBase>> weakCaches_;
   public:
-    mozilla::LinkedList<WeakCache<void*>>& weakCaches() { return weakCaches_.ref(); }
-    void registerWeakCache(WeakCache<void*>* cachep) {
+    mozilla::LinkedList<detail::WeakCacheBase>& weakCaches() { return weakCaches_.ref(); }
+    void registerWeakCache(detail::WeakCacheBase* cachep) {
         weakCaches().insertBack(cachep);
     }
 
   private:
     /*
      * Mapping from not yet marked keys to a vector of all values that the key
      * maps to in any live weak map.
      */
@@ -460,28 +460,28 @@ struct Zone : public JS::shadow::Zone,
   private:
     // Shared Shape property tree.
     js::ZoneGroupData<js::PropertyTree> propertyTree_;
   public:
     js::PropertyTree& propertyTree() { return propertyTree_.ref(); }
 
   private:
     // Set of all unowned base shapes in the Zone.
-    js::ZoneGroupData<JS::WeakCache<js::BaseShapeSet>> baseShapes_;
+    js::ZoneGroupData<js::BaseShapeSet> baseShapes_;
   public:
-    JS::WeakCache<js::BaseShapeSet>& baseShapes() { return baseShapes_.ref(); }
+    js::BaseShapeSet& baseShapes() { return baseShapes_.ref(); }
 
   private:
     // Set of initial shapes in the Zone. For certain prototypes -- namely,
     // those of various builtin classes -- there are two entries: one for a
     // lookup via TaggedProto, and one for a lookup via JSProtoKey. See
     // InitialShapeProto.
-    js::ZoneGroupData<JS::WeakCache<js::InitialShapeSet>> initialShapes_;
+    js::ZoneGroupData<js::InitialShapeSet> initialShapes_;
   public:
-    JS::WeakCache<js::InitialShapeSet>& initialShapes() { return initialShapes_.ref(); }
+    js::InitialShapeSet& initialShapes() { return initialShapes_.ref(); }
 
 #ifdef JSGC_HASH_TABLE_CHECKS
     void checkInitialShapesTableAfterMovingGC();
     void checkBaseShapeTableAfterMovingGC();
 #endif
     void fixupInitialShapeTable();
     void fixupAfterMovingGC();
 
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -70,17 +70,17 @@ JSCompartment::JSCompartment(Zone* zone,
     allocationMetadataBuilder(nullptr),
     lastAnimationTime(0),
     regExps(zone),
     globalWriteBarriered(0),
     detachedTypedObjects(0),
     objectMetadataState(ImmediateMetadata()),
     selfHostingScriptSource(nullptr),
     objectMetadataTable(nullptr),
-    innerViews(zone, InnerViewTable()),
+    innerViews(zone),
     lazyArrayBuffers(nullptr),
     wasm(zone),
     nonSyntacticLexicalEnvironments_(nullptr),
     gcIncomingGrayPointers(nullptr),
     debugModeBits(0),
     validAccessPtr(nullptr),
     randomKeyGenerator_(runtime_->forkRandomKeyGenerator()),
     watchpointMap(nullptr),
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2627,17 +2627,17 @@ GCRuntime::updateRuntimePointersToReloca
         if (JSTraceDataOp op = grayRootTracer.op)
             (*op)(&trc, grayRootTracer.data);
     }
 
     // Sweep everything to fix up weak pointers.
     WatchpointMap::sweepAll(rt);
     Debugger::sweepAll(rt->defaultFreeOp());
     jit::JitRuntime::SweepJitcodeGlobalTable(rt);
-    for (JS::WeakCache<void*>* cache : rt->weakCaches())
+    for (JS::detail::WeakCacheBase* cache : rt->weakCaches())
         cache->sweep();
 
     // Type inference may put more blocks here to free.
     blocksToFreeAfterSweeping.ref().freeAll();
 
     // Call callbacks to get the rest of the system to fixup other untraced pointers.
     callWeakPointerZonesCallbacks();
 }
@@ -4988,22 +4988,22 @@ GCRuntime::endMarkingSweepGroup()
     }
     MOZ_ASSERT(marker.isDrained());
     marker.setMarkColorBlack();
 }
 
 // Causes the given WeakCache to be swept when run.
 class SweepWeakCacheTask : public GCParallelTask
 {
-    JS::WeakCache<void*>& cache;
+    JS::detail::WeakCacheBase& cache;
 
     SweepWeakCacheTask(const SweepWeakCacheTask&) = delete;
 
   public:
-    SweepWeakCacheTask(JSRuntime* rt, JS::WeakCache<void*>& wc)
+    SweepWeakCacheTask(JSRuntime* rt, JS::detail::WeakCacheBase& wc)
       : GCParallelTask(rt), cache(wc)
     {}
 
     SweepWeakCacheTask(SweepWeakCacheTask&& other)
       : GCParallelTask(mozilla::Move(other)), cache(other.cache)
     {}
 
     void run() override {
@@ -5208,43 +5208,43 @@ GCRuntime::sweepJitDataOnMainThread(Free
 
 using WeakCacheTaskVector = mozilla::Vector<SweepWeakCacheTask, 0, SystemAllocPolicy>;
 
 template <typename Functor>
 static inline bool
 IterateWeakCaches(JSRuntime* rt, Functor f)
 {
     for (GCSweepGroupIter zone(rt); !zone.done(); zone.next()) {
-        for (JS::WeakCache<void*>* cache : zone->weakCaches()) {
+        for (JS::detail::WeakCacheBase* cache : zone->weakCaches()) {
             if (!f(cache))
                 return false;
         }
     }
 
-    for (JS::WeakCache<void*>* cache : rt->weakCaches()) {
+    for (JS::detail::WeakCacheBase* cache : rt->weakCaches()) {
         if (!f(cache))
             return false;
     }
 
     return true;
 }
 
 static WeakCacheTaskVector
 PrepareWeakCacheTasks(JSRuntime* rt)
 {
     // Build a vector of sweep tasks to run on a helper thread.
     WeakCacheTaskVector tasks;
-    bool ok = IterateWeakCaches(rt, [&] (JS::WeakCache<void*>* cache) {
+    bool ok = IterateWeakCaches(rt, [&] (JS::detail::WeakCacheBase* cache) {
         return tasks.emplaceBack(rt, *cache);
     });
 
     // If we ran out of memory, do all the work now and ensure we return an
     // empty list.
     if (!ok) {
-        IterateWeakCaches(rt, [&] (JS::WeakCache<void*>* cache) {
+        IterateWeakCaches(rt, [&] (JS::detail::WeakCacheBase* cache) {
             SweepWeakCacheTask(rt, *cache).runFromActiveCooperatingThread(rt);
             return true;
         });
         tasks.clear();
     }
 
     return tasks;
 }
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -454,17 +454,17 @@ struct FallibleHashMethods<ObjectGroupCo
 
 class ObjectGroupCompartment::NewTable : public JS::WeakCache<js::GCHashSet<NewEntry, NewEntry,
                                                                             SystemAllocPolicy>>
 {
     using Table = js::GCHashSet<NewEntry, NewEntry, SystemAllocPolicy>;
     using Base = JS::WeakCache<Table>;
 
   public:
-    explicit NewTable(Zone* zone) : Base(zone, Table()) {}
+    explicit NewTable(Zone* zone) : Base(zone) {}
 };
 
 MOZ_ALWAYS_INLINE ObjectGroup*
 ObjectGroupCompartment::DefaultNewGroupCache::lookup(const Class* clasp, TaggedProto proto,
                                                      JSObject* associated)
 {
     if (group_ &&
         associated_ == associated &&
@@ -1458,17 +1458,17 @@ class ObjectGroupCompartment::Allocation
   : public JS::WeakCache<js::GCHashMap<AllocationSiteKey, ReadBarrieredObjectGroup,
                                        AllocationSiteKey, SystemAllocPolicy>>
 {
     using Table = js::GCHashMap<AllocationSiteKey, ReadBarrieredObjectGroup,
                                 AllocationSiteKey, SystemAllocPolicy>;
     using Base = JS::WeakCache<Table>;
 
   public:
-    explicit AllocationSiteTable(Zone* zone) : Base(zone, Table()) {}
+    explicit AllocationSiteTable(Zone* zone) : Base(zone) {}
 };
 
 /* static */ ObjectGroup*
 ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* scriptArg, jsbytecode* pc,
                                  JSProtoKey kind, HandleObject protoArg /* = nullptr */)
 {
     MOZ_ASSERT(!useSingletonForAllocationSite(scriptArg, pc, kind));
     MOZ_ASSERT_IF(protoArg, kind == JSProto_Array);
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -1189,17 +1189,17 @@ RegExpShared::sizeOfExcludingThis(mozill
         n += mallocSizeOf(tables[i]);
 
     return n;
 }
 
 /* RegExpCompartment */
 
 RegExpCompartment::RegExpCompartment(Zone* zone)
-  : set_(zone, Set(zone->runtimeFromActiveCooperatingThread())),
+  : set_(zone, zone->runtimeFromActiveCooperatingThread()),
     matchResultTemplateObject_(nullptr),
     optimizableRegExpPrototypeShape_(nullptr),
     optimizableRegExpInstanceShape_(nullptr)
 {}
 
 RegExpCompartment::~RegExpCompartment()
 {
     MOZ_ASSERT_IF(set_.initialized(), set_.empty());
@@ -1289,29 +1289,29 @@ RegExpCompartment::sweep(JSRuntime* rt)
         optimizableRegExpInstanceShape_.set(nullptr);
     }
 }
 
 bool
 RegExpCompartment::get(JSContext* cx, HandleAtom source, RegExpFlag flags,
                        MutableHandleRegExpShared result)
 {
-    DependentAddPtr<Set> p(cx, set_.get(), Key(source, flags));
+    DependentAddPtr<Set> p(cx, set_, Key(source, flags));
     if (p) {
         result.set(*p);
         return true;
     }
 
     auto shared = Allocate<RegExpShared>(cx);
     if (!shared)
         return false;
 
     new (shared) RegExpShared(source, flags);
 
-    if (!p.add(cx, set_.get(), Key(source, flags), shared)) {
+    if (!p.add(cx, set_, Key(source, flags), shared)) {
         ReportOutOfMemory(cx);
         return false;
     }
 
     result.set(shared);
     return true;
 }
 
--- a/js/src/vm/RegExpShared.h
+++ b/js/src/vm/RegExpShared.h
@@ -250,18 +250,18 @@ class RegExpCompartment
             return l.atom == r.atom && l.flag == r.flag;
         }
     };
 
     /*
      * The set of all RegExpShareds in the compartment. On every GC, every
      * RegExpShared that was not marked is deleted and removed from the set.
      */
-    using Set = JS::GCHashSet<ReadBarriered<RegExpShared*>, Key, RuntimeAllocPolicy>;
-    JS::WeakCache<Set> set_;
+    using Set = JS::WeakCache<JS::GCHashSet<ReadBarriered<RegExpShared*>, Key, RuntimeAllocPolicy>>;
+    Set set_;
 
     /*
      * This is the template object where the result of re.exec() is based on,
      * if there is a result. This is used in CreateRegExpMatchResult to set
      * the input/index properties faster.
      */
     ReadBarriered<ArrayObject*> matchResultTemplateObject_;
 
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -926,12 +926,12 @@ JS::UpdateJSContextProfilerSampleBufferG
 JS_FRIEND_API(bool)
 JS::IsProfilingEnabledForContext(JSContext* cx)
 {
     MOZ_ASSERT(cx);
     return cx->runtime()->geckoProfiler().enabled();
 }
 
 JS_PUBLIC_API(void)
-JS::shadow::RegisterWeakCache(JSRuntime* rt, WeakCache<void*>* cachep)
+JS::shadow::RegisterWeakCache(JSRuntime* rt, detail::WeakCacheBase* cachep)
 {
     rt->registerWeakCache(cachep);
 }
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -549,20 +549,20 @@ struct JSRuntime : public js::MallocProv
         return windowProxyClass_;
     }
     void setWindowProxyClass(const js::Class* clasp) {
         windowProxyClass_ = clasp;
     }
 
   private:
     // List of non-ephemeron weak containers to sweep during beginSweepingSweepGroup.
-    js::ActiveThreadData<mozilla::LinkedList<JS::WeakCache<void*>>> weakCaches_;
+    js::ActiveThreadData<mozilla::LinkedList<JS::detail::WeakCacheBase>> weakCaches_;
   public:
-    mozilla::LinkedList<JS::WeakCache<void*>>& weakCaches() { return weakCaches_.ref(); }
-    void registerWeakCache(JS::WeakCache<void*>* cachep) {
+    mozilla::LinkedList<JS::detail::WeakCacheBase>& weakCaches() { return weakCaches_.ref(); }
+    void registerWeakCache(JS::detail::WeakCacheBase* cachep) {
         weakCaches().insertBack(cachep);
     }
 
     template <typename T>
     struct GlobalObjectWatchersSiblingAccess {
       static T* GetNext(T* elm) {
         return elm->onNewGlobalObjectWatchersLink.mNext;
       }
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -1274,17 +1274,17 @@ BaseShape::canSkipMarkingShapeTable(Shap
 #ifdef JSGC_HASH_TABLE_CHECKS
 
 void
 Zone::checkBaseShapeTableAfterMovingGC()
 {
     if (!baseShapes().initialized())
         return;
 
-    for (BaseShapeSet::Enum e(baseShapes().get()); !e.empty(); e.popFront()) {
+    for (BaseShapeSet::Enum e(baseShapes()); !e.empty(); e.popFront()) {
         UnownedBaseShape* base = e.front().unbarrieredGet();
         CheckGCThingAfterMovingGC(base);
 
         BaseShapeSet::Ptr ptr = baseShapes().lookup(base);
         MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &e.front());
     }
 }
 
@@ -1335,17 +1335,17 @@ Zone::checkInitialShapesTableAfterMoving
     if (!initialShapes().initialized())
         return;
 
     /*
      * Assert that the postbarriers have worked and that nothing is left in
      * initialShapes that points into the nursery, and that the hash table
      * entries are discoverable.
      */
-    for (InitialShapeSet::Enum e(initialShapes().get()); !e.empty(); e.popFront()) {
+    for (InitialShapeSet::Enum e(initialShapes()); !e.empty(); e.popFront()) {
         InitialShapeEntry entry = e.front();
         JSProtoKey protoKey = entry.proto.key();
         TaggedProto proto = entry.proto.proto().unbarrieredGet();
         Shape* shape = entry.shape.unbarrieredGet();
 
         CheckGCThingAfterMovingGC(shape);
         if (proto.isObject())
             CheckGCThingAfterMovingGC(proto.toObject());
@@ -1448,17 +1448,17 @@ EmptyShape::getInitialShape(JSContext* c
     // shapes across globals.
     Rooted<TaggedProto> protoRoot(cx, proto);
     Shape* shape = nullptr;
     bool insertKey = false;
     mozilla::Maybe<DependentAddPtr<InitialShapeSet>> keyPointer;
 
     JSProtoKey key = GetInitialShapeProtoKey(protoRoot, cx);
     if (key != JSProto_LIMIT) {
-        keyPointer.emplace(MakeDependentAddPtr(cx, table.get(),
+        keyPointer.emplace(MakeDependentAddPtr(cx, table,
                                                Lookup(clasp, Lookup::ShapeProto(key),
                                                       nfixed, objectFlags)));
         if (keyPointer.ref()) {
             shape = keyPointer.ref()->shape;
             MOZ_ASSERT(shape);
         } else {
             insertKey = true;
         }
@@ -1479,17 +1479,17 @@ EmptyShape::getInitialShape(JSContext* c
     Lookup lookup(clasp, shapeProto, nfixed, objectFlags);
     if (!protoPointer.add(cx, table, lookup, InitialShapeEntry(shape, shapeProto)))
         return nullptr;
 
     // Also add an entry based on the JSProtoKey, if needed.
     if (insertKey) {
         Lookup::ShapeProto shapeProto(key);
         Lookup lookup(clasp, shapeProto, nfixed, objectFlags);
-        if (!keyPointer->add(cx, table.get(), lookup, InitialShapeEntry(shape, shapeProto)))
+        if (!keyPointer->add(cx, table, lookup, InitialShapeEntry(shape, shapeProto)))
             return nullptr;
     }
 
     return shape;
 }
 
 /* static */ Shape*
 EmptyShape::getInitialShape(JSContext* cx, const Class* clasp, TaggedProto proto,
@@ -1583,17 +1583,17 @@ EmptyShape::insertInitialShape(JSContext
 }
 
 void
 Zone::fixupInitialShapeTable()
 {
     if (!initialShapes().initialized())
         return;
 
-    for (InitialShapeSet::Enum e(initialShapes().get()); !e.empty(); e.popFront()) {
+    for (InitialShapeSet::Enum e(initialShapes()); !e.empty(); e.popFront()) {
         // The shape may have been moved, but we can update that in place.
         Shape* shape = e.front().shape.unbarrieredGet();
         if (IsForwarded(shape)) {
             shape = Forwarded(shape);
             e.mutableFront().shape.set(shape);
         }
         shape->updateBaseShapeAfterMovingGC();
 
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -588,19 +588,19 @@ struct DefaultHasher<jsid>
     static HashNumber hash(jsid id) {
         return HashId(id);
     }
     static bool match(jsid id1, jsid id2) {
         return id1 == id2;
     }
 };
 
-using BaseShapeSet = JS::GCHashSet<ReadBarriered<UnownedBaseShape*>,
-                                   StackBaseShape,
-                                   SystemAllocPolicy>;
+using BaseShapeSet = JS::WeakCache<JS::GCHashSet<ReadBarriered<UnownedBaseShape*>,
+                                                 StackBaseShape,
+                                                 SystemAllocPolicy>>;
 
 class Shape : public gc::TenuredCell
 {
     friend class ::JSObject;
     friend class ::JSFunction;
     friend class NativeObject;
     friend class PropertyTree;
     friend class TenuringTracer;
@@ -1274,17 +1274,19 @@ struct InitialShapeEntry
         Shape* ushape = shape.unbarrieredGet();
         TaggedProto uproto = proto.proto().unbarrieredGet();
         JSObject* protoObj = uproto.raw();
         return (gc::IsAboutToBeFinalizedUnbarriered(&ushape) ||
                 (uproto.isObject() && gc::IsAboutToBeFinalizedUnbarriered(&protoObj)));
     }
 };
 
-using InitialShapeSet = JS::GCHashSet<InitialShapeEntry, InitialShapeEntry, SystemAllocPolicy>;
+using InitialShapeSet = JS::WeakCache<JS::GCHashSet<InitialShapeEntry,
+                                                    InitialShapeEntry,
+                                                    SystemAllocPolicy>>;
 
 struct StackShape
 {
     /* For performance, StackShape only roots when absolutely necessary. */
     UnownedBaseShape* base;
     jsid propid;
     GetterOp rawGetter;
     SetterOp rawSetter;
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -1017,17 +1017,17 @@ WasmInstanceObject::create(JSContext* cx
                            HandleObject proto)
 {
     UniquePtr<ExportMap> exports = js::MakeUnique<ExportMap>();
     if (!exports || !exports->init()) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
-    UniquePtr<WeakScopeMap> scopes = js::MakeUnique<WeakScopeMap>(cx->zone(), ScopeMap());
+    UniquePtr<WeakScopeMap> scopes = js::MakeUnique<WeakScopeMap>(cx->zone());
     if (!scopes || !scopes->init()) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
     AutoSetNewObjectMetadata metadata(cx);
     RootedWasmInstanceObject obj(cx, NewObjectWithGivenProto<WasmInstanceObject>(cx, proto));
     if (!obj)
@@ -1446,17 +1446,17 @@ WasmMemoryObject::observers() const
     MOZ_ASSERT(hasObservers());
     return *reinterpret_cast<WeakInstanceSet*>(getReservedSlot(OBSERVERS_SLOT).toPrivate());
 }
 
 WasmMemoryObject::WeakInstanceSet*
 WasmMemoryObject::getOrCreateObservers(JSContext* cx)
 {
     if (!hasObservers()) {
-        auto observers = MakeUnique<WeakInstanceSet>(cx->zone(), InstanceSet());
+        auto observers = MakeUnique<WeakInstanceSet>(cx->zone());
         if (!observers || !observers->init()) {
             ReportOutOfMemory(cx);
             return nullptr;
         }
 
         setReservedSlot(OBSERVERS_SLOT, PrivateValue(observers.release()));
     }
 
--- a/js/src/wasm/WasmTable.cpp
+++ b/js/src/wasm/WasmTable.cpp
@@ -28,17 +28,17 @@
 
 using namespace js;
 using namespace js::wasm;
 using mozilla::CheckedInt;
 
 Table::Table(JSContext* cx, const TableDesc& desc, HandleWasmTableObject maybeObject,
              UniqueByteArray array)
   : maybeObject_(maybeObject),
-    observers_(cx->zone(), InstanceSet()),
+    observers_(cx->zone()),
     array_(Move(array)),
     kind_(desc.kind),
     length_(desc.limits.initial),
     maximum_(desc.limits.maximum),
     external_(desc.external)
 {}
 
 /* static */ SharedTable