Backed out 7 changesets (bug 1731218) for causing spidermonkey build bustages in RootingAPI. CLOSED TREE
authorSandor Molnar <smolnar@mozilla.com>
Tue, 21 Sep 2021 11:37:12 +0300
changeset 592590 2be5c656e888fb1ebdadc7220561621e7c146f4a
parent 592589 3e3296e0b0e655a144a7482ed360d0f1daefa245
child 592591 4c4b28b643e6f25bea8fef5487023459ed0d600e
push id38809
push usersmolnar@mozilla.com
push dateTue, 21 Sep 2021 15:52:59 +0000
treeherdermozilla-central@79a3346995bc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1731218
milestone94.0a1
backs out0dfcae8520269987517985f34e30be82d698e463
e2c59b5af7eae47644125bebb533b8454c8d6a85
3e98b832dcc6fffeaa4251137cec10cb679476d4
57e60277e4ca8b09b1d6b82fee6f97379b794c83
3b264a4bc67e2adb877fc2af9c58b63b84f4de7f
304f27af6b950cc07970fbd25e29491122a4d614
06e7a1992de8e6e3335a19b49f2e3f2d81614909
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 7 changesets (bug 1731218) for causing spidermonkey build bustages in RootingAPI. CLOSED TREE Backed out changeset 0dfcae852026 (bug 1731218) Backed out changeset e2c59b5af7ea (bug 1731218) Backed out changeset 3e98b832dcc6 (bug 1731218) Backed out changeset 57e60277e4ca (bug 1731218) Backed out changeset 3b264a4bc67e (bug 1731218) Backed out changeset 304f27af6b95 (bug 1731218) Backed out changeset 06e7a1992de8 (bug 1731218)
js/public/RootingAPI.h
js/public/Value.h
js/src/gc/MaybeRooted.h
js/src/gc/RootMarking.cpp
js/src/jsapi-tests/testGCExactRooting.cpp
js/src/vm/Interpreter.cpp
js/src/vm/JSObject.h
js/src/vm/Runtime.h
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -120,27 +120,26 @@ struct BarrierMethods {};
 template <typename Element, typename Wrapper>
 class WrappedPtrOperations {};
 
 template <typename Element, typename Wrapper>
 class MutableWrappedPtrOperations
     : public WrappedPtrOperations<Element, Wrapper> {};
 
 template <typename T, typename Wrapper>
-class RootedOperations : public MutableWrappedPtrOperations<T, Wrapper> {};
-
-template <typename T, typename Wrapper>
-class HandleOperations : public WrappedPtrOperations<T, Wrapper> {};
+class RootedBase : public MutableWrappedPtrOperations<T, Wrapper> {};
 
 template <typename T, typename Wrapper>
-class MutableHandleOperations : public MutableWrappedPtrOperations<T, Wrapper> {
-};
+class HandleBase : public WrappedPtrOperations<T, Wrapper> {};
 
 template <typename T, typename Wrapper>
-class HeapOperations : public MutableWrappedPtrOperations<T, 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) \
@@ -285,17 +284,17 @@ inline void AssertGCThingIsNotNurseryAll
  * 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 MOZ_NON_MEMMOVABLE Heap : public js::HeapOperations<T, Heap<T>> {
+class MOZ_NON_MEMMOVABLE 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;
 
@@ -445,17 +444,17 @@ inline void AssertObjectIsNotGray(const 
  * 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::HeapOperations<T, TenuredHeap<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.");
   }
   explicit TenuredHeap(T p) : bits(0) { setPtr(p); }
@@ -565,20 +564,20 @@ template <typename T>
 class PersistentRooted;
 
 /**
  * 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 HandleOperations<T> specialization containing them.
+ * specialization, define a HandleBase<T> specialization containing them.
  */
 template <typename T>
-class MOZ_NONHEAP_CLASS Handle : public js::HandleOperations<T, Handle<T>> {
+class MOZ_NONHEAP_CLASS Handle : public js::HandleBase<T, Handle<T>> {
   friend class MutableHandle<T>;
 
  public:
   using ElementType = T;
 
   Handle(const Handle<T>&) = default;
 
   /* Creates a handle from a handle of a type convertible to T. */
@@ -663,22 +662,22 @@ struct DefineComparisonOps<Handle<T>> : 
 
 }  // namespace detail
 
 /**
  * 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 MutableHandleOperations<T> specialization containing
+ * specialization, define a MutableHandleBase<T> specialization containing
  * them.
  */
 template <typename T>
 class MOZ_STACK_CLASS MutableHandle
-    : public js::MutableHandleOperations<T, MutableHandle<T>> {
+    : 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:
   // Disallow nullptr for overloading purposes.
@@ -873,92 +872,89 @@ struct FallibleHashMethods<js::MovableCe
 
 namespace js {
 
 struct VirtualTraceable {
   virtual ~VirtualTraceable() = default;
   virtual void trace(JSTracer* trc, const char* name) = 0;
 };
 
-class StackRootedBase {
- public:
-  StackRootedBase* previous() { return prev; }
-
- protected:
-  StackRootedBase** stack;
-  StackRootedBase* prev;
+template <typename T>
+struct RootedTraceable final : public VirtualTraceable {
+  static_assert(JS::MapTypeToRootKind<T>::kind == JS::RootKind::Traceable,
+                "RootedTraceable is intended only for usage with a Traceable");
 
-  template <typename T>
-  auto* derived() {
-    return static_cast<JS::Rooted<T>*>(this);
-  }
-};
-
-class PersistentRootedBase
-    : protected mozilla::LinkedListElement<PersistentRootedBase> {
- protected:
-  friend class mozilla::LinkedList<PersistentRootedBase>;
-  friend class mozilla::LinkedListElement<PersistentRootedBase>;
+  T ptr;
 
-  template <typename T>
-  auto* derived() {
-    return static_cast<JS::PersistentRooted<T>*>(this);
-  }
-};
-
-struct StackRootedTraceableBase : public StackRootedBase,
-                                  public VirtualTraceable {};
-
-class PersistentRootedTraceableBase : public PersistentRootedBase,
-                                      public VirtualTraceable {};
+  template <typename... CtorArgs>
+  explicit RootedTraceable(std::in_place_t, CtorArgs... args)
+      : ptr(std::forward<CtorArgs>(args)...) {}
 
-template <typename Base, typename T>
-class TypedRootedGCThingBase : public Base {
- public:
-  void trace(JSTracer* trc, const char* name);
-};
+  template <typename U, typename = typename std::is_constructible<T, U>::type>
+  MOZ_IMPLICIT RootedTraceable(U&& initial) : ptr(std::forward<U>(initial)) {}
 
-template <typename Base, typename T>
-class TypedRootedTraceableBase : public Base {
- public:
+  operator T&() { return ptr; }
+  operator const T&() const { return ptr; }
+
   void trace(JSTracer* trc, const char* name) override {
-    auto* self = this->template derived<T>();
-    JS::GCPolicy<T>::trace(trc, self->address(), name);
+    JS::GCPolicy<T>::trace(trc, &ptr, name);
   }
 };
 
 template <typename T>
 struct RootedTraceableTraits {
-  using StackRootedBase = TypedRootedTraceableBase<StackRootedTraceableBase, T>;
-  using PersistentRootedBase =
-      TypedRootedTraceableBase<PersistentRootedTraceableBase, T>;
+  static T* address(RootedTraceable<T>& self) { return &self.ptr; }
+  static const T* address(const RootedTraceable<T>& self) { return &self.ptr; }
+  static void trace(JSTracer* trc, VirtualTraceable* thingp, const char* name);
 };
 
 template <typename T>
 struct RootedGCThingTraits {
-  using StackRootedBase = TypedRootedGCThingBase<StackRootedBase, T>;
-  using PersistentRootedBase = TypedRootedGCThingBase<PersistentRootedBase, T>;
+  static T* address(T& self) { return &self; }
+  static const T* address(const T& self) { return &self; }
+  static void trace(JSTracer* trc, T* thingp, const char* name);
 };
 
 } /* namespace js */
 
 namespace JS {
 
 class JS_PUBLIC_API AutoGCRooter;
 
 enum class AutoGCRooterKind : uint8_t {
   WrapperVector, /* js::AutoWrapperVector */
   Wrapper,       /* js::AutoWrapperRooter */
   Custom,        /* js::CustomAutoRooter */
 
   Limit
 };
 
+namespace detail {
+// Dummy type to store root list entry pointers as. This code does not just use
+// the actual type, because then eg JSObject* and JSFunction* would be assumed
+// to never alias but they do (they are stored in the same list). Also, do not
+// use `void*` so that `Rooted<void*>` is a compile error.
+struct RootListEntry;
+}  // namespace detail
+
+template <>
+struct MapTypeToRootKind<detail::RootListEntry*> {
+  static const RootKind kind = RootKind::Traceable;
+};
+
+// Workaround MSVC issue where GCPolicy is needed even though this dummy type is
+// never instantiated. Ideally, RootListEntry is removed in the future and an
+// appropriate class hierarchy for the Rooted<T> types.
+template <>
+struct GCPolicy<detail::RootListEntry*>
+    : public IgnoreGCPolicy<detail::RootListEntry*> {};
+
 using RootedListHeads =
-    mozilla::EnumeratedArray<RootKind, RootKind::Limit, js::StackRootedBase*>;
+    mozilla::EnumeratedArray<RootKind, RootKind::Limit,
+                             Rooted<detail::RootListEntry*>*>;
 
 using AutoRooterListHeads =
     mozilla::EnumeratedArray<AutoGCRooterKind, AutoGCRooterKind::Limit,
                              AutoGCRooter*>;
 
 // Superclass of JSContext which can be used for rooting data in use by the
 // current thread but that does not provide all the functions of a JSContext.
 class RootingContext {
@@ -1086,39 +1082,43 @@ class MOZ_RAII JS_PUBLIC_API CustomAutoR
 
 namespace detail {
 
 template <typename T>
 constexpr bool IsTraceable_v =
     MapTypeToRootKind<T>::kind == JS::RootKind::Traceable;
 
 template <typename T>
+using RootedPtr =
+    std::conditional_t<IsTraceable_v<T>, js::RootedTraceable<T>, T>;
+
+template <typename T>
 using RootedPtrTraits =
     std::conditional_t<IsTraceable_v<T>, js::RootedTraceableTraits<T>,
                        js::RootedGCThingTraits<T>>;
 
 } /* namespace detail */
 
 /**
  * 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 RootedOperations<T> specialization containing them.
+ * specialization, define a RootedBase<T> specialization containing them.
  */
 template <typename T>
-class MOZ_RAII Rooted : public detail::RootedPtrTraits<T>::StackRootedBase,
-                        public js::RootedOperations<T, Rooted<T>> {
+class MOZ_RAII Rooted : public js::RootedBase<T, Rooted<T>> {
+  using Ptr = detail::RootedPtr<T>;
   using PtrTraits = detail::RootedPtrTraits<T>;
 
   inline void registerWithRootLists(RootedListHeads& roots) {
     this->stack = &roots[JS::MapTypeToRootKind<T>::kind];
-    this->prev = *this->stack;
-    *this->stack = this;
+    this->prev = *stack;
+    *stack = reinterpret_cast<Rooted<detail::RootListEntry*>*>(this);
   }
 
   inline RootedListHeads& rootLists(RootingContext* cx) {
     return cx->stackRoots_;
   }
   inline RootedListHeads& rootLists(JSContext* cx) {
     return rootLists(RootingContext::get(cx));
   }
@@ -1136,17 +1136,18 @@ class MOZ_RAII Rooted : public detail::R
             typename =
                 std::enable_if_t<std::is_copy_constructible_v<T>, RootingContext>>
   explicit Rooted(const RootingContext& cx) : ptr(SafelyInitialized<T>()) {
     registerWithRootLists(rootLists(cx));
   }
 
   // Provide an initial value. Requires T to be constructible from the given
   // argument.
-  template <typename RootingContext, typename S>
+  template <typename RootingContext, typename S,
+            typename = typename std::is_constructible<T, S>::type>
   Rooted(const RootingContext& cx, S&& initial)
       : ptr(std::forward<S>(initial)) {
     MOZ_ASSERT(GCPolicy<T>::isValid(ptr));
     registerWithRootLists(rootLists(cx));
   }
 
   // (Traceables only) Construct the contained value from the given arguments.
   // Constructs in-place, so T does not need to be copyable or movable.
@@ -1155,26 +1156,29 @@ class MOZ_RAII Rooted : public detail::R
   // choose the above SafelyInitialized<T> constructor, because otherwise
   // identical functions with parameter packs are considered less specialized.
   //
   // The SFINAE type must again depend on an inferred template parameter.
   template <
       typename RootingContext, typename... CtorArgs,
       typename = std::enable_if_t<detail::IsTraceable_v<T>, RootingContext>>
   explicit Rooted(const RootingContext& cx, CtorArgs... args)
-      : ptr(std::forward<CtorArgs>(args)...) {
+      : ptr(std::in_place, std::forward<CtorArgs>(args)...) {
     MOZ_ASSERT(GCPolicy<T>::isValid(ptr));
     registerWithRootLists(rootLists(cx));
   }
 
   ~Rooted() {
-    MOZ_ASSERT(*this->stack == this);
-    *this->stack = this->prev;
+    MOZ_ASSERT(*stack ==
+               reinterpret_cast<Rooted<detail::RootListEntry*>*>(this));
+    *stack = prev;
   }
 
+  Rooted<T>* previous() { return reinterpret_cast<Rooted<T>*>(prev); }
+
   /*
    * This method is public for Rooted so that Codegen.py can use a Rooted
    * interchangeably with a MutableHandleValue.
    */
   void set(const T& value) {
     ptr = value;
     MOZ_ASSERT(GCPolicy<T>::isValid(ptr));
   }
@@ -1184,21 +1188,31 @@ class MOZ_RAII Rooted : public detail::R
   }
 
   DECLARE_POINTER_CONSTREF_OPS(T);
   DECLARE_POINTER_ASSIGN_OPS(Rooted, T);
 
   T& get() { return ptr; }
   const T& get() const { return ptr; }
 
-  T* address() { return &ptr; }
-  const T* address() const { return &ptr; }
+  T* address() { return PtrTraits::address(ptr); }
+  const T* address() const { return PtrTraits::address(ptr); }
+
+  void trace(JSTracer* trc, const char* name);
 
  private:
-  T ptr;
+  /*
+   * These need to be templated on RootListEntry* to avoid aliasing issues
+   * between, for example, Rooted<JSObject*> and Rooted<JSFunction*>, which use
+   * the same stack head pointer for different classes.
+   */
+  Rooted<detail::RootListEntry*>** stack;
+  Rooted<detail::RootListEntry*>* prev;
+
+  Ptr ptr;
 
   Rooted(const Rooted&) = delete;
 } JS_HAZ_ROOTED;
 
 namespace detail {
 
 template <typename T>
 struct DefineComparisonOps<Rooted<T>> : std::true_type {
@@ -1248,17 +1262,17 @@ inline ProfilingStack* GetContextProfili
  *
  * 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 <typename Container>
-class RootedOperations<JSObject*, 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
@@ -1266,17 +1280,17 @@ class RootedOperations<JSObject*, Contai
  *
  * 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 <typename Container>
-class HandleOperations<JSObject*, Container>
+class HandleBase<JSObject*, Container>
     : public WrappedPtrOperations<JSObject*, Container> {
  public:
   template <class U>
   JS::Handle<U*> as() const;
 };
 
 } /* namespace js */
 
@@ -1315,21 +1329,23 @@ inline MutableHandle<T>::MutableHandle(R
 
 template <typename T>
 inline MutableHandle<T>::MutableHandle(PersistentRooted<T>* root) {
   static_assert(sizeof(MutableHandle<T>) == sizeof(T*),
                 "MutableHandle must be binary compatible with T*.");
   ptr = root->address();
 }
 
-JS_PUBLIC_API void AddPersistentRoot(RootingContext* cx, RootKind kind,
-                                     js::PersistentRootedBase* root);
+JS_PUBLIC_API void AddPersistentRoot(
+    RootingContext* cx, RootKind kind,
+    PersistentRooted<detail::RootListEntry*>* root);
 
-JS_PUBLIC_API void AddPersistentRoot(JSRuntime* rt, RootKind kind,
-                                     js::PersistentRootedBase* root);
+JS_PUBLIC_API void AddPersistentRoot(
+    JSRuntime* rt, RootKind kind,
+    PersistentRooted<detail::RootListEntry*>* root);
 
 /**
  * A copyable, assignable global GC root type with arbitrary lifetime, an
  * infallible constructor, and automatic unrooting on destruction.
  *
  * These roots can be used in heap-allocated data structures, so they are not
  * associated with any particular JSContext or stack. They are registered with
  * the JSRuntime itself, without locking. Initialization may take place on
@@ -1356,30 +1372,39 @@ JS_PUBLIC_API void AddPersistentRoot(JSR
  * 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 detail::RootedPtrTraits<T>::PersistentRootedBase,
-      public js::RootedOperations<T, PersistentRooted<T>> {
+    : public js::RootedBase<T, PersistentRooted<T>>,
+      private mozilla::LinkedListElement<PersistentRooted<T>> {
+  using ListBase = mozilla::LinkedListElement<PersistentRooted<T>>;
+  using Ptr = detail::RootedPtr<T>;
   using PtrTraits = detail::RootedPtrTraits<T>;
 
+  friend class mozilla::LinkedList<PersistentRooted>;
+  friend class mozilla::LinkedListElement<PersistentRooted>;
+
   void registerWithRootLists(RootingContext* cx) {
     MOZ_ASSERT(!initialized());
     JS::RootKind kind = JS::MapTypeToRootKind<T>::kind;
-    AddPersistentRoot(cx, kind, this);
+    AddPersistentRoot(
+        cx, kind,
+        reinterpret_cast<JS::PersistentRooted<detail::RootListEntry*>*>(this));
   }
 
   void registerWithRootLists(JSRuntime* rt) {
     MOZ_ASSERT(!initialized());
     JS::RootKind kind = JS::MapTypeToRootKind<T>::kind;
-    AddPersistentRoot(rt, kind, this);
+    AddPersistentRoot(
+        rt, kind,
+        reinterpret_cast<JS::PersistentRooted<detail::RootListEntry*>*>(this));
   }
 
   // Used when JSContext type is incomplete and so it is not known to inherit
   // from RootingContext.
   void registerWithRootLists(JSContext* cx) {
     registerWithRootLists(RootingContext::get(cx));
   }
 
@@ -1401,33 +1426,34 @@ class PersistentRooted
   PersistentRooted(const RootHolder& cx, U&& initial)
       : ptr(std::forward<U>(initial)) {
     registerWithRootLists(cx);
   }
 
   template <typename RootHolder, typename... CtorArgs,
             typename = std::enable_if_t<detail::IsTraceable_v<T>, RootHolder>>
   explicit PersistentRooted(const RootHolder& cx, CtorArgs... args)
-      : ptr(std::forward<CtorArgs>(args)...) {
+      : ptr(std::in_place, std::forward<CtorArgs>(args)...) {
     registerWithRootLists(cx);
   }
 
-  PersistentRooted(const PersistentRooted& rhs) : ptr(rhs.ptr) {
+  PersistentRooted(const PersistentRooted& rhs)
+      : mozilla::LinkedListElement<PersistentRooted<T>>(), ptr(rhs.ptr) {
     /*
      * Copy construction takes advantage of the fact that the original
      * is already inserted, and simply adds itself to whatever list the
      * original was on - no JSRuntime pointer needed.
      *
      * This requires mutating rhs's links, but those should be 'mutable'
      * anyway. C++ doesn't let us declare mutable base classes.
      */
     const_cast<PersistentRooted&>(rhs).setNext(this);
   }
 
-  bool initialized() const { return this->isInList(); }
+  bool initialized() const { return ListBase::isInList(); }
 
   void init(RootingContext* cx) { init(cx, SafelyInitialized<T>()); }
   void init(JSContext* cx) { init(RootingContext::get(cx)); }
 
   template <typename U>
   void init(RootingContext* cx, U&& initial) {
     ptr = std::forward<U>(initial);
     registerWithRootLists(cx);
@@ -1436,40 +1462,42 @@ class PersistentRooted
   void init(JSContext* cx, U&& initial) {
     ptr = std::forward<U>(initial);
     registerWithRootLists(RootingContext::get(cx));
   }
 
   void reset() {
     if (initialized()) {
       set(SafelyInitialized<T>());
-      this->remove();
+      ListBase::remove();
     }
   }
 
   DECLARE_POINTER_CONSTREF_OPS(T);
   DECLARE_POINTER_ASSIGN_OPS(PersistentRooted, T);
 
   T& get() { return ptr; }
   const T& get() const { return ptr; }
 
   T* address() {
     MOZ_ASSERT(initialized());
-    return &ptr;
+    return PtrTraits::address(ptr);
   }
-  const T* address() const { return &ptr; }
+  const T* address() const { return PtrTraits::address(ptr); }
 
   template <typename U>
   void set(U&& value) {
     MOZ_ASSERT(initialized());
     ptr = std::forward<U>(value);
   }
 
+  void trace(JSTracer* trc, const char* name);
+
  private:
-  T ptr;
+  Ptr ptr;
 } JS_HAZ_ROOTED;
 
 namespace detail {
 
 template <typename T>
 struct DefineComparisonOps<PersistentRooted<T>> : std::true_type {
   static const T& get(const PersistentRooted<T>& v) { return v.get(); }
 };
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -1212,17 +1212,17 @@ class MutableWrappedPtrOperations<JS::Va
   }
 };
 
 /*
  * Augment the generic Heap<T> interface when T = Value with
  * type-querying, value-extracting, and mutating operations.
  */
 template <typename Wrapper>
-class HeapOperations<JS::Value, Wrapper>
+class HeapBase<JS::Value, Wrapper>
     : public MutableWrappedPtrOperations<JS::Value, Wrapper> {};
 
 MOZ_HAVE_NORETURN MOZ_COLD MOZ_NEVER_INLINE void ReportBadValueTypeAndCrash(
     const JS::Value& val);
 
 // If the Value is a GC pointer type, call |f| with the pointer cast to that
 // type and return the result wrapped in a Maybe, otherwise return None().
 template <typename F>
--- a/js/src/gc/MaybeRooted.h
+++ b/js/src/gc/MaybeRooted.h
@@ -23,17 +23,17 @@
 
 namespace js {
 
 /**
  * Interface substitute for Rooted<T> which does not root the variable's
  * memory.
  */
 template <typename T>
-class MOZ_RAII FakeRooted : public RootedOperations<T, FakeRooted<T>> {
+class MOZ_RAII FakeRooted : public RootedBase<T, FakeRooted<T>> {
  public:
   using ElementType = T;
 
   explicit FakeRooted(JSContext* cx) : ptr(JS::SafelyInitialized<T>()) {}
 
   FakeRooted(JSContext* cx, T initial) : ptr(initial) {}
 
   DECLARE_POINTER_CONSTREF_OPS(T);
@@ -67,17 +67,17 @@ struct DefineComparisonOps<js::FakeRoote
 namespace js {
 
 /**
  * Interface substitute for MutableHandle<T> which is not required to point to
  * rooted memory.
  */
 template <typename T>
 class FakeMutableHandle
-    : public js::MutableHandleOperations<T, FakeMutableHandle<T>> {
+    : public js::MutableHandleBase<T, FakeMutableHandle<T>> {
  public:
   using ElementType = T;
 
   MOZ_IMPLICIT FakeMutableHandle(T* t) : ptr(t) {}
 
   MOZ_IMPLICIT FakeMutableHandle(FakeRooted<T>* root) : ptr(root->address()) {}
 
   void set(const T& v) { *ptr = v; }
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -35,40 +35,57 @@ using namespace js::gc;
 using mozilla::LinkedList;
 
 using JS::AutoGCRooter;
 
 using RootRange = RootedValueMap::Range;
 using RootEntry = RootedValueMap::Entry;
 using RootEnum = RootedValueMap::Enum;
 
-template <typename Base, typename T>
-inline void TypedRootedGCThingBase<Base, T>::trace(JSTracer* trc,
-                                                   const char* name) {
-  auto* self = this->template derived<T>();
-  TraceNullableRoot(trc, self->address(), name);
+// For more detail see JS::Rooted::root and js::RootedTraceable.
+//
+// The JS::RootKind::Traceable list contains a bunch of totally disparate types,
+// but to refer to this list we need /something/ in the type field. We use the
+// following type as a compatible stand-in. No actual methods from
+// ConcreteTraceable type are actually used at runtime.
+struct ConcreteTraceable {
+  ConcreteTraceable() = delete;
+  void trace(JSTracer*) = delete;
+};
+
+template <typename T>
+inline void RootedGCThingTraits<T>::trace(JSTracer* trc, T* thingp,
+                                          const char* name) {
+  TraceNullableRoot(trc, thingp, name);
 }
 
 template <typename T>
-static inline void TraceExactStackRootList(JSTracer* trc,
-                                           StackRootedBase* listHead,
-                                           const char* name) {
-  // Check size of Rooted<T> does not increase.
-  static_assert(sizeof(Rooted<T>) == sizeof(T) + 2 * sizeof(uintptr_t));
+inline void RootedTraceableTraits<T>::trace(JSTracer* trc,
+                                            VirtualTraceable* thingp,
+                                            const char* name) {
+  thingp->trace(trc, name);
+}
 
-  for (StackRootedBase* root = listHead; root; root = root->previous()) {
-    static_cast<Rooted<T>*>(root)->trace(trc, name);
-  }
+template <typename T>
+inline void JS::Rooted<T>::trace(JSTracer* trc, const char* name) {
+  PtrTraits::trace(trc, &ptr, name);
 }
 
-static inline void TraceExactStackRootTraceableList(JSTracer* trc,
-                                                    StackRootedBase* listHead,
-                                                    const char* name) {
-  for (StackRootedBase* root = listHead; root; root = root->previous()) {
-    static_cast<StackRootedTraceableBase*>(root)->trace(trc, name);
+template <typename T>
+inline void JS::PersistentRooted<T>::trace(JSTracer* trc, const char* name) {
+  PtrTraits::trace(trc, &ptr, name);
+}
+
+template <typename T>
+static inline void TraceExactStackRootList(
+    JSTracer* trc, JS::Rooted<JS::detail::RootListEntry*>* listHead,
+    const char* name) {
+  auto* typedList = reinterpret_cast<JS::Rooted<T>*>(listHead);
+  for (JS::Rooted<T>* root = typedList; root; root = root->previous()) {
+    root->trace(trc, name);
   }
 }
 
 static inline void TraceStackRoots(JSTracer* trc,
                                    JS::RootedListHeads& stackRoots) {
 #define TRACE_ROOTS(name, type, _, _1)                                \
   TraceExactStackRootList<type*>(trc, stackRoots[JS::RootKind::name], \
                                  "exact-" #name);
@@ -76,40 +93,36 @@ static inline void TraceStackRoots(JSTra
 #undef TRACE_ROOTS
   TraceExactStackRootList<jsid>(trc, stackRoots[JS::RootKind::Id], "exact-id");
   TraceExactStackRootList<Value>(trc, stackRoots[JS::RootKind::Value],
                                  "exact-value");
 
   // RootedTraceable uses virtual dispatch.
   JS::AutoSuppressGCAnalysis nogc;
 
-  TraceExactStackRootTraceableList(trc, stackRoots[JS::RootKind::Traceable],
-                                   "Traceable");
+  TraceExactStackRootList<ConcreteTraceable>(
+      trc, stackRoots[JS::RootKind::Traceable], "Traceable");
 }
 
 void JS::RootingContext::traceStackRoots(JSTracer* trc) {
   TraceStackRoots(trc, stackRoots_);
 }
 
 static void TraceExactStackRoots(JSContext* cx, JSTracer* trc) {
   cx->traceStackRoots(trc);
 }
 
 template <typename T>
 static inline void TracePersistentRootedList(
-    JSTracer* trc, LinkedList<PersistentRootedBase>& list, const char* name) {
-  for (PersistentRootedBase* root : list) {
-    static_cast<PersistentRooted<T>*>(root)->trace(trc, name);
-  }
-}
-
-static inline void TracePersistentRootedTraceableList(
-    JSTracer* trc, LinkedList<PersistentRootedBase>& list, const char* name) {
-  for (PersistentRootedBase* root : list) {
-    static_cast<PersistentRootedTraceableBase*>(root)->trace(trc, name);
+    JSTracer* trc,
+    LinkedList<PersistentRooted<JS::detail::RootListEntry*>>& list,
+    const char* name) {
+  auto& typedList = reinterpret_cast<LinkedList<PersistentRooted<T>>&>(list);
+  for (PersistentRooted<T>* root : typedList) {
+    root->trace(trc, name);
   }
 }
 
 void JSRuntime::tracePersistentRoots(JSTracer* trc) {
 #define TRACE_ROOTS(name, type, _, _1)                                       \
   TracePersistentRootedList<type*>(trc, heapRoots.ref()[JS::RootKind::name], \
                                    "persistent-" #name);
   JS_FOR_EACH_TRACEKIND(TRACE_ROOTS)
@@ -117,29 +130,30 @@ void JSRuntime::tracePersistentRoots(JST
   TracePersistentRootedList<jsid>(trc, heapRoots.ref()[JS::RootKind::Id],
                                   "persistent-id");
   TracePersistentRootedList<Value>(trc, heapRoots.ref()[JS::RootKind::Value],
                                    "persistent-value");
 
   // RootedTraceable uses virtual dispatch.
   JS::AutoSuppressGCAnalysis nogc;
 
-  TracePersistentRootedTraceableList(
+  TracePersistentRootedList<ConcreteTraceable>(
       trc, heapRoots.ref()[JS::RootKind::Traceable], "persistent-traceable");
 }
 
 static void TracePersistentRooted(JSRuntime* rt, JSTracer* trc) {
   rt->tracePersistentRoots(trc);
 }
 
 template <typename T>
 static void FinishPersistentRootedChain(
-    LinkedList<PersistentRootedBase>& list) {
+    LinkedList<PersistentRooted<JS::detail::RootListEntry*>>& listArg) {
+  auto& list = reinterpret_cast<LinkedList<PersistentRooted<T>>&>(listArg);
   while (!list.isEmpty()) {
-    static_cast<PersistentRooted<T>*>(list.getFirst())->reset();
+    list.getFirst()->reset();
   }
 }
 
 void JSRuntime::finishPersistentRoots() {
 #define FINISH_ROOT_LIST(name, type, _, _1) \
   FinishPersistentRootedChain<type*>(heapRoots.ref()[JS::RootKind::name]);
   JS_FOR_EACH_TRACEKIND(FINISH_ROOT_LIST)
 #undef FINISH_ROOT_LIST
@@ -449,18 +463,20 @@ void js::gc::GCRuntime::finishRoots() {
 
 void js::gc::GCRuntime::checkNoRuntimeRoots(AutoGCSession& session) {
 #ifdef DEBUG
   AssertNoRootsTracer trc(rt);
   traceRuntimeForMajorGC(&trc, session);
 #endif  // DEBUG
 }
 
-JS_PUBLIC_API void JS::AddPersistentRoot(JS::RootingContext* cx, RootKind kind,
-                                         PersistentRootedBase* root) {
-  JSRuntime* rt = static_cast<JSContext*>(cx)->runtime();
+JS_PUBLIC_API void JS::AddPersistentRoot(
+    JS::RootingContext* cx, RootKind kind,
+    PersistentRooted<JS::detail::RootListEntry*>* root) {
+  static_cast<JSContext*>(cx)->runtime()->heapRoots.ref()[kind].insertBack(
+      root);
+}
+
+JS_PUBLIC_API void JS::AddPersistentRoot(
+    JSRuntime* rt, RootKind kind,
+    PersistentRooted<JS::detail::RootListEntry*>* root) {
   rt->heapRoots.ref()[kind].insertBack(root);
 }
-
-JS_PUBLIC_API void JS::AddPersistentRoot(JSRuntime* rt, RootKind kind,
-                                         PersistentRootedBase* root) {
-  rt->heapRoots.ref()[kind].insertBack(root);
-}
--- a/js/src/jsapi-tests/testGCExactRooting.cpp
+++ b/js/src/jsapi-tests/testGCExactRooting.cpp
@@ -118,17 +118,17 @@ BEGIN_TEST(testGCRootedStaticStructInter
   JS::Rooted<MyContainer> r1(cx);
   JS::Rooted<MyContainer> r2(cx, 3.4);
   JS::Rooted<MyContainer> r3(cx, MyContainer(cx));
   JS::Rooted<MyContainer> r4(cx, cx);
   JS::Rooted<MyContainer> r5(cx, cx, cx, cx);
 
   JS::Rooted<Value> rv(cx);
 
-  CHECK_EQUAL(r1.constructor(), 1);    // direct SafelyInitialized<T>
+  CHECK_EQUAL(r1.constructor(), 101);  // copy of SafelyInitialized<T>
   CHECK_EQUAL(r2.constructor(), 2);    // direct MyContainer(3.4)
   CHECK_EQUAL(r3.constructor(), 103);  // copy of MyContainer(cx)
   CHECK_EQUAL(r4.constructor(), 3);    // direct MyContainer(cx)
   CHECK_EQUAL(r5.constructor(), 4);    // direct MyContainer(cx, cx, cx)
 
   // Test Rooted constructor forwarding for a non-copyable type.
   JS::Rooted<MyNonCopyableContainer> nc1(cx);
   JS::Rooted<MyNonCopyableContainer> nc2(cx, 3.4);
@@ -164,17 +164,17 @@ BEGIN_TEST(testGCRootedStaticStructInter
     JS::PersistentRooted<MyContainer> heap(cx, container);
 
     // Copyable types in place.
     JS::PersistentRooted<MyContainer> cp1(cx);
     JS::PersistentRooted<MyContainer> cp2(cx, 7.8);
     JS::PersistentRooted<MyContainer> cp3(cx, cx);
     JS::PersistentRooted<MyContainer> cp4(cx, cx, cx, cx);
 
-    CHECK_EQUAL(cp1.constructor(), 1);    // direct SafelyInitialized<T>
+    CHECK_EQUAL(cp1.constructor(), 101);  // copy of SafelyInitialized<T>
     CHECK_EQUAL(cp2.constructor(), 2);    // direct MyContainer(double)
     CHECK_EQUAL(cp3.constructor(), 3);    // direct MyContainer(cx)
     CHECK_EQUAL(cp4.constructor(), 4);    // direct MyContainer(cx, cx, cx)
 
     // Construct uncopyable type in place.
     JS::PersistentRooted<MyNonCopyableContainer> ncp1(cx);
     JS::PersistentRooted<MyNonCopyableContainer> ncp2(cx, 7.8);
 
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -1788,17 +1788,17 @@ static MOZ_ALWAYS_INLINE void InitElemAr
  * 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 ReservedRooted : public RootedOperations<T, ReservedRooted<T>> {
+class ReservedRooted : public RootedBase<T, ReservedRooted<T>> {
   Rooted<T>* savedRoot;
 
  public:
   ReservedRooted(Rooted<T>* root, const T& ptr) : savedRoot(root) {
     *root = ptr;
   }
 
   explicit ReservedRooted(Rooted<T>* root) : savedRoot(root) {
--- a/js/src/vm/JSObject.h
+++ b/js/src/vm/JSObject.h
@@ -559,27 +559,27 @@ class JSObject
 
 template <>
 inline bool JSObject::is<JSObject>() const {
   return true;
 }
 
 template <typename Wrapper>
 template <typename U>
-MOZ_ALWAYS_INLINE JS::Handle<U*> js::RootedOperations<JSObject*, Wrapper>::as()
+MOZ_ALWAYS_INLINE JS::Handle<U*> js::RootedBase<JSObject*, Wrapper>::as()
     const {
   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::HandleOperations<JSObject*, Wrapper>::as()
+MOZ_ALWAYS_INLINE JS::Handle<U*> js::HandleBase<JSObject*, Wrapper>::as()
     const {
   const JS::Handle<JSObject*>& self =
       *static_cast<const JS::Handle<JSObject*>*>(this);
   MOZ_ASSERT(self->template is<U>());
   return Handle<U*>::fromMarkedLocation(
       reinterpret_cast<U* const*>(self.address()));
 }
 
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -427,19 +427,19 @@ struct JSRuntime {
  private:
   /* Gecko profiling metadata */
   js::UnprotectedData<js::GeckoProfilerRuntime> geckoProfiler_;
 
  public:
   js::GeckoProfilerRuntime& geckoProfiler() { return geckoProfiler_.ref(); }
 
   // Heap GC roots for PersistentRooted pointers.
-  js::MainThreadData<
-      mozilla::EnumeratedArray<JS::RootKind, JS::RootKind::Limit,
-                               mozilla::LinkedList<js::PersistentRootedBase>>>
+  js::MainThreadData<mozilla::EnumeratedArray<
+      JS::RootKind, JS::RootKind::Limit,
+      mozilla::LinkedList<JS::PersistentRooted<JS::detail::RootListEntry*>>>>
       heapRoots;
 
   void tracePersistentRoots(JSTracer* trc);
   void finishPersistentRoots();
 
   void finishRoots();
 
  private: