Bug 1325406 - Refactor rooting base class templates r=sfink r=mccr8
authorJon Coppeard <jcoppeard@mozilla.com>
Tue, 10 Jan 2017 10:12:14 +0000
changeset 373745 2e1a0c4886c5393498627d394c4f661e6f2ea6b2
parent 373744 006e9ea4166ed1849f15e042be9683d1599423d3
child 373746 b1bed58dbb66e44c6620f0cde392ee22da7600a2
push id6996
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 20:48:21 +0000
treeherdermozilla-beta@d89512dab048 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink, mccr8
bugs1325406
milestone53.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 1325406 - Refactor rooting base class templates r=sfink r=mccr8
dom/bindings/RootedOwningNonNull.h
dom/bindings/RootedRefPtr.h
dom/xbl/nsXBLMaybeCompiled.h
js/public/GCHashTable.h
js/public/GCVariant.h
js/public/GCVector.h
js/public/RootingAPI.h
js/public/SweepingAPI.h
js/public/Value.h
js/src/builtin/MapObject.h
js/src/ds/TraceableFifo.h
js/src/gc/Barrier.h
js/src/jsapi-tests/testGCExactRooting.cpp
js/src/jsapi.h
js/src/jscompartment.cpp
js/src/jsobj.h
js/src/vm/ArrayBufferObject.cpp
js/src/vm/ArrayBufferObject.h
js/src/vm/Interpreter.cpp
js/src/vm/JSONParser.h
js/src/vm/SavedStacks.h
js/src/vm/Scope.h
js/src/vm/Shape.h
js/src/vm/TaggedProto.h
--- a/dom/bindings/RootedOwningNonNull.h
+++ b/dom/bindings/RootedOwningNonNull.h
@@ -46,28 +46,19 @@ struct GCPolicy<mozilla::OwningNonNull<T
     if ((*tp).isInitialized()) {
       (*tp)->Trace(trc);
     }
   }
 };
 } // namespace JS
 
 namespace js {
-template<typename T>
-struct RootedBase<mozilla::OwningNonNull<T>>
+template<typename T, typename Wrapper>
+struct WrappedPtrOperations<mozilla::OwningNonNull<T>, Wrapper>
 {
-  typedef mozilla::OwningNonNull<T> SmartPtrType;
-
-  operator SmartPtrType& () const
-  {
-    auto& self = *static_cast<const JS::Rooted<SmartPtrType>*>(this);
-    return self.get();
-  }
-
   operator T& () const
   {
-    auto& self = *static_cast<const JS::Rooted<SmartPtrType>*>(this);
-    return self.get();
+    return static_cast<const Wrapper*>(this)->get();
   }
 };
 } // namespace js
 
 #endif /* mozilla_RootedOwningNonNull_h__ */
--- a/dom/bindings/RootedRefPtr.h
+++ b/dom/bindings/RootedRefPtr.h
@@ -34,26 +34,19 @@ struct GCPolicy<RefPtr<T>>
     if (*tp) {
       (*tp)->Trace(trc);
     }
   }
 };
 } // namespace JS
 
 namespace js {
-template<typename T>
-struct RootedBase<RefPtr<T>>
+template<typename T, typename Wrapper>
+struct WrappedPtrOperations<RefPtr<T>, Wrapper>
 {
-  operator RefPtr<T>& () const
-  {
-    auto& self = *static_cast<const JS::Rooted<RefPtr<T>>*>(this);
-    return self.get();
-  }
-
   operator T*() const
   {
-    auto& self = *static_cast<const JS::Rooted<RefPtr<T>>*>(this);
-    return self.get();
+    return static_cast<const Wrapper*>(this)->get();
   }
 };
 } // namespace js
 
 #endif /* mozilla_RootedRefPtr_h__ */
--- a/dom/xbl/nsXBLMaybeCompiled.h
+++ b/dom/xbl/nsXBLMaybeCompiled.h
@@ -122,25 +122,25 @@ struct BarrierMethods<nsXBLMaybeCompiled
 };
 
 template <class T>
 struct IsHeapConstructibleType<nsXBLMaybeCompiled<T>>
 { // Yes, this is the exception to the rule. Sorry.
   static constexpr bool value = true;
 };
 
-template <class UncompiledT>
-class HeapBase<nsXBLMaybeCompiled<UncompiledT>>
+template <class UncompiledT, class Wrapper>
+class HeapBase<nsXBLMaybeCompiled<UncompiledT>, Wrapper>
 {
-  const JS::Heap<nsXBLMaybeCompiled<UncompiledT>>& wrapper() const {
-    return *static_cast<const JS::Heap<nsXBLMaybeCompiled<UncompiledT>>*>(this);
+  const Wrapper& wrapper() const {
+    return *static_cast<const Wrapper*>(this);
   }
 
-  JS::Heap<nsXBLMaybeCompiled<UncompiledT>>& wrapper() {
-    return *static_cast<JS::Heap<nsXBLMaybeCompiled<UncompiledT>>*>(this);
+  Wrapper& wrapper() {
+    return *static_cast<Wrapper*>(this);
   }
 
   const nsXBLMaybeCompiled<UncompiledT>* extract() const {
     return wrapper().address();
   }
 
   nsXBLMaybeCompiled<UncompiledT>* extract() {
     return wrapper().unsafeGet();
--- a/js/public/GCHashTable.h
+++ b/js/public/GCHashTable.h
@@ -129,23 +129,23 @@ class GCRekeyableHashMap : public JS::GC
     // GCRekeyableHashMap is movable
     GCRekeyableHashMap(GCRekeyableHashMap&& rhs) : Base(mozilla::Move(rhs)) {}
     void operator=(GCRekeyableHashMap&& rhs) {
         MOZ_ASSERT(this != &rhs, "self-move assignment is prohibited");
         Base::operator=(mozilla::Move(rhs));
     }
 };
 
-template <typename Outer, typename... Args>
-class GCHashMapOperations
+template <typename Wrapper, typename... Args>
+class WrappedPtrOperations<JS::GCHashMap<Args...>, Wrapper>
 {
     using Map = JS::GCHashMap<Args...>;
     using Lookup = typename Map::Lookup;
 
-    const Map& map() const { return static_cast<const Outer*>(this)->get(); }
+    const Map& map() const { return static_cast<const Wrapper*>(this)->get(); }
 
   public:
     using AddPtr = typename Map::AddPtr;
     using Ptr = typename Map::Ptr;
     using Range = typename Map::Range;
 
     bool initialized() const                   { return map().initialized(); }
     Ptr lookup(const Lookup& l) const          { return map().lookup(l); }
@@ -158,28 +158,28 @@ class GCHashMapOperations
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         return map().sizeOfExcludingThis(mallocSizeOf);
     }
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         return mallocSizeOf(this) + map().sizeOfExcludingThis(mallocSizeOf);
     }
 };
 
-template <typename Outer, typename... Args>
-class MutableGCHashMapOperations
-  : public GCHashMapOperations<Outer, Args...>
+template <typename Wrapper, typename... Args>
+class MutableWrappedPtrOperations<JS::GCHashMap<Args...>, Wrapper>
+  : public WrappedPtrOperations<JS::GCHashMap<Args...>, Wrapper>
 {
     using Map = JS::GCHashMap<Args...>;
     using Lookup = typename Map::Lookup;
 
-    Map& map() { return static_cast<Outer*>(this)->get(); }
+    Map& map() { return static_cast<Wrapper*>(this)->get(); }
 
   public:
     using AddPtr = typename Map::AddPtr;
-    struct Enum : public Map::Enum { explicit Enum(Outer& o) : Map::Enum(o.map()) {} };
+    struct Enum : public Map::Enum { explicit Enum(Wrapper& o) : Map::Enum(o.map()) {} };
     using Ptr = typename Map::Ptr;
     using Range = typename Map::Range;
 
     bool init(uint32_t len = 16) { return map().init(len); }
     void clear()                 { map().clear(); }
     void finish()                { map().finish(); }
     void remove(Ptr p)           { map().remove(p); }
 
@@ -206,36 +206,16 @@ class MutableGCHashMapOperations
     }
 
     template<typename KeyInput, typename ValueInput>
     bool putNew(KeyInput&& k, ValueInput&& v) {
         return map().putNew(mozilla::Forward<KeyInput>(k), mozilla::Forward<ValueInput>(v));
     }
 };
 
-template <typename A, typename B, typename C, typename D, typename E>
-class RootedBase<JS::GCHashMap<A,B,C,D,E>>
-  : public MutableGCHashMapOperations<JS::Rooted<JS::GCHashMap<A,B,C,D,E>>, A,B,C,D,E>
-{};
-
-template <typename A, typename B, typename C, typename D, typename E>
-class MutableHandleBase<JS::GCHashMap<A,B,C,D,E>>
-  : public MutableGCHashMapOperations<JS::MutableHandle<JS::GCHashMap<A,B,C,D,E>>, A,B,C,D,E>
-{};
-
-template <typename A, typename B, typename C, typename D, typename E>
-class HandleBase<JS::GCHashMap<A,B,C,D,E>>
-  : public GCHashMapOperations<JS::Handle<JS::GCHashMap<A,B,C,D,E>>, A,B,C,D,E>
-{};
-
-template <typename A, typename B, typename C, typename D, typename E>
-class WeakCacheBase<JS::GCHashMap<A,B,C,D,E>>
-  : public MutableGCHashMapOperations<JS::WeakCache<JS::GCHashMap<A,B,C,D,E>>, A,B,C,D,E>
-{};
-
 } // namespace js
 
 namespace JS {
 
 // A GCHashSet is a HashSet with an additional trace method that knows
 // be traced to be kept alive will generally want to use this GCHashSet
 // specialization in lieu of HashSet.
 //
@@ -287,23 +267,23 @@ class GCHashSet : public js::HashSet<T, 
     GCHashSet(const GCHashSet& hs) = delete;
     GCHashSet& operator=(const GCHashSet& hs) = delete;
 };
 
 } // namespace JS
 
 namespace js {
 
-template <typename Outer, typename... Args>
-class GCHashSetOperations
+template <typename Wrapper, typename... Args>
+class WrappedPtrOperations<JS::GCHashSet<Args...>, Wrapper>
 {
     using Set = JS::GCHashSet<Args...>;
     using Lookup = typename Set::Lookup;
 
-    const Set& set() const { return static_cast<const Outer*>(this)->get(); }
+    const Set& set() const { return static_cast<const Wrapper*>(this)->get(); }
 
   public:
     using AddPtr = typename Set::AddPtr;
     using Entry = typename Set::Entry;
     using Ptr = typename Set::Ptr;
     using Range = typename Set::Range;
 
     bool initialized() const                   { return set().initialized(); }
@@ -317,29 +297,29 @@ class GCHashSetOperations
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         return set().sizeOfExcludingThis(mallocSizeOf);
     }
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         return mallocSizeOf(this) + set().sizeOfExcludingThis(mallocSizeOf);
     }
 };
 
-template <typename Outer, typename... Args>
-class MutableGCHashSetOperations
-  : public GCHashSetOperations<Outer, Args...>
+template <typename Wrapper, typename... Args>
+class MutableWrappedPtrOperations<JS::GCHashSet<Args...>, Wrapper>
+  : public WrappedPtrOperations<JS::GCHashSet<Args...>, Wrapper>
 {
     using Set = JS::GCHashSet<Args...>;
     using Lookup = typename Set::Lookup;
 
-    Set& set() { return static_cast<Outer*>(this)->get(); }
+    Set& set() { return static_cast<Wrapper*>(this)->get(); }
 
   public:
     using AddPtr = typename Set::AddPtr;
     using Entry = typename Set::Entry;
-    struct Enum : public Set::Enum { explicit Enum(Outer& o) : Set::Enum(o.set()) {} };
+    struct Enum : public Set::Enum { explicit Enum(Wrapper& o) : Set::Enum(o.set()) {} };
     using Ptr = typename Set::Ptr;
     using Range = typename Set::Range;
 
     bool init(uint32_t len = 16) { return set().init(len); }
     void clear()                 { set().clear(); }
     void finish()                { set().finish(); }
     void remove(Ptr p)           { set().remove(p); }
     void remove(const Lookup& l) { set().remove(l); }
@@ -365,35 +345,11 @@ class MutableGCHashSetOperations
     }
 
     template<typename TInput>
     bool putNew(const Lookup& l, TInput&& t) {
         return set().putNew(l, mozilla::Forward<TInput>(t));
     }
 };
 
-template <typename T, typename HP, typename AP>
-class RootedBase<JS::GCHashSet<T, HP, AP>>
-  : public MutableGCHashSetOperations<JS::Rooted<JS::GCHashSet<T, HP, AP>>, T, HP, AP>
-{
-};
-
-template <typename T, typename HP, typename AP>
-class MutableHandleBase<JS::GCHashSet<T, HP, AP>>
-  : public MutableGCHashSetOperations<JS::MutableHandle<JS::GCHashSet<T, HP, AP>>, T, HP, AP>
-{
-};
-
-template <typename T, typename HP, typename AP>
-class HandleBase<JS::GCHashSet<T, HP, AP>>
-  : public GCHashSetOperations<JS::Handle<JS::GCHashSet<T, HP, AP>>, T, HP, AP>
-{
-};
-
-template <typename T, typename HP, typename AP>
-class WeakCacheBase<JS::GCHashSet<T, HP, AP>>
-  : public MutableGCHashSetOperations<JS::WeakCache<JS::GCHashSet<T, HP, AP>>, T, HP, AP>
-{
-};
-
 } /* namespace js */
 
 #endif /* GCHashTable_h */
--- a/js/public/GCVariant.h
+++ b/js/public/GCVariant.h
@@ -119,23 +119,23 @@ struct GCPolicy<mozilla::Variant<Ts...>>
         Impl::trace(trc, v, name);
     }
 };
 
 } // namespace JS
 
 namespace js {
 
-template <typename Outer, typename... Ts>
-class GCVariantOperations
+template <typename Wrapper, typename... Ts>
+class WrappedPtrOperations<mozilla::Variant<Ts...>, Wrapper>
 {
     using Impl = JS::detail::GCVariantImplementation<Ts...>;
     using Variant = mozilla::Variant<Ts...>;
 
-    const Variant& variant() const { return static_cast<const Outer*>(this)->get(); }
+    const Variant& variant() const { return static_cast<const Wrapper*>(this)->get(); }
 
   public:
     template <typename T>
     bool is() const {
         return variant().template is<T>();
     }
 
     template <typename T>
@@ -145,54 +145,34 @@ class GCVariantOperations
 
     template <typename Matcher>
     typename Matcher::ReturnType
     match(Matcher& matcher) const {
         return Impl::match(matcher, JS::Handle<Variant>::fromMarkedLocation(&variant()));
     }
 };
 
-template <typename Outer, typename... Ts>
-class MutableGCVariantOperations
-  : public GCVariantOperations<Outer, Ts...>
+template <typename Wrapper, typename... Ts>
+class MutableWrappedPtrOperations<mozilla::Variant<Ts...>, Wrapper>
+  : public WrappedPtrOperations<mozilla::Variant<Ts...>, Wrapper>
 {
     using Impl = JS::detail::GCVariantImplementation<Ts...>;
     using Variant = mozilla::Variant<Ts...>;
 
-    const Variant& variant() const { return static_cast<const Outer*>(this)->get(); }
-    Variant& variant() { return static_cast<Outer*>(this)->get(); }
+    const Variant& variant() const { return static_cast<const Wrapper*>(this)->get(); }
+    Variant& variant() { return static_cast<Wrapper*>(this)->get(); }
 
   public:
     template <typename T>
     JS::MutableHandle<T> as() {
         return JS::MutableHandle<T>::fromMarkedLocation(&variant().template as<T>());
     }
 
     template <typename Matcher>
     typename Matcher::ReturnType
     match(Matcher& matcher) {
         return Impl::match(matcher, JS::MutableHandle<Variant>::fromMarkedLocation(&variant()));
     }
 };
 
-template <typename... Ts>
-class RootedBase<mozilla::Variant<Ts...>>
-  : public MutableGCVariantOperations<JS::Rooted<mozilla::Variant<Ts...>>, Ts...>
-{ };
-
-template <typename... Ts>
-class MutableHandleBase<mozilla::Variant<Ts...>>
-  : public MutableGCVariantOperations<JS::MutableHandle<mozilla::Variant<Ts...>>, Ts...>
-{ };
-
-template <typename... Ts>
-class HandleBase<mozilla::Variant<Ts...>>
-  : public GCVariantOperations<JS::Handle<mozilla::Variant<Ts...>>, Ts...>
-{ };
-
-template <typename... Ts>
-class PersistentRootedBase<mozilla::Variant<Ts...>>
-  : public MutableGCVariantOperations<JS::PersistentRooted<mozilla::Variant<Ts...>>, Ts...>
-{ };
-
 } // namespace js
 
 #endif // js_GCVariant_h
--- a/js/public/GCVector.h
+++ b/js/public/GCVector.h
@@ -130,43 +130,43 @@ class GCVector
             GCPolicy<T>::trace(trc, &elem, "vector element");
     }
 };
 
 } // namespace JS
 
 namespace js {
 
-template <typename Outer, typename T, size_t Capacity, typename AllocPolicy>
-class GCVectorOperations
+template <typename Wrapper, typename T, size_t Capacity, typename AllocPolicy>
+class WrappedPtrOperations<JS::GCVector<T, Capacity, AllocPolicy>, Wrapper>
 {
     using Vec = JS::GCVector<T, Capacity, AllocPolicy>;
-    const Vec& vec() const { return static_cast<const Outer*>(this)->get(); }
+    const Vec& vec() const { return static_cast<const Wrapper*>(this)->get(); }
 
   public:
     const AllocPolicy& allocPolicy() const { return vec().allocPolicy(); }
     size_t length() const { return vec().length(); }
     bool empty() const { return vec().empty(); }
     size_t capacity() const { return vec().capacity(); }
     const T* begin() const { return vec().begin(); }
     const T* end() const { return vec().end(); }
     const T& back() const { return vec().back(); }
 
     JS::Handle<T> operator[](size_t aIndex) const {
         return JS::Handle<T>::fromMarkedLocation(&vec().operator[](aIndex));
     }
 };
 
-template <typename Outer, typename T, size_t Capacity, typename AllocPolicy>
-class MutableGCVectorOperations
-  : public GCVectorOperations<Outer, T, Capacity, AllocPolicy>
+template <typename Wrapper, typename T, size_t Capacity, typename AllocPolicy>
+class MutableWrappedPtrOperations<JS::GCVector<T, Capacity, AllocPolicy>, Wrapper>
+  : public WrappedPtrOperations<JS::GCVector<T, Capacity, AllocPolicy>, Wrapper>
 {
     using Vec = JS::GCVector<T, Capacity, AllocPolicy>;
-    const Vec& vec() const { return static_cast<const Outer*>(this)->get(); }
-    Vec& vec() { return static_cast<Outer*>(this)->get(); }
+    const Vec& vec() const { return static_cast<const Wrapper*>(this)->get(); }
+    Vec& vec() { return static_cast<Wrapper*>(this)->get(); }
 
   public:
     const AllocPolicy& allocPolicy() const { return vec().allocPolicy(); }
     AllocPolicy& allocPolicy() { return vec().allocPolicy(); }
     const T* begin() const { return vec().begin(); }
     T* begin() { return vec().begin(); }
     const T* end() const { return vec().end(); }
     T* end() { return vec().end(); }
@@ -219,31 +219,11 @@ class MutableGCVectorOperations
     T popCopy() { return vec().popCopy(); }
     template<typename U> T* insert(T* aP, U&& aVal) {
         return vec().insert(aP, mozilla::Forward<U>(aVal));
     }
     void erase(T* aT) { vec().erase(aT); }
     void erase(T* aBegin, T* aEnd) { vec().erase(aBegin, aEnd); }
 };
 
-template <typename T, size_t N, typename AP>
-class RootedBase<JS::GCVector<T,N,AP>>
-  : public MutableGCVectorOperations<JS::Rooted<JS::GCVector<T,N,AP>>, T,N,AP>
-{};
-
-template <typename T, size_t N, typename AP>
-class MutableHandleBase<JS::GCVector<T,N,AP>>
-  : public MutableGCVectorOperations<JS::MutableHandle<JS::GCVector<T,N,AP>>, T,N,AP>
-{};
-
-template <typename T, size_t N, typename AP>
-class HandleBase<JS::GCVector<T,N,AP>>
-  : public GCVectorOperations<JS::Handle<JS::GCVector<T,N,AP>>, T,N,AP>
-{};
-
-template <typename T, size_t N, typename AP>
-class PersistentRootedBase<JS::GCVector<T,N,AP>>
-  : public MutableGCVectorOperations<JS::PersistentRooted<JS::GCVector<T,N,AP>>, T,N,AP>
-{};
-
 } // namespace js
 
 #endif // js_GCVector_h
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -108,38 +108,44 @@
  */
 
 namespace js {
 
 template <typename T>
 struct BarrierMethods {
 };
 
-template <typename T>
-class RootedBase {};
+template <typename Element, typename Wrapper>
+class WrappedPtrOperations {};
 
-template <typename T>
-class HandleBase {};
+template <typename Element, typename Wrapper>
+class MutableWrappedPtrOperations : public WrappedPtrOperations<Element, Wrapper> {};
+
+template <typename T, typename Wrapper>
+class RootedBase : public MutableWrappedPtrOperations<T, Wrapper> {};
 
-template <typename T>
-class MutableHandleBase {};
+template <typename T, typename Wrapper>
+class HandleBase : public WrappedPtrOperations<T, Wrapper> {};
 
-template <typename T>
-class HeapBase {};
+template <typename T, typename Wrapper>
+class MutableHandleBase : public MutableWrappedPtrOperations<T, Wrapper> {};
+
+template <typename T, typename Wrapper>
+class HeapBase : public MutableWrappedPtrOperations<T, Wrapper> {};
 
 // Cannot use FOR_EACH_HEAP_ABLE_GC_POINTER_TYPE, as this would import too many macros into scope
 template <typename T> struct IsHeapConstructibleType    { static constexpr bool value = false; };
 #define DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE(T) \
     template <> struct IsHeapConstructibleType<T> { static constexpr bool value = true; };
 FOR_EACH_PUBLIC_GC_POINTER_TYPE(DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE)
 FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE)
 #undef DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE
 
-template <typename T>
-class PersistentRootedBase {};
+template <typename T, typename Wrapper>
+class PersistentRootedBase : public MutableWrappedPtrOperations<T, Wrapper> {};
 
 static void* const ConstNullValue = nullptr;
 
 namespace gc {
 struct Cell;
 template<typename T>
 struct PersistentRootedMarker;
 } /* namespace gc */
@@ -218,17 +224,17 @@ AssertGCThingIsNotAnObjectSubclass(js::g
  * keep the pointed-to GC thing alive.
  *
  * Heap<T> objects should only be used on the heap. GC references stored on the
  * C/C++ stack must use Rooted/Handle/MutableHandle instead.
  *
  * Type T must be a public GC pointer type.
  */
 template <typename T>
-class Heap : public js::HeapBase<T>
+class Heap : public js::HeapBase<T, Heap<T>>
 {
     // Please note: this can actually also be used by nsXBLMaybeCompiled<T>, for legacy reasons.
     static_assert(js::IsHeapConstructibleType<T>::value,
                   "Type T must be a public GC pointer type");
   public:
     using ElementType = T;
 
     Heap() {
@@ -357,17 +363,17 @@ ScriptIsMarkedGray(const Heap<JSScript*>
  * The considerations to keep in mind when using a TenuredHeap<T> vs a normal
  * Heap<T> are:
  *
  *  - It is invalid for a TenuredHeap<T> to refer to a non-tenured thing.
  *  - It is however valid for a Heap<T> to refer to a tenured thing.
  *  - It is not possible to store flag bits in a Heap<T>.
  */
 template <typename T>
-class TenuredHeap : public js::HeapBase<T>
+class TenuredHeap : public js::HeapBase<T, TenuredHeap<T>>
 {
   public:
     using ElementType = T;
 
     TenuredHeap() : bits(0) {
         static_assert(sizeof(T) == sizeof(TenuredHeap<T>),
                       "TenuredHeap<T> must be binary compatible with T.");
     }
@@ -440,17 +446,17 @@ class TenuredHeap : public js::HeapBase<
  * Reference to a T that has been rooted elsewhere. This is most useful
  * as a parameter type, which guarantees that the T lvalue is properly
  * rooted. See "Move GC Stack Rooting" above.
  *
  * If you want to add additional methods to Handle for a specific
  * specialization, define a HandleBase<T> specialization containing them.
  */
 template <typename T>
-class MOZ_NONHEAP_CLASS Handle : public js::HandleBase<T>
+class MOZ_NONHEAP_CLASS Handle : public js::HandleBase<T, Handle<T>>
 {
     friend class JS::MutableHandle<T>;
 
   public:
     using ElementType = T;
 
     /* Creates a handle from a handle of a type convertible to T. */
     template <typename S>
@@ -530,17 +536,17 @@ class MOZ_NONHEAP_CLASS Handle : public 
  * Similar to a handle, but the underlying storage can be changed. This is
  * useful for outparams.
  *
  * If you want to add additional methods to MutableHandle for a specific
  * specialization, define a MutableHandleBase<T> specialization containing
  * them.
  */
 template <typename T>
-class MOZ_STACK_CLASS MutableHandle : public js::MutableHandleBase<T>
+class MOZ_STACK_CLASS MutableHandle : public js::MutableHandleBase<T, MutableHandle<T>>
 {
   public:
     using ElementType = T;
 
     inline MOZ_IMPLICIT MutableHandle(Rooted<T>* root);
     inline MOZ_IMPLICIT MutableHandle(PersistentRooted<T>* root);
 
   private:
@@ -742,17 +748,17 @@ namespace JS {
  * Local variable of type T whose value is always rooted. This is typically
  * used for local variables, or for non-rooted values being passed to a
  * function that requires a handle, e.g. Foo(Root<T>(cx, x)).
  *
  * If you want to add additional methods to Rooted for a specific
  * specialization, define a RootedBase<T> specialization containing them.
  */
 template <typename T>
-class MOZ_RAII Rooted : public js::RootedBase<T>
+class MOZ_RAII Rooted : public js::RootedBase<T, Rooted<T>>
 {
     inline void registerWithRootLists(js::RootedListHeads& roots) {
         this->stack = &roots[JS::MapTypeToRootKind<T>::kind];
         this->prev = *stack;
         *stack = reinterpret_cast<Rooted<void*>*>(this);
     }
 
     inline js::RootedListHeads& rootLists(JS::RootingContext* cx) {
@@ -840,45 +846,45 @@ namespace js {
  * class-querying and downcasting operations.
  *
  * Given a Rooted<JSObject*> obj, one can view
  *   Handle<StringObject*> h = obj.as<StringObject*>();
  * as an optimization of
  *   Rooted<StringObject*> rooted(cx, &obj->as<StringObject*>());
  *   Handle<StringObject*> h = rooted;
  */
-template <>
-class RootedBase<JSObject*>
+template <typename Container>
+class RootedBase<JSObject*, Container> : public MutableWrappedPtrOperations<JSObject*, Container>
 {
   public:
     template <class U>
     JS::Handle<U*> as() const;
 };
 
 /**
  * Augment the generic Handle<T> interface when T = JSObject* with
  * downcasting operations.
  *
  * Given a Handle<JSObject*> obj, one can view
  *   Handle<StringObject*> h = obj.as<StringObject*>();
  * as an optimization of
  *   Rooted<StringObject*> rooted(cx, &obj->as<StringObject*>());
  *   Handle<StringObject*> h = rooted;
  */
-template <>
-class HandleBase<JSObject*>
+template <typename Container>
+class HandleBase<JSObject*, Container> : public WrappedPtrOperations<JSObject*, Container>
 {
   public:
     template <class U>
     JS::Handle<U*> as() const;
 };
 
 /** Interface substitute for Rooted<T> which does not root the variable's memory. */
 template <typename T>
-class MOZ_RAII FakeRooted : public RootedBase<T>
+class MOZ_RAII FakeRooted : public RootedBase<T, FakeRooted<T>>
 {
   public:
     using ElementType = T;
 
     template <typename CX>
     explicit FakeRooted(CX* cx) : ptr(JS::GCPolicy<T>::initial()) {}
 
     template <typename CX>
@@ -896,17 +902,17 @@ class MOZ_RAII FakeRooted : public Roote
         ptr = value;
     }
 
     FakeRooted(const FakeRooted&) = delete;
 };
 
 /** Interface substitute for MutableHandle<T> which is not required to point to rooted memory. */
 template <typename T>
-class FakeMutableHandle : public js::MutableHandleBase<T>
+class FakeMutableHandle : public js::MutableHandleBase<T, FakeMutableHandle<T>>
 {
   public:
     using ElementType = T;
 
     MOZ_IMPLICIT FakeMutableHandle(T* t) {
         ptr = t;
     }
 
@@ -1065,17 +1071,17 @@ MutableHandle<T>::MutableHandle(Persiste
  * In the context of Firefox, this is a severe restriction: almost everything in
  * Firefox is owned by some JS object or another, so using PersistentRooted in
  * such objects would introduce leaks. For these kinds of edges, Heap<T> or
  * TenuredHeap<T> would be better types. It's up to the implementor of the type
  * containing Heap<T> or TenuredHeap<T> members to make sure their referents get
  * marked when the object itself is marked.
  */
 template<typename T>
-class PersistentRooted : public js::PersistentRootedBase<T>,
+class PersistentRooted : public js::RootedBase<T, PersistentRooted<T>>,
                          private mozilla::LinkedListElement<PersistentRooted<T>>
 {
     using ListBase = mozilla::LinkedListElement<PersistentRooted<T>>;
 
     friend class mozilla::LinkedList<PersistentRooted>;
     friend class mozilla::LinkedListElement<PersistentRooted>;
 
     void registerWithRootLists(js::RootLists& roots) {
@@ -1225,54 +1231,35 @@ class JS_PUBLIC_API(ObjectPtr)
     explicit operator bool() const { return value.unbarrieredGet(); }
     explicit operator bool() { return value.unbarrieredGet(); }
 };
 
 } /* namespace JS */
 
 namespace js {
 
-template <typename Outer, typename T, typename D>
-class UniquePtrOperations
+template <typename T, typename D, typename Container>
+class WrappedPtrOperations<UniquePtr<T, D>, Container>
 {
-    const UniquePtr<T, D>& uniquePtr() const { return static_cast<const Outer*>(this)->get(); }
+    const UniquePtr<T, D>& uniquePtr() const { return static_cast<const Container*>(this)->get(); }
 
   public:
     explicit operator bool() const { return !!uniquePtr(); }
 };
 
-template <typename Outer, typename T, typename D>
-class MutableUniquePtrOperations : public UniquePtrOperations<Outer, T, D>
+template <typename T, typename D, typename Container>
+class MutableWrappedPtrOperations<UniquePtr<T, D>, Container>
+  : public WrappedPtrOperations<UniquePtr<T, D>, Container>
 {
-    UniquePtr<T, D>& uniquePtr() { return static_cast<Outer*>(this)->get(); }
+    UniquePtr<T, D>& uniquePtr() { return static_cast<Container*>(this)->get(); }
 
   public:
     MOZ_MUST_USE typename UniquePtr<T, D>::Pointer release() { return uniquePtr().release(); }
 };
 
-template <typename T, typename D>
-class RootedBase<UniquePtr<T, D>>
-  : public MutableUniquePtrOperations<JS::Rooted<UniquePtr<T, D>>, T, D>
-{ };
-
-template <typename T, typename D>
-class MutableHandleBase<UniquePtr<T, D>>
-  : public MutableUniquePtrOperations<JS::MutableHandle<UniquePtr<T, D>>, T, D>
-{ };
-
-template <typename T, typename D>
-class HandleBase<UniquePtr<T, D>>
-  : public UniquePtrOperations<JS::Handle<UniquePtr<T, D>>, T, D>
-{ };
-
-template <typename T, typename D>
-class PersistentRootedBase<UniquePtr<T, D>>
-  : public MutableUniquePtrOperations<JS::PersistentRooted<UniquePtr<T, D>>, T, D>
-{ };
-
 namespace gc {
 
 template <typename T, typename TraceCallbacks>
 void
 CallTraceCallbackOnNonHeap(T* v, const TraceCallbacks& aCallbacks, const char* aName, void* aClosure)
 {
     static_assert(sizeof(T) == sizeof(JS::Heap<T>), "T and Heap<T> must be compatible.");
     MOZ_ASSERT(v);
--- a/js/public/SweepingAPI.h
+++ b/js/public/SweepingAPI.h
@@ -4,33 +4,28 @@
  * 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 js_SweepingAPI_h
 #define js_SweepingAPI_h
 
 #include "js/HeapAPI.h"
 
-namespace js {
-template <typename T>
-class WeakCacheBase {};
-} // namespace js
-
 namespace JS {
 template <typename T> class WeakCache;
 
 namespace shadow {
 JS_PUBLIC_API(void)
 RegisterWeakCache(JS::Zone* zone, JS::WeakCache<void*>* cachep);
 } // namespace shadow
 
 // A WeakCache stores the given Sweepable container and links itself into a
 // list of such caches that are swept during each GC.
 template <typename T>
-class WeakCache : public js::WeakCacheBase<T>,
+class WeakCache : public js::MutableWrappedPtrOperations<T, WeakCache<T>>,
                   private mozilla::LinkedListElement<WeakCache<T>>
 {
     friend class mozilla::LinkedListElement<WeakCache<T>>;
     friend class mozilla::LinkedList<WeakCache<T>>;
 
     WeakCache() = delete;
     WeakCache(const WeakCache&) = delete;
 
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -1283,30 +1283,28 @@ struct BarrierMethods<JS::Value>
     static void postBarrier(JS::Value* v, const JS::Value& prev, const JS::Value& next) {
         JS::HeapValuePostBarrier(v, prev, next);
     }
     static void exposeToJS(const JS::Value& v) {
         JS::ExposeValueToActiveJS(v);
     }
 };
 
-template <class Outer> class MutableValueOperations;
+template <class Wrapper> class MutableValueOperations;
 
 /**
  * A class designed for CRTP use in implementing the non-mutating parts of the
- * Value interface in Value-like classes.  Outer must be a class inheriting
- * ValueOperations<Outer> with a visible get() method returning a const
- * reference to the Value abstracted by Outer.
+ * Value interface in Value-like classes.  Wrapper must be a class inheriting
+ * ValueOperations<Wrapper> with a visible get() method returning a const
+ * reference to the Value abstracted by Wrapper.
  */
-template <class Outer>
-class ValueOperations
+template <class Wrapper>
+class WrappedPtrOperations<JS::Value, Wrapper>
 {
-    friend class MutableValueOperations<Outer>;
-
-    const JS::Value& value() const { return static_cast<const Outer*>(this)->get(); }
+    const JS::Value& value() const { return static_cast<const Wrapper*>(this)->get(); }
 
   public:
     bool isUndefined() const { return value().isUndefined(); }
     bool isNull() const { return value().isNull(); }
     bool isBoolean() const { return value().isBoolean(); }
     bool isTrue() const { return value().isTrue(); }
     bool isFalse() const { return value().isFalse(); }
     bool isNumber() const { return value().isNumber(); }
@@ -1341,24 +1339,24 @@ class ValueOperations
     JSValueType extractNonDoubleType() const { return value().extractNonDoubleType(); }
 
     JSWhyMagic whyMagic() const { return value().whyMagic(); }
     uint32_t magicUint32() const { return value().magicUint32(); }
 };
 
 /**
  * A class designed for CRTP use in implementing all the mutating parts of the
- * Value interface in Value-like classes.  Outer must be a class inheriting
- * MutableValueOperations<Outer> with visible get() methods returning const and
- * non-const references to the Value abstracted by Outer.
+ * Value interface in Value-like classes.  Wrapper must be a class inheriting
+ * MutableWrappedPtrOperations<Wrapper> with visible get() methods returning const and
+ * non-const references to the Value abstracted by Wrapper.
  */
-template <class Outer>
-class MutableValueOperations : public ValueOperations<Outer>
+template <class Wrapper>
+class MutableWrappedPtrOperations<JS::Value, Wrapper> : public WrappedPtrOperations<JS::Value, Wrapper>
 {
-    JS::Value& value() { return static_cast<Outer*>(this)->get(); }
+    JS::Value& value() { return static_cast<Wrapper*>(this)->get(); }
 
   public:
     void setNull() { value().setNull(); }
     void setUndefined() { value().setUndefined(); }
     void setInt32(int32_t i) { value().setInt32(i); }
     void setDouble(double d) { value().setDouble(d); }
     void setNaN() { setDouble(JS::GenericNaN()); }
     void setBoolean(bool b) { value().setBoolean(b); }
@@ -1373,23 +1371,19 @@ class MutableValueOperations : public Va
     void setPrivateUint32(uint32_t ui) { this->value().setPrivateUint32(ui); }
     void setPrivateGCThing(js::gc::Cell* cell) { this->value().setPrivateGCThing(cell); }
 };
 
 /*
  * Augment the generic Heap<T> interface when T = Value with
  * type-querying, value-extracting, and mutating operations.
  */
-template <>
-class HeapBase<JS::Value> : public ValueOperations<JS::Heap<JS::Value> >
+template <typename Wrapper>
+class HeapBase<JS::Value, Wrapper> : public WrappedPtrOperations<JS::Value, Wrapper>
 {
-    typedef JS::Heap<JS::Value> Outer;
-
-    friend class ValueOperations<Outer>;
-
     void setBarriered(const JS::Value& v) {
         *static_cast<JS::Heap<JS::Value>*>(this) = v;
     }
 
   public:
     void setNull() { setBarriered(JS::NullValue()); }
     void setUndefined() { setBarriered(JS::UndefinedValue()); }
     void setInt32(int32_t i) { setBarriered(JS::Int32Value(i)); }
@@ -1426,32 +1420,16 @@ class HeapBase<JS::Value> : public Value
     void setObjectOrNull(JSObject* arg) {
         if (arg)
             setObject(*arg);
         else
             setNull();
     }
 };
 
-template <>
-class HandleBase<JS::Value> : public ValueOperations<JS::Handle<JS::Value> >
-{};
-
-template <>
-class MutableHandleBase<JS::Value> : public MutableValueOperations<JS::MutableHandle<JS::Value> >
-{};
-
-template <>
-class RootedBase<JS::Value> : public MutableValueOperations<JS::Rooted<JS::Value> >
-{};
-
-template <>
-class PersistentRootedBase<JS::Value> : public MutableValueOperations<JS::PersistentRooted<JS::Value>>
-{};
-
 /*
  * If the Value is a GC pointer type, convert to that type and call |f| with
  * the pointer. If the Value is not a GC type, calls F::defaultValue.
  */
 template <typename F, typename... Args>
 auto
 DispatchTyped(F f, const JS::Value& val, Args&&... args)
   -> decltype(f(static_cast<JSObject*>(nullptr), mozilla::Forward<Args>(args)...))
--- a/js/src/builtin/MapObject.h
+++ b/js/src/builtin/MapObject.h
@@ -48,24 +48,32 @@ class HashableValue
     HashableValue trace(JSTracer* trc) const;
     Value get() const { return value.get(); }
 
     void trace(JSTracer* trc) {
         TraceEdge(trc, &value, "HashableValue");
     }
 };
 
-template <>
-class RootedBase<HashableValue> {
+template <typename Wrapper>
+class WrappedPtrOperations<HashableValue, Wrapper>
+{
+  public:
+    Value value() const {
+        return static_cast<const Wrapper*>(this)->get().get();
+    }
+};
+
+template <typename Wrapper>
+class MutableWrappedPtrOperations<HashableValue, Wrapper>
+  : public WrappedPtrOperations<HashableValue, Wrapper>
+{
   public:
     MOZ_MUST_USE bool setValue(JSContext* cx, HandleValue v) {
-        return static_cast<JS::Rooted<HashableValue>*>(this)->get().setValue(cx, v);
-    }
-    Value value() const {
-        return static_cast<const JS::Rooted<HashableValue>*>(this)->get().get();
+        return static_cast<Wrapper*>(this)->get().setValue(cx, v);
     }
 };
 
 template <class Key, class Value, class OrderedHashPolicy, class AllocPolicy>
 class OrderedHashMap;
 
 template <class T, class OrderedHashPolicy, class AllocPolicy>
 class OrderedHashSet;
--- a/js/src/ds/TraceableFifo.h
+++ b/js/src/ds/TraceableFifo.h
@@ -47,82 +47,42 @@ class TraceableFifo : public js::Fifo<T,
     void trace(JSTracer* trc) {
         for (size_t i = 0; i < this->front_.length(); ++i)
             JS::GCPolicy<T>::trace(trc, &this->front_[i], "fifo element");
         for (size_t i = 0; i < this->rear_.length(); ++i)
             JS::GCPolicy<T>::trace(trc, &this->rear_[i], "fifo element");
     }
 };
 
-template <typename Outer, typename T, size_t Capacity, typename AllocPolicy>
-class TraceableFifoOperations
+template <typename Wrapper, typename T, size_t Capacity, typename AllocPolicy>
+class WrappedPtrOperations<TraceableFifo<T, Capacity, AllocPolicy>, Wrapper>
 {
     using TF = TraceableFifo<T, Capacity, AllocPolicy>;
-    const TF& fifo() const { return static_cast<const Outer*>(this)->extract(); }
+    const TF& fifo() const { return static_cast<const Wrapper*>(this)->get(); }
 
   public:
     size_t length() const { return fifo().length(); }
     bool empty() const { return fifo().empty(); }
     const T& front() const { return fifo().front(); }
 };
 
-template <typename Outer, typename T, size_t Capacity, typename AllocPolicy>
-class MutableTraceableFifoOperations
-  : public TraceableFifoOperations<Outer, T, Capacity, AllocPolicy>
+template <typename Wrapper, typename T, size_t Capacity, typename AllocPolicy>
+class MutableWrappedPtrOperations<TraceableFifo<T, Capacity, AllocPolicy>, Wrapper>
+  : public WrappedPtrOperations<TraceableFifo<T, Capacity, AllocPolicy>, Wrapper>
 {
     using TF = TraceableFifo<T, Capacity, AllocPolicy>;
-    TF& fifo() { return static_cast<Outer*>(this)->extract(); }
+    TF& fifo() { return static_cast<Wrapper*>(this)->get(); }
 
   public:
     T& front() { return fifo().front(); }
 
     template<typename U> bool pushBack(U&& u) { return fifo().pushBack(mozilla::Forward<U>(u)); }
     template<typename... Args> bool emplaceBack(Args&&... args) {
         return fifo().emplaceBack(mozilla::Forward<Args...>(args...));
     }
 
     bool popFront() { return fifo().popFront(); }
     void clear() { fifo().clear(); }
 };
 
-template <typename A, size_t B, typename C>
-class RootedBase<TraceableFifo<A,B,C>>
-  : public MutableTraceableFifoOperations<JS::Rooted<TraceableFifo<A,B,C>>, A,B,C>
-{
-    using TF = TraceableFifo<A,B,C>;
-
-    friend class TraceableFifoOperations<JS::Rooted<TF>, A,B,C>;
-    const TF& extract() const { return *static_cast<const JS::Rooted<TF>*>(this)->address(); }
-
-    friend class MutableTraceableFifoOperations<JS::Rooted<TF>, A,B,C>;
-    TF& extract() { return *static_cast<JS::Rooted<TF>*>(this)->address(); }
-};
-
-template <typename A, size_t B, typename C>
-class MutableHandleBase<TraceableFifo<A,B,C>>
-  : public MutableTraceableFifoOperations<JS::MutableHandle<TraceableFifo<A,B,C>>, A,B,C>
-{
-    using TF = TraceableFifo<A,B,C>;
-
-    friend class TraceableFifoOperations<JS::MutableHandle<TF>, A,B,C>;
-    const TF& extract() const {
-        return *static_cast<const JS::MutableHandle<TF>*>(this)->address();
-    }
-
-    friend class MutableTraceableFifoOperations<JS::MutableHandle<TF>, A,B,C>;
-    TF& extract() { return *static_cast<JS::MutableHandle<TF>*>(this)->address(); }
-};
-
-template <typename A, size_t B, typename C>
-class HandleBase<TraceableFifo<A,B,C>>
-  : public TraceableFifoOperations<JS::Handle<TraceableFifo<A,B,C>>, A,B,C>
-{
-    using TF = TraceableFifo<A,B,C>;
-
-    friend class TraceableFifoOperations<JS::Handle<TF>, A,B,C>;
-    const TF& extract() const {
-        return *static_cast<const JS::Handle<TF>*>(this)->address();
-    }
-};
-
 } // namespace js
 
 #endif // js_TraceableFifo_h
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -311,25 +311,19 @@ struct InternalBarrierMethods<jsid>
 {
     static bool isMarkable(jsid id) { return JSID_IS_GCTHING(id); }
     static bool isMarkableTaggedPointer(jsid id) { return isMarkable(id); }
 
     static void preBarrier(jsid id) { DispatchTyped(PreBarrierFunctor<jsid>(), id); }
     static void postBarrier(jsid* idp, jsid prev, jsid next) {}
 };
 
-// Barrier classes can use Mixins to add methods to a set of barrier
-// instantiations, to make the barriered thing look and feel more like the
-// thing itself.
-template <typename T>
-class BarrieredBaseMixins {};
-
 // Base class of all barrier types.
 template <typename T>
-class BarrieredBase : public BarrieredBaseMixins<T>
+class BarrieredBase
 {
   protected:
     // BarrieredBase is not directly instantiable.
     explicit BarrieredBase(const T& v) : value(v) {}
 
     // Storage for all barrier classes. |value| must be a GC thing reference
     // type: either a direct pointer to a GC thing or a supported tagged
     // pointer that can reference GC things, such as JS::Value or jsid. Nested
@@ -340,19 +334,22 @@ class BarrieredBase : public BarrieredBa
     // Note: this is public because C++ cannot friend to a specific template instantiation.
     // Friending to the generic template leads to a number of unintended consequences, including
     // template resolution ambiguity and a circular dependency with Tracing.h.
     T* unsafeUnbarrieredForTracing() { return &value; }
 };
 
 // Base class for barriered pointer types that intercept only writes.
 template <class T>
-class WriteBarrieredBase : public BarrieredBase<T>
+class WriteBarrieredBase : public BarrieredBase<T>,
+                           public WrappedPtrOperations<T, WriteBarrieredBase<T>>
 {
   protected:
+    using BarrieredBase<T>::value;
+
     // WriteBarrieredBase is not directly instantiable.
     explicit WriteBarrieredBase(const T& v) : BarrieredBase<T>(v) {}
 
   public:
     using ElementType = T;
 
     DECLARE_POINTER_CONSTREF_OPS(T);
 
@@ -561,18 +558,22 @@ class ReadBarrieredBase : public Barrier
 // Incremental GC requires that weak pointers have read barriers. See the block
 // comment at the top of Barrier.h for a complete discussion of why.
 //
 // Note that this class also has post-barriers, so is safe to use with nursery
 // pointers. However, when used as a hashtable key, care must still be taken to
 // insert manual post-barriers on the table for rekeying if the key is based in
 // any way on the address of the object.
 template <typename T>
-class ReadBarriered : public ReadBarrieredBase<T>
+class ReadBarriered : public ReadBarrieredBase<T>,
+                      public WrappedPtrOperations<T, ReadBarriered<T>>
 {
+  protected:
+    using ReadBarrieredBase<T>::value;
+
   public:
     ReadBarriered() : ReadBarrieredBase<T>(JS::GCPolicy<T>::initial()) {}
 
     // It is okay to add barriers implicitly.
     MOZ_IMPLICIT ReadBarriered(const T& v) : ReadBarrieredBase<T>(v) {
         this->post(JS::GCPolicy<T>::initial(), v);
     }
 
@@ -629,22 +630,16 @@ class ReadBarriered : public ReadBarrier
     }
 };
 
 // A WeakRef pointer does not hold its target live and is automatically nulled
 // out when the GC discovers that it is not reachable from any other path.
 template <typename T>
 using WeakRef = ReadBarriered<T>;
 
-// Add Value operations to all Barrier types. Note, this must be defined before
-// HeapSlot for HeapSlot's base to get these operations.
-template <>
-class BarrieredBaseMixins<JS::Value> : public ValueOperations<WriteBarrieredBase<JS::Value>>
-{};
-
 // A pre- and post-barriered Value that is specialized to be aware that it
 // resides in a slots or elements vector. This allows it to be relocated in
 // memory, but with substantially less overhead than a HeapPtr.
 class HeapSlot : public WriteBarrieredBase<Value>
 {
   public:
     enum Kind {
         Slot = 0,
--- a/js/src/jsapi-tests/testGCExactRooting.cpp
+++ b/js/src/jsapi-tests/testGCExactRooting.cpp
@@ -52,29 +52,20 @@ struct MyContainer
     MyContainer() : obj(nullptr), str(nullptr) {}
     void trace(JSTracer* trc) {
         js::TraceNullableEdge(trc, &obj, "test container");
         js::TraceNullableEdge(trc, &str, "test container");
     }
 };
 
 namespace js {
-template <>
-struct RootedBase<MyContainer> {
-    HeapPtr<JSObject*>& obj() { return static_cast<Rooted<MyContainer>*>(this)->get().obj; }
-    HeapPtr<JSString*>& str() { return static_cast<Rooted<MyContainer>*>(this)->get().str; }
-};
-template <>
-struct PersistentRootedBase<MyContainer> {
-    HeapPtr<JSObject*>& obj() {
-        return static_cast<PersistentRooted<MyContainer>*>(this)->get().obj;
-    }
-    HeapPtr<JSString*>& str() {
-        return static_cast<PersistentRooted<MyContainer>*>(this)->get().str;
-    }
+template <typename Wrapper>
+struct MutableWrappedPtrOperations<MyContainer, Wrapper> {
+    HeapPtr<JSObject*>& obj() { return static_cast<Wrapper*>(this)->get().obj; }
+    HeapPtr<JSString*>& str() { return static_cast<Wrapper*>(this)->get().str; }
 };
 } // namespace js
 
 BEGIN_TEST(testGCRootedStaticStructInternalStackStorageAugmented)
 {
     JS::Rooted<MyContainer> container(cx);
     container.obj() = JS_NewObject(cx, nullptr);
     container.str() = JS_NewStringCopyZ(cx, "Hello");
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2564,20 +2564,24 @@ struct JS_PUBLIC_API(PropertyDescriptor)
     PropertyDescriptor()
       : obj(nullptr), attrs(0), getter(nullptr), setter(nullptr), value(JS::UndefinedValue())
     {}
 
     static void trace(PropertyDescriptor* self, JSTracer* trc) { self->trace(trc); }
     void trace(JSTracer* trc);
 };
 
-template <typename Outer>
-class PropertyDescriptorOperations
+} // namespace JS
+
+namespace js {
+
+template <typename Wrapper>
+class WrappedPtrOperations<JS::PropertyDescriptor, Wrapper>
 {
-    const PropertyDescriptor& desc() const { return static_cast<const Outer*>(this)->get(); }
+    const JS::PropertyDescriptor& desc() const { return static_cast<const Wrapper*>(this)->get(); }
 
     bool has(unsigned bit) const {
         MOZ_ASSERT(bit != 0);
         MOZ_ASSERT((bit & (bit - 1)) == 0);  // only a single bit
         return (desc().attrs & bit) != 0;
     }
 
     bool hasAny(unsigned bits) const {
@@ -2696,51 +2700,52 @@ class PropertyDescriptorOperations
     void assertCompleteIfFound() const {
 #ifdef DEBUG
         if (object())
             assertComplete();
 #endif
     }
 };
 
-template <typename Outer>
-class MutablePropertyDescriptorOperations : public PropertyDescriptorOperations<Outer>
+template <typename Wrapper>
+class MutableWrappedPtrOperations<JS::PropertyDescriptor, Wrapper>
+    : public js::WrappedPtrOperations<JS::PropertyDescriptor, Wrapper>
 {
-    PropertyDescriptor& desc() { return static_cast<Outer*>(this)->get(); }
+    JS::PropertyDescriptor& desc() { return static_cast<Wrapper*>(this)->get(); }
 
   public:
     void clear() {
         object().set(nullptr);
         setAttributes(0);
         setGetter(nullptr);
         setSetter(nullptr);
         value().setUndefined();
     }
 
-    void initFields(HandleObject obj, HandleValue v, unsigned attrs,
+    void initFields(JS::HandleObject obj, JS::HandleValue v, unsigned attrs,
                     JSGetterOp getterOp, JSSetterOp setterOp) {
         MOZ_ASSERT(getterOp != JS_PropertyStub);
         MOZ_ASSERT(setterOp != JS_StrictPropertyStub);
 
         object().set(obj);
         value().set(v);
         setAttributes(attrs);
         setGetter(getterOp);
         setSetter(setterOp);
     }
 
-    void assign(PropertyDescriptor& other) {
+    void assign(JS::PropertyDescriptor& other) {
         object().set(other.obj);
         setAttributes(other.attrs);
         setGetter(other.getter);
         setSetter(other.setter);
         value().set(other.value);
     }
 
-    void setDataDescriptor(HandleValue v, unsigned attrs) {
+    void setDataDescriptor(JS::HandleValue v, unsigned attrs) {
         MOZ_ASSERT((attrs & ~(JSPROP_ENUMERATE |
                               JSPROP_PERMANENT |
                               JSPROP_READONLY |
                               JSPROP_IGNORE_ENUMERATE |
                               JSPROP_IGNORE_PERMANENT |
                               JSPROP_IGNORE_READONLY)) == 0);
         object().set(nullptr);
         setAttributes(attrs);
@@ -2805,36 +2810,17 @@ class MutablePropertyDescriptorOperation
     }
     JS::MutableHandleObject setterObject() {
         MOZ_ASSERT(this->hasSetterObject());
         return JS::MutableHandleObject::fromMarkedLocation(
                 reinterpret_cast<JSObject**>(&desc().setter));
     }
 };
 
-} /* namespace JS */
-
-namespace js {
-
-template <>
-class RootedBase<JS::PropertyDescriptor>
-  : public JS::MutablePropertyDescriptorOperations<JS::Rooted<JS::PropertyDescriptor>>
-{};
-
-template <>
-class HandleBase<JS::PropertyDescriptor>
-  : public JS::PropertyDescriptorOperations<JS::Handle<JS::PropertyDescriptor>>
-{};
-
-template <>
-class MutableHandleBase<JS::PropertyDescriptor>
-  : public JS::MutablePropertyDescriptorOperations<JS::MutableHandle<JS::PropertyDescriptor>>
-{};
-
-} /* namespace js */
+} // namespace js
 
 namespace JS {
 
 extern JS_PUBLIC_API(bool)
 ObjectToCompletePropertyDescriptor(JSContext* cx,
                                    JS::HandleObject obj,
                                    JS::HandleValue descriptor,
                                    JS::MutableHandle<PropertyDescriptor> desc);
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -715,18 +715,19 @@ JSCompartment::finishRoots()
         nonSyntacticLexicalEnvironments_->clear();
 }
 
 void
 JSCompartment::sweepAfterMinorGC(JSTracer* trc)
 {
     globalWriteBarriered = 0;
 
-    if (innerViews.needsSweepAfterMinorGC())
-        innerViews.sweepAfterMinorGC();
+    InnerViewTable& table = innerViews.get();
+    if (table.needsSweepAfterMinorGC())
+        table.sweepAfterMinorGC();
 
     crossCompartmentWrappers.sweepAfterMinorGC(trc);
 }
 
 void
 JSCompartment::sweepSavedStacks()
 {
     savedStacks_.sweep();
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -587,31 +587,33 @@ class JSObject : public js::gc::Cell
     static const size_t MAX_BYTE_SIZE = 4 * sizeof(void*) + 16 * sizeof(JS::Value);
 
   private:
     JSObject() = delete;
     JSObject(const JSObject& other) = delete;
     void operator=(const JSObject& other) = delete;
 };
 
-template <class U>
+template <typename Wrapper>
+template <typename U>
 MOZ_ALWAYS_INLINE JS::Handle<U*>
-js::RootedBase<JSObject*>::as() const
+js::RootedBase<JSObject*, Wrapper>::as() const
 {
-    const JS::Rooted<JSObject*>& self = *static_cast<const JS::Rooted<JSObject*>*>(this);
-    MOZ_ASSERT(self->is<U>());
+    const Wrapper& self = *static_cast<const Wrapper*>(this);
+    MOZ_ASSERT(self->template is<U>());
     return Handle<U*>::fromMarkedLocation(reinterpret_cast<U* const*>(self.address()));
 }
 
+template <typename Wrapper>
 template <class U>
 MOZ_ALWAYS_INLINE JS::Handle<U*>
-js::HandleBase<JSObject*>::as() const
+js::HandleBase<JSObject*, Wrapper>::as() const
 {
     const JS::Handle<JSObject*>& self = *static_cast<const JS::Handle<JSObject*>*>(this);
-    MOZ_ASSERT(self->is<U>());
+    MOZ_ASSERT(self->template is<U>());
     return Handle<U*>::fromMarkedLocation(reinterpret_cast<U* const*>(self.address()));
 }
 
 /*
  * The only sensible way to compare JSObject with == is by identity. We use
  * const& instead of * as a syntactic way to assert non-null. This leads to an
  * abundance of address-of operators to identity. Hence this overload.
  */
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -345,17 +345,17 @@ ArrayBufferObject::detach(JSContext* cx,
             oomUnsafe.crash("ArrayBufferObject::detach");
         MarkObjectGroupFlags(cx, cx->global(), OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER);
         cx->compartment()->detachedTypedObjects = 1;
     }
 
     // Update all views of the buffer to account for the buffer having been
     // detached, and clear the buffer's data and list of views.
 
-    auto& innerViews = cx->compartment()->innerViews;
+    auto& innerViews = cx->compartment()->innerViews.get();
     if (InnerViewTable::ViewVector* views = innerViews.maybeViewsUnbarriered(buffer)) {
         for (size_t i = 0; i < views->length(); i++)
             NoteViewBufferWasDetached((*views)[i], newContents, cx);
         innerViews.removeViews(buffer);
     }
     if (buffer->firstView()) {
         if (buffer->forInlineTypedObject()) {
             // The buffer points to inline data in its first view, so to keep
@@ -420,17 +420,17 @@ ArrayBufferObject::changeContents(JSCont
     MOZ_RELEASE_ASSERT(!isWasm());
     MOZ_ASSERT(!forInlineTypedObject());
 
     // Change buffer contents.
     uint8_t* oldDataPointer = dataPointer();
     setNewData(cx->runtime()->defaultFreeOp(), newContents, ownsState);
 
     // Update all views.
-    auto& innerViews = cx->compartment()->innerViews;
+    auto& innerViews = cx->compartment()->innerViews.get();
     if (InnerViewTable::ViewVector* views = innerViews.maybeViewsUnbarriered(this)) {
         for (size_t i = 0; i < views->length(); i++)
             changeViewContents(cx, (*views)[i], oldDataPointer, newContents);
     }
     if (firstView())
         changeViewContents(cx, firstView(), oldDataPointer, newContents);
 }
 
--- a/js/src/vm/ArrayBufferObject.h
+++ b/js/src/vm/ArrayBufferObject.h
@@ -550,17 +550,16 @@ template<> inline bool TypeIsUnsigned<ui
 // Per-compartment table that manages the relationship between array buffers
 // and the views that use their storage.
 class InnerViewTable
 {
   public:
     typedef Vector<ArrayBufferViewObject*, 1, SystemAllocPolicy> ViewVector;
 
     friend class ArrayBufferObject;
-    friend class WeakCacheBase<InnerViewTable>;
 
   private:
     struct MapGCPolicy {
         static bool needsSweep(JSObject** key, ViewVector* value) {
             return InnerViewTable::sweepEntry(key, *value);
         }
     };
 
@@ -611,33 +610,25 @@ class InnerViewTable
 
     bool needsSweepAfterMinorGC() const {
         return !nurseryKeys.empty() || !nurseryKeysValid;
     }
 
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
 };
 
-template <>
-class WeakCacheBase<InnerViewTable>
+template <typename Wrapper>
+class MutableWrappedPtrOperations<InnerViewTable, Wrapper>
+    : public WrappedPtrOperations<InnerViewTable, Wrapper>
 {
     InnerViewTable& table() {
-        return static_cast<JS::WeakCache<InnerViewTable>*>(this)->get();
-    }
-    const InnerViewTable& table() const {
-        return static_cast<const JS::WeakCache<InnerViewTable>*>(this)->get();
+        return static_cast<Wrapper*>(this)->get();
     }
 
   public:
-    InnerViewTable::ViewVector* maybeViewsUnbarriered(ArrayBufferObject* obj) {
-        return table().maybeViewsUnbarriered(obj);
-    }
-    void removeViews(ArrayBufferObject* obj) { table().removeViews(obj); }
-    void sweepAfterMinorGC() { table().sweepAfterMinorGC(); }
-    bool needsSweepAfterMinorGC() const { return table().needsSweepAfterMinorGC(); }
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
         return table().sizeOfExcludingThis(mallocSizeOf);
     }
 };
 
 } // namespace js
 
 template <>
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -1530,21 +1530,17 @@ GetSuperEnvFunction(JSContext* cx, Inter
  * upon entry. ReservedRooted "borrows" a reserved Rooted variable and uses it
  * within a local scope, resetting the value to nullptr (or the appropriate
  * equivalent for T) at scope end. This avoids inserting/removing the Rooted
  * from the rooter list, while preventing stale values from being kept alive
  * unnecessarily.
  */
 
 template<typename T>
-class ReservedRootedBase {
-};
-
-template<typename T>
-class ReservedRooted : public ReservedRootedBase<T>
+class ReservedRooted : public RootedBase<T, ReservedRooted<T>>
 {
     Rooted<T>* savedRoot;
 
   public:
     ReservedRooted(Rooted<T>* root, const T& ptr) : savedRoot(root) {
         *root = ptr;
     }
 
@@ -1562,24 +1558,16 @@ class ReservedRooted : public ReservedRo
     MutableHandle<T> operator&() { return &*savedRoot; }
 
     DECLARE_NONPOINTER_ACCESSOR_METHODS(savedRoot->get())
     DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(savedRoot->get())
     DECLARE_POINTER_CONSTREF_OPS(T)
     DECLARE_POINTER_ASSIGN_OPS(ReservedRooted, T)
 };
 
-template <>
-class ReservedRootedBase<Value> : public ValueOperations<ReservedRooted<Value>>
-{};
-
-template <>
-class ReservedRootedBase<Scope*> : public ScopeCastOperation<ReservedRooted<Scope*>>
-{};
-
 static MOZ_NEVER_INLINE bool
 Interpret(JSContext* cx, RunState& state)
 {
 /*
  * Define macros for an interpreter loop. Opcode dispatch may be either by a
  * switch statement or by indirect goto (aka a threaded interpreter), depending
  * on compiler support.
  *
--- a/js/src/vm/JSONParser.h
+++ b/js/src/vm/JSONParser.h
@@ -251,18 +251,21 @@ class MOZ_STACK_CLASS JSONParser : publi
 
     void getTextPosition(uint32_t* column, uint32_t* line);
 
   private:
     JSONParser(const JSONParser& other) = delete;
     void operator=(const JSONParser& other) = delete;
 };
 
-template <typename CharT>
-struct RootedBase<JSONParser<CharT>> {
+template <typename CharT, typename Wrapper>
+class MutableWrappedPtrOperations<JSONParser<CharT>, Wrapper>
+  : public WrappedPtrOperations<JSONParser<CharT>, Wrapper>
+{
+  public:
     bool parse(MutableHandleValue vp) {
-        return static_cast<Rooted<JSONParser<CharT>>*>(this)->get().parse(vp);
+        return static_cast<Wrapper*>(this)->get().parse(vp);
     }
 };
 
 } /* namespace js */
 
 #endif /* vm_JSONParser_h */
--- a/js/src/vm/SavedStacks.h
+++ b/js/src/vm/SavedStacks.h
@@ -261,34 +261,16 @@ class SavedStacks {
             return true;
         }
 
         HeapPtr<JSAtom*> source;
         size_t line;
         uint32_t column;
     };
 
-    template <typename Outer>
-    struct LocationValueOperations {
-        JSAtom* source() const { return loc().source; }
-        size_t line() const { return loc().line; }
-        uint32_t column() const { return loc().column; }
-      private:
-        const LocationValue& loc() const { return static_cast<const Outer*>(this)->get(); }
-    };
-
-    template <typename Outer>
-    struct MutableLocationValueOperations : public LocationValueOperations<Outer> {
-        void setSource(JSAtom* v) { loc().source = v; }
-        void setLine(size_t v) { loc().line = v; }
-        void setColumn(uint32_t v) { loc().column = v; }
-      private:
-        LocationValue& loc() { return static_cast<Outer*>(this)->get(); }
-    };
-
   private:
     struct PCLocationHasher : public DefaultHasher<PCKey> {
         using ScriptPtrHasher = DefaultHasher<JSScript*>;
         using BytecodePtrHasher = DefaultHasher<jsbytecode*>;
 
         static HashNumber hash(const PCKey& key) {
             return mozilla::AddToHash(ScriptPtrHasher::hash(key.script),
                                       BytecodePtrHasher::hash(key.pc));
@@ -309,24 +291,41 @@ class SavedStacks {
     // it is no longer held by the table.
     using PCLocationMap = GCHashMap<PCKey, LocationValue, PCLocationHasher, SystemAllocPolicy>;
     PCLocationMap pcLocationMap;
 
     MOZ_MUST_USE bool getLocation(JSContext* cx, const FrameIter& iter,
                                   MutableHandle<LocationValue> locationp);
 };
 
-template <>
-class RootedBase<SavedStacks::LocationValue>
-  : public SavedStacks::MutableLocationValueOperations<JS::Rooted<SavedStacks::LocationValue>>
-{};
+template <typename Wrapper>
+struct WrappedPtrOperations<SavedStacks::LocationValue, Wrapper>
+{
+    JSAtom* source() const { return loc().source; }
+    size_t line() const { return loc().line; }
+    uint32_t column() const { return loc().column; }
+
+  private:
+    const SavedStacks::LocationValue& loc() const {
+        return static_cast<const Wrapper*>(this)->get();
+    }
+};
 
-template <>
-class MutableHandleBase<SavedStacks::LocationValue>
-  : public SavedStacks::MutableLocationValueOperations<JS::MutableHandle<SavedStacks::LocationValue>>
-{};
+template <typename Wrapper>
+struct MutableWrappedPtrOperations<SavedStacks::LocationValue, Wrapper>
+    : public WrappedPtrOperations<SavedStacks::LocationValue, Wrapper>
+{
+    void setSource(JSAtom* v) { loc().source = v; }
+    void setLine(size_t v) { loc().line = v; }
+    void setColumn(uint32_t v) { loc().column = v; }
+
+  private:
+    SavedStacks::LocationValue& loc() {
+        return static_cast<Wrapper*>(this)->get();
+    }
+};
 
 UTF8CharsZ
 BuildUTF8StackString(JSContext* cx, HandleObject stack);
 
 } /* namespace js */
 
 #endif /* vm_SavedStacks_h */
--- a/js/src/vm/Scope.h
+++ b/js/src/vm/Scope.h
@@ -17,16 +17,17 @@
 #include "gc/Policy.h"
 #include "js/UbiNode.h"
 #include "js/UniquePtr.h"
 #include "vm/Xdr.h"
 
 namespace js {
 
 class ModuleObject;
+class Scope;
 
 enum class BindingKind : uint8_t
 {
     Import,
     FormalParameter,
     Var,
     Let,
     Const,
@@ -178,16 +179,31 @@ class BindingLocation
 
     uint16_t argumentSlot() const {
         MOZ_ASSERT(kind_ == Kind::Argument);
         return mozilla::AssertedCast<uint16_t>(slot_);
     }
 };
 
 //
+// Allow using is<T> and as<T> on Rooted<Scope*> and Handle<Scope*>.
+//
+template <typename Wrapper>
+class WrappedPtrOperations<Scope*, Wrapper>
+{
+  public:
+    template <class U>
+    JS::Handle<U*> as() const {
+        const Wrapper& self = *static_cast<const Wrapper*>(this);
+        MOZ_ASSERT_IF(self, self->template is<U>());
+        return Handle<U*>::fromMarkedLocation(reinterpret_cast<U* const*>(self.address()));
+    }
+};
+
+//
 // The base class of all Scopes.
 //
 class Scope : public js::gc::TenuredCell
 {
     friend class GCMarker;
 
     // The kind determines data_.
     ScopeKind kind_;
@@ -1264,20 +1280,20 @@ class MOZ_STACK_CLASS ScopeIter
             TraceRoot(trc, &scope_, "scope iter scope");
     }
 };
 
 //
 // Specializations of Rooted containers for the iterators.
 //
 
-template <typename Outer>
-class BindingIterOperations
+template <typename Wrapper>
+class WrappedPtrOperations<BindingIter, Wrapper>
 {
-    const BindingIter& iter() const { return static_cast<const Outer*>(this)->get(); }
+    const BindingIter& iter() const { return static_cast<const Wrapper*>(this)->get(); }
 
   public:
     bool done() const { return iter().done(); }
     explicit operator bool() const { return !done(); }
     bool isLast() const { return iter().isLast(); }
     bool canHaveArgumentSlots() const { return iter().canHaveArgumentSlots(); }
     bool canHaveFrameSlots() const { return iter().canHaveFrameSlots(); }
     bool canHaveEnvironmentSlots() const { return iter().canHaveEnvironmentSlots(); }
@@ -1287,102 +1303,50 @@ class BindingIterOperations
     BindingKind kind() const { return iter().kind(); }
     bool isTopLevelFunction() const { return iter().isTopLevelFunction(); }
     bool hasArgumentSlot() const { return iter().hasArgumentSlot(); }
     uint16_t argumentSlot() const { return iter().argumentSlot(); }
     uint32_t nextFrameSlot() const { return iter().nextFrameSlot(); }
     uint32_t nextEnvironmentSlot() const { return iter().nextEnvironmentSlot(); }
 };
 
-template <typename Outer>
-class MutableBindingIterOperations : public BindingIterOperations<Outer>
+template <typename Wrapper>
+class MutableWrappedPtrOperations<BindingIter, Wrapper>
+  : public WrappedPtrOperations<BindingIter, Wrapper>
 {
-    BindingIter& iter() { return static_cast<Outer*>(this)->get(); }
+    BindingIter& iter() { return static_cast<Wrapper*>(this)->get(); }
 
   public:
     void operator++(int) { iter().operator++(1); }
 };
 
-template <typename Outer>
-class ScopeIterOperations
+template <typename Wrapper>
+class WrappedPtrOperations<ScopeIter, Wrapper>
 {
-    const ScopeIter& iter() const { return static_cast<const Outer*>(this)->get(); }
+    const ScopeIter& iter() const { return static_cast<const Wrapper*>(this)->get(); }
 
   public:
     bool done() const { return iter().done(); }
     explicit operator bool() const { return !done(); }
     Scope* scope() const { return iter().scope(); }
     ScopeKind kind() const { return iter().kind(); }
     Shape* environmentShape() const { return iter().environmentShape(); }
     bool hasSyntacticEnvironment() const { return iter().hasSyntacticEnvironment(); }
 };
 
-template <typename Outer>
-class MutableScopeIterOperations : public ScopeIterOperations<Outer>
+template <typename Wrapper>
+class MutableWrappedPtrOperations<ScopeIter, Wrapper>
+  : public WrappedPtrOperations<ScopeIter, Wrapper>
 {
-    ScopeIter& iter() { return static_cast<Outer*>(this)->get(); }
+    ScopeIter& iter() { return static_cast<Wrapper*>(this)->get(); }
 
   public:
     void operator++(int) { iter().operator++(1); }
 };
 
-#define SPECIALIZE_ROOTING_CONTAINERS(Iter, BaseIter)                    \
-    template <>                                                          \
-    class RootedBase<Iter>                                               \
-      : public Mutable##BaseIter##Operations<JS::Rooted<Iter>>           \
-    { };                                                                 \
-                                                                         \
-    template <>                                                          \
-    class MutableHandleBase<Iter>                                        \
-      : public Mutable##BaseIter##Operations<JS::MutableHandle<Iter>>    \
-    { };                                                                 \
-                                                                         \
-    template <>                                                          \
-    class HandleBase<Iter>                                               \
-      : public BaseIter##Operations<JS::Handle<Iter>>                    \
-    { };                                                                 \
-                                                                         \
-    template <>                                                          \
-    class PersistentRootedBase<Iter>                                     \
-      : public Mutable##BaseIter##Operations<JS::PersistentRooted<Iter>> \
-    { }
-
-SPECIALIZE_ROOTING_CONTAINERS(BindingIter, BindingIter);
-SPECIALIZE_ROOTING_CONTAINERS(PositionalFormalParameterIter, BindingIter);
-SPECIALIZE_ROOTING_CONTAINERS(ScopeIter, ScopeIter);
-
-#undef SPECIALIZE_ROOTING_CONTAINERS
-
-//
-// Allow using is<T> and as<T> on Rooted<Scope*> and Handle<Scope*>.
-//
-
-template <typename Outer>
-struct ScopeCastOperation
-{
-    template <class U>
-    JS::Handle<U*> as() const {
-        const Outer& self = *static_cast<const Outer*>(this);
-        MOZ_ASSERT_IF(self, self->template is<U>());
-        return Handle<U*>::fromMarkedLocation(reinterpret_cast<U* const*>(self.address()));
-    }
-};
-
-template <>
-class RootedBase<Scope*> : public ScopeCastOperation<JS::Rooted<Scope*>>
-{ };
-
-template <>
-class HandleBase<Scope*> : public ScopeCastOperation<JS::Handle<Scope*>>
-{ };
-
-template <>
-class MutableHandleBase<Scope*> : public ScopeCastOperation<JS::MutableHandle<Scope*>>
-{ };
-
 } // namespace js
 
 namespace JS {
 
 template <>
 struct GCPolicy<js::ScopeKind> : public IgnoreGCPolicy<js::ScopeKind>
 { };
 
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -1237,19 +1237,20 @@ struct InitialShapeEntry
     inline InitialShapeEntry(Shape* shape, const Lookup::ShapeProto& proto);
 
     static inline HashNumber hash(const Lookup& lookup);
     static inline bool match(const InitialShapeEntry& key, const Lookup& lookup);
     static void rekey(InitialShapeEntry& k, const InitialShapeEntry& newKey) { k = newKey; }
 
     bool needsSweep() {
         Shape* ushape = shape.unbarrieredGet();
-        JSObject* protoObj = proto.proto().raw();
+        TaggedProto uproto = proto.proto().unbarrieredGet();
+        JSObject* protoObj = uproto.raw();
         return (gc::IsAboutToBeFinalizedUnbarriered(&ushape) ||
-                (proto.proto().isObject() && gc::IsAboutToBeFinalizedUnbarriered(&protoObj)));
+                (uproto.isObject() && gc::IsAboutToBeFinalizedUnbarriered(&protoObj)));
     }
 };
 
 using InitialShapeSet = JS::GCHashSet<InitialShapeEntry, InitialShapeEntry, SystemAllocPolicy>;
 
 struct StackShape
 {
     /* For performance, StackShape only roots when absolutely necessary. */
@@ -1329,56 +1330,46 @@ struct StackShape
         return hash;
     }
 
     // Traceable implementation.
     static void trace(StackShape* stackShape, JSTracer* trc) { stackShape->trace(trc); }
     void trace(JSTracer* trc);
 };
 
-template <typename Outer>
-class StackShapeOperations {
-    const StackShape& ss() const { return static_cast<const Outer*>(this)->get(); }
+template <typename Wrapper>
+class WrappedPtrOperations<StackShape, Wrapper>
+{
+    const StackShape& ss() const { return static_cast<const Wrapper*>(this)->get(); }
 
   public:
     bool hasSlot() const { return ss().hasSlot(); }
     bool hasMissingSlot() const { return ss().hasMissingSlot(); }
     uint32_t slot() const { return ss().slot(); }
     uint32_t maybeSlot() const { return ss().maybeSlot(); }
     uint32_t slotSpan() const { return ss().slotSpan(); }
     bool isAccessorShape() const { return ss().isAccessorShape(); }
     uint8_t attrs() const { return ss().attrs; }
 };
 
-template <typename Outer>
-class MutableStackShapeOperations : public StackShapeOperations<Outer> {
-    StackShape& ss() { return static_cast<Outer*>(this)->get(); }
+template <typename Wrapper>
+class MutableWrappedPtrOperations<StackShape, Wrapper>
+  : public WrappedPtrOperations<StackShape, Wrapper>
+{
+    StackShape& ss() { return static_cast<Wrapper*>(this)->get(); }
 
   public:
     void updateGetterSetter(GetterOp rawGetter, SetterOp rawSetter) {
         ss().updateGetterSetter(rawGetter, rawSetter);
     }
     void setSlot(uint32_t slot) { ss().setSlot(slot); }
     void setBase(UnownedBaseShape* base) { ss().base = base; }
     void setAttrs(uint8_t attrs) { ss().attrs = attrs; }
 };
 
-template <>
-class RootedBase<StackShape> : public MutableStackShapeOperations<JS::Rooted<StackShape>>
-{};
-
-template <>
-class HandleBase<StackShape> : public StackShapeOperations<JS::Handle<StackShape>>
-{};
-
-template <>
-class MutableHandleBase<StackShape>
-  : public MutableStackShapeOperations<JS::MutableHandle<StackShape>>
-{};
-
 inline
 Shape::Shape(const StackShape& other, uint32_t nfixed)
   : base_(other.base),
     propid_(other.propid),
     slotInfo(other.maybeSlot() | (nfixed << FIXED_SLOTS_SHIFT)),
     attrs(other.attrs),
     flags(other.flags),
     parent(nullptr)
--- a/js/src/vm/TaggedProto.h
+++ b/js/src/vm/TaggedProto.h
@@ -73,46 +73,34 @@ struct InternalBarrierMethods<TaggedProt
         return proto.isObject();
     }
 
     static bool isMarkable(const TaggedProto& proto) {
         return proto.isObject();
     }
 };
 
-template<class Outer>
-class TaggedProtoOperations
+template <class Wrapper>
+class WrappedPtrOperations<TaggedProto, Wrapper>
 {
     const TaggedProto& value() const {
-        return static_cast<const Outer*>(this)->get();
+        return static_cast<const Wrapper*>(this)->get();
     }
 
   public:
     uintptr_t toWord() const { return value().toWord(); }
     inline bool isDynamic() const { return value().isDynamic(); }
     inline bool isObject() const { return value().isObject(); }
     inline JSObject* toObject() const { return value().toObject(); }
     inline JSObject* toObjectOrNull() const { return value().toObjectOrNull(); }
     JSObject* raw() const { return value().raw(); }
     HashNumber hashCode() const { return value().hashCode(); }
     uint64_t uniqueId() const { return value().uniqueId(); }
 };
 
-template <>
-class HandleBase<TaggedProto> : public TaggedProtoOperations<Handle<TaggedProto>>
-{};
-
-template <>
-class RootedBase<TaggedProto> : public TaggedProtoOperations<Rooted<TaggedProto>>
-{};
-
-template <>
-class BarrieredBaseMixins<TaggedProto> : public TaggedProtoOperations<GCPtr<TaggedProto>>
-{};
-
 // If the TaggedProto is a JSObject pointer, convert to that type and call |f|
 // with the pointer. If the TaggedProto is lazy, calls F::defaultValue.
 template <typename F, typename... Args>
 auto
 DispatchTyped(F f, const TaggedProto& proto, Args&&... args)
   -> decltype(f(static_cast<JSObject*>(nullptr), mozilla::Forward<Args>(args)...))
 {
     if (proto.isObject())