Bug 1243888 - Derive RootKind automatically from TraceKind; r=sfink
authorTerrence Cole <terrence@mozilla.com>
Wed, 27 Jan 2016 09:54:53 -0800
changeset 282597 5eaf5e034e6d323cb957884dc474c32ef188ab25
parent 282596 a85f477cd68f3ce55e65557f2706bd472129147c
child 282598 650c2be3cbf3c9edbab182f07b5e1aeb28c83f48
push id17362
push usercbook@mozilla.com
push dateTue, 02 Feb 2016 10:54:53 +0000
treeherderfx-team@e5f1b4782e38 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1243888
milestone47.0a1
Bug 1243888 - Derive RootKind automatically from TraceKind; r=sfink
js/public/RootingAPI.h
js/public/TraceKind.h
js/src/gc/RootMarking.cpp
js/src/jit/IonCode.h
js/src/jscntxt.cpp
js/src/jsgc.cpp
js/src/jsobj.h
js/src/jspubtd.h
js/src/jsscript.h
js/src/vm/ObjectGroup.h
js/src/vm/Shape.h
js/src/vm/String.h
js/src/vm/Symbol.h
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -504,33 +504,16 @@ class MOZ_STACK_CLASS MutableHandle : pu
 
     T* ptr;
 };
 
 } /* namespace JS */
 
 namespace js {
 
-/**
- * By default, things should use the inheritance hierarchy to find their
- * ThingRootKind. Some pointer types are explicitly set in jspubtd.h so that
- * Rooted<T> may be used without the class definition being available.
- */
-template <typename T>
-struct RootKind
-{
-    static ThingRootKind rootKind() { return T::rootKind(); }
-};
-
-template <typename T>
-struct RootKind<T*>
-{
-    static ThingRootKind rootKind() { return T::rootKind(); }
-};
-
 template <typename T>
 struct BarrierMethods<T*>
 {
     static T* initial() { return nullptr; }
     static void postBarrier(T** vp, T* prev, T* next) {
         if (next)
             JS::AssertGCThingIsNotAnObjectSubclass(reinterpret_cast<js::gc::Cell*>(next));
     }
@@ -602,18 +585,16 @@ namespace JS {
 
 // Non pointer types -- structs or classes that contain GC pointers, either as
 // a member or in a more complex container layout -- can also be stored in a
 // [Persistent]Rooted if it derives from JS::Traceable. A JS::Traceable stored
 // in a [Persistent]Rooted must implement the method:
 //     |static void trace(T*, JSTracer*)|
 class Traceable
 {
-  public:
-    static js::ThingRootKind rootKind() { return js::THING_ROOT_TRACEABLE; }
 };
 
 } /* namespace JS */
 
 namespace js {
 
 template <typename T>
 class DispatchWrapper
@@ -689,18 +670,17 @@ namespace JS {
 template <typename T>
 class MOZ_RAII Rooted : public js::RootedBase<T>
 {
     static_assert(!mozilla::IsConvertible<T, Traceable*>::value,
                   "Rooted takes pointer or Traceable types but not Traceable* type");
 
     /* Note: CX is a subclass of either ContextFriendFields or PerThreadDataFriendFields. */
     void registerWithRootLists(js::RootLists& roots) {
-        js::ThingRootKind kind = js::RootKind<T>::rootKind();
-        this->stack = &roots.stackRoots_[kind];
+        this->stack = &roots.stackRoots_[JS::MapTypeToRootKind<T>::kind];
         this->prev = *stack;
         *stack = reinterpret_cast<Rooted<void*>*>(this);
     }
 
   public:
     template <typename RootingContext>
     explicit Rooted(const RootingContext& cx)
       : ptr(js::GCPolicy<T>::initial())
@@ -1007,26 +987,26 @@ class PersistentRooted : public js::Pers
     friend class mozilla::LinkedListElement<PersistentRooted>;
 
     friend struct js::gc::PersistentRootedMarker<T>;
 
     friend void js::gc::FinishPersistentRootedChains(js::RootLists&);
 
     void registerWithRootLists(js::RootLists& roots) {
         MOZ_ASSERT(!initialized());
-        js::ThingRootKind kind = js::RootKind<T>::rootKind();
+        JS::RootKind kind = JS::MapTypeToRootKind<T>::kind;
         roots.heapRoots_[kind].insertBack(reinterpret_cast<JS::PersistentRooted<void*>*>(this));
         // Until marking and destruction support the full set, we assert that
         // we don't try to add any unsupported types.
-        MOZ_ASSERT(kind == js::THING_ROOT_OBJECT ||
-                   kind == js::THING_ROOT_SCRIPT ||
-                   kind == js::THING_ROOT_STRING ||
-                   kind == js::THING_ROOT_ID ||
-                   kind == js::THING_ROOT_VALUE ||
-                   kind == js::THING_ROOT_TRACEABLE);
+        MOZ_ASSERT(kind == JS::RootKind::Object ||
+                   kind == JS::RootKind::Script ||
+                   kind == JS::RootKind::String ||
+                   kind == JS::RootKind::Id ||
+                   kind == JS::RootKind::Value ||
+                   kind == JS::RootKind::Traceable);
     }
 
   public:
     PersistentRooted() : ptr(js::GCPolicy<T>::initial()) {}
 
     template <typename RootingContext>
     explicit PersistentRooted(const RootingContext& cx)
       : ptr(js::GCPolicy<T>::initial())
--- a/js/public/TraceKind.h
+++ b/js/public/TraceKind.h
@@ -55,37 +55,101 @@ enum class TraceKind
     JitCode = 0x1F,
     LazyScript = 0x2F
 };
 const static uintptr_t OutOfLineTraceKindMask = 0x07;
 static_assert(uintptr_t(JS::TraceKind::BaseShape) & OutOfLineTraceKindMask, "mask bits are set");
 static_assert(uintptr_t(JS::TraceKind::JitCode) & OutOfLineTraceKindMask, "mask bits are set");
 static_assert(uintptr_t(JS::TraceKind::LazyScript) & OutOfLineTraceKindMask, "mask bits are set");
 
+// When this header is imported inside SpiderMonkey, the class definitions are
+// available and we can query those definitions to find the correct kind
+// directly from the class hierarchy.
+template <typename T>
+struct MapTypeToTraceKind {
+    static const JS::TraceKind kind = T::TraceKind;
+};
+
+// When this header is used outside SpiderMonkey, the class definitions are not
+// available, so the following table containing all public GC types is used.
 #define JS_FOR_EACH_TRACEKIND(D) \
  /* PrettyName       TypeName           AddToCCKind */ \
     D(BaseShape,     js::BaseShape,     true) \
     D(JitCode,       js::jit::JitCode,  true) \
     D(LazyScript,    js::LazyScript,    true) \
     D(Object,        JSObject,          true) \
     D(ObjectGroup,   js::ObjectGroup,   true) \
     D(Script,        JSScript,          true) \
     D(Shape,         js::Shape,         true) \
     D(String,        JSString,          false) \
     D(Symbol,        JS::Symbol,        false)
 
-// Map from base trace type to the trace kind.
-template <typename T> struct MapTypeToTraceKind {};
+// Map from all public types to their trace kind.
 #define JS_EXPAND_DEF(name, type, _) \
     template <> struct MapTypeToTraceKind<type> { \
         static const JS::TraceKind kind = JS::TraceKind::name; \
     };
 JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
 #undef JS_EXPAND_DEF
 
+// RootKind is closely related to TraceKind. Whereas TraceKind's indices are
+// laid out for convenient embedding as a pointer tag, the indicies of RootKind
+// are designed for use as array keys via EnumeratedArray.
+enum class RootKind : int8_t
+{
+    // These map 1:1 with trace kinds.
+    BaseShape = 0,
+    JitCode,
+    LazyScript,
+    Object,
+    ObjectGroup,
+    Script,
+    Shape,
+    String,
+    Symbol,
+
+    // These tagged pointers are special-cased for performance.
+    Id,
+    Value,
+
+    // Everything else.
+    Traceable,
+
+    Limit
+};
+
+// Most RootKind correspond directly to a trace kind.
+template <TraceKind traceKind> struct MapTraceKindToRootKind {};
+#define JS_EXPAND_DEF(name, _0, _1) \
+    template <> struct MapTraceKindToRootKind<JS::TraceKind::name> { \
+        static const JS::RootKind kind = JS::RootKind::name; \
+    };
+JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF)
+#undef JS_EXPAND_DEF
+
+// Specify the RootKind for all types. Value and jsid map to special cases;
+// pointer types we can derive directly from the TraceKind; everything else
+// should go in the Traceable list and use GCPolicy<T>::trace for tracing.
+template <typename T>
+struct MapTypeToRootKind {
+    static const JS::RootKind kind = JS::RootKind::Traceable;
+};
+template <typename T>
+struct MapTypeToRootKind<T*> {
+    static const JS::RootKind kind = \
+        JS::MapTraceKindToRootKind<JS::MapTypeToTraceKind<T>::kind>::kind;
+};
+template <> struct MapTypeToRootKind<JS::Value> {
+    static const JS::RootKind kind = JS::RootKind::Value;
+};
+template <> struct MapTypeToRootKind<jsid> {
+    static const JS::RootKind kind = JS::RootKind::Id;
+};
+template <> struct MapTypeToRootKind<JSFunction*> : public MapTypeToRootKind<JSObject*> {};
+
 // Fortunately, few places in the system need to deal with fully abstract
 // cells. In those places that do, we generally want to move to a layout
 // templated function as soon as possible. This template wraps the upcast
 // for that dispatch.
 //
 // Given a call:
 //
 //    DispatchTraceKindTyped(f, thing, traceKind, ... args)
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -37,53 +37,46 @@ using JS::AutoGCRooter;
 
 typedef RootedValueMap::Range RootRange;
 typedef RootedValueMap::Entry RootEntry;
 typedef RootedValueMap::Enum RootEnum;
 
 template <typename T>
 using TraceFunction = void (*)(JSTracer* trc, T* ref, const char* name);
 
-template <class T, TraceFunction<T> TraceFn = TraceNullableRoot, class Source>
+template <class T, TraceFunction<T> TraceFn = TraceNullableRoot>
 static inline void
-MarkExactStackRootList(JSTracer* trc, Source* s, const char* name)
+MarkExactStackRootList(JSTracer* trc, JS::Rooted<void*>* rooter, const char* name)
 {
-    Rooted<T>* rooter = s->roots.template gcRooters<T>();
     while (rooter) {
-        T* addr = rooter->address();
+        T* addr = reinterpret_cast<JS::Rooted<T>*>(rooter)->address();
         TraceFn(trc, addr, name);
         rooter = rooter->previous();
     }
 }
 
-template<class T>
-static void
-MarkExactStackRootsAcrossTypes(T context, JSTracer* trc)
+void
+js::RootLists::traceStackRoots(JSTracer* trc)
 {
-    MarkExactStackRootList<JSObject*>(trc, context, "exact-object");
-    MarkExactStackRootList<Shape*>(trc, context, "exact-shape");
-    MarkExactStackRootList<BaseShape*>(trc, context, "exact-baseshape");
-    MarkExactStackRootList<ObjectGroup*>(trc, context, "exact-objectgroup");
-    MarkExactStackRootList<JSString*>(trc, context, "exact-string");
-    MarkExactStackRootList<JS::Symbol*>(trc, context, "exact-symbol");
-    MarkExactStackRootList<jit::JitCode*>(trc, context, "exact-jitcode");
-    MarkExactStackRootList<JSScript*>(trc, context, "exact-script");
-    MarkExactStackRootList<LazyScript*>(trc, context, "exact-lazy-script");
-    MarkExactStackRootList<jsid>(trc, context, "exact-id");
-    MarkExactStackRootList<Value>(trc, context, "exact-value");
+#define MARK_ROOTS(name, type, _) \
+    MarkExactStackRootList<type*>(trc, stackRoots_[JS::RootKind::name], "exact-" #name);
+JS_FOR_EACH_TRACEKIND(MARK_ROOTS)
+#undef MARK_ROOTS
+    MarkExactStackRootList<jsid>(trc, stackRoots_[JS::RootKind::Id], "exact-id");
+    MarkExactStackRootList<Value>(trc, stackRoots_[JS::RootKind::Value], "exact-value");
     MarkExactStackRootList<JS::Traceable, js::DispatchWrapper<JS::Traceable>::TraceWrapped>(
-        trc, context, "Traceable");
+        trc, stackRoots_[JS::RootKind::Traceable], "Traceable");
 }
 
 static void
 MarkExactStackRoots(JSRuntime* rt, JSTracer* trc)
 {
     for (ContextIter cx(rt); !cx.done(); cx.next())
-        MarkExactStackRootsAcrossTypes<JSContext*>(cx.get(), trc);
-    MarkExactStackRootsAcrossTypes<PerThreadData*>(&rt->mainThread, trc);
+        cx->roots.traceStackRoots(trc);
+    rt->mainThread.roots.traceStackRoots(trc);
 }
 
 inline void
 AutoGCRooter::trace(JSTracer* trc)
 {
     switch (tag_) {
       case PARSER:
         frontend::MarkParser(trc, this);
@@ -241,17 +234,17 @@ js::gc::MarkPersistentRootedChainsInList
     PersistentRootedMarker<jsid>::markChain(trc, roots.getPersistentRootedList<jsid>(),
                                             "PersistentRooted<jsid>");
     PersistentRootedMarker<Value>::markChain(trc, roots.getPersistentRootedList<Value>(),
                                              "PersistentRooted<Value>");
 
     PersistentRootedMarker<JS::Traceable>::markChain<
         js::DispatchWrapper<JS::Traceable>::TraceWrapped>(trc,
             reinterpret_cast<mozilla::LinkedList<JS::PersistentRooted<JS::Traceable>>&>(
-                roots.heapRoots_[THING_ROOT_TRACEABLE]),
+                roots.heapRoots_[JS::RootKind::Traceable]),
             "PersistentRooted<Traceable>");
 }
 
 void
 js::gc::MarkPersistentRootedChains(JSTracer* trc)
 {
     for (ContextIter cx(trc->runtime()); !cx.done(); cx.next())
         MarkPersistentRootedChainsInLists(cx->roots, trc);
--- a/js/src/jit/IonCode.h
+++ b/js/src/jit/IonCode.h
@@ -155,17 +155,17 @@ class JitCode : public gc::TenuredCell
     // Allocates a new JitCode object which will be managed by the GC. If no
     // object can be allocated, nullptr is returned. On failure, |pool| is
     // automatically released, so the code may be freed.
     template <AllowGC allowGC>
     static JitCode* New(JSContext* cx, uint8_t* code, uint32_t bufferSize, uint32_t headerSize,
                         ExecutablePool* pool, CodeKind kind);
 
   public:
-    static inline ThingRootKind rootKind() { return THING_ROOT_JIT_CODE; }
+    static const JS::TraceKind TraceKind = JS::TraceKind::JitCode;
 };
 
 class SnapshotWriter;
 class RecoverWriter;
 class SafepointWriter;
 class SafepointIndex;
 class OsiIndex;
 class IonCache;
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -181,18 +181,19 @@ js::DestroyContext(JSContext* cx, Destro
         rt->gc.gc(GC_NORMAL, JS::gcreason::DESTROY_CONTEXT);
     }
     js_delete_poison(cx);
 }
 
 void
 RootLists::checkNoGCRooters() {
 #ifdef DEBUG
-    for (int i = 0; i < THING_ROOT_LIMIT; ++i)
-        MOZ_ASSERT(stackRoots_[i] == nullptr);
+    for (auto const& stackRootPtr : stackRoots_) {
+        MOZ_ASSERT(stackRootPtr == nullptr);
+    }
 #endif
 }
 
 bool
 AutoResolving::alreadyStartedSlow() const
 {
     MOZ_ASSERT(link);
     AutoResolving* cursor = link;
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1384,17 +1384,17 @@ FinishPersistentRootedChain(mozilla::Lin
 void
 js::gc::FinishPersistentRootedChains(RootLists& roots)
 {
     FinishPersistentRootedChain(roots.getPersistentRootedList<JSObject*>());
     FinishPersistentRootedChain(roots.getPersistentRootedList<JSScript*>());
     FinishPersistentRootedChain(roots.getPersistentRootedList<JSString*>());
     FinishPersistentRootedChain(roots.getPersistentRootedList<jsid>());
     FinishPersistentRootedChain(roots.getPersistentRootedList<Value>());
-    FinishPersistentRootedChain(roots.heapRoots_[THING_ROOT_TRACEABLE]);
+    FinishPersistentRootedChain(roots.heapRoots_[JS::RootKind::Traceable]);
 }
 
 void
 GCRuntime::finishRoots()
 {
     if (rootsHash.initialized())
         rootsHash.clear();
 
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -287,17 +287,17 @@ class JSObject : public js::gc::Cell
     JSAtom* maybeConstructorDisplayAtom() const;
 
     /* GC support. */
 
     void traceChildren(JSTracer* trc);
 
     void fixupAfterMovingGC();
 
-    static js::ThingRootKind rootKind() { return js::THING_ROOT_OBJECT; }
+    static const JS::TraceKind TraceKind = JS::TraceKind::Object;
     static const size_t MaxTagBits = 3;
     static bool isNullLike(const JSObject* obj) { return uintptr_t(obj) < (1 << MaxTagBits); }
 
     MOZ_ALWAYS_INLINE JS::Zone* zone() const {
         return group_->zone();
     }
     MOZ_ALWAYS_INLINE JS::shadow::Zone* shadowZone() const {
         return JS::shadow::Zone::asShadowZone(zone());
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -7,22 +7,24 @@
 #ifndef jspubtd_h
 #define jspubtd_h
 
 /*
  * JS public API typedefs.
  */
 
 #include "mozilla/Assertions.h"
+#include "mozilla/EnumeratedArray.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/PodOperations.h"
 
 #include "jsprototypes.h"
 #include "jstypes.h"
 
+#include "js/TraceKind.h"
 #include "js/TypeDecls.h"
 
 #if defined(JS_GC_ZEAL) || defined(DEBUG)
 # define JSGC_HASH_TABLE_CHECKS
 #endif
 
 namespace JS {
 
@@ -257,135 +259,94 @@ class ExclusiveContext;
 enum StackKind
 {
     StackForSystemCode,      // C++, such as the GC, running on behalf of the VM.
     StackForTrustedScript,   // Script running with trusted principals.
     StackForUntrustedScript, // Script running with untrusted principals.
     StackKindCount
 };
 
-enum ThingRootKind
-{
-    THING_ROOT_OBJECT,
-    THING_ROOT_SHAPE,
-    THING_ROOT_BASE_SHAPE,
-    THING_ROOT_OBJECT_GROUP,
-    THING_ROOT_STRING,
-    THING_ROOT_SYMBOL,
-    THING_ROOT_JIT_CODE,
-    THING_ROOT_SCRIPT,
-    THING_ROOT_LAZY_SCRIPT,
-    THING_ROOT_ID,
-    THING_ROOT_VALUE,
-    THING_ROOT_TRACEABLE,
-    THING_ROOT_LIMIT
-};
-
-template <typename T>
-struct RootKind;
-
-/*
- * Specifically mark the ThingRootKind of externally visible types, so that
- * JSAPI users may use JSRooted... types without having the class definition
- * available.
- */
-template<typename T, ThingRootKind Kind>
-struct SpecificRootKind
-{
-    static ThingRootKind rootKind() { return Kind; }
-};
-
-template <> struct RootKind<JSObject*> : SpecificRootKind<JSObject*, THING_ROOT_OBJECT> {};
-template <> struct RootKind<JSFlatString*> : SpecificRootKind<JSFlatString*, THING_ROOT_STRING> {};
-template <> struct RootKind<JSFunction*> : SpecificRootKind<JSFunction*, THING_ROOT_OBJECT> {};
-template <> struct RootKind<JSString*> : SpecificRootKind<JSString*, THING_ROOT_STRING> {};
-template <> struct RootKind<JS::Symbol*> : SpecificRootKind<JS::Symbol*, THING_ROOT_SYMBOL> {};
-template <> struct RootKind<JSScript*> : SpecificRootKind<JSScript*, THING_ROOT_SCRIPT> {};
-template <> struct RootKind<jsid> : SpecificRootKind<jsid, THING_ROOT_ID> {};
-template <> struct RootKind<JS::Value> : SpecificRootKind<JS::Value, THING_ROOT_VALUE> {};
-
 // Abstracts JS rooting mechanisms so they can be shared between the JSContext
 // and JSRuntime.
 class RootLists
 {
     // Stack GC roots for stack-allocated GC heap pointers.
-    JS::Rooted<void*>* stackRoots_[THING_ROOT_LIMIT];
+    mozilla::EnumeratedArray<JS::RootKind, JS::RootKind::Limit, JS::Rooted<void*>*> stackRoots_;
     template <typename T> friend class JS::Rooted;
 
     // Stack GC roots for stack-allocated AutoFooRooter classes.
     JS::AutoGCRooter* autoGCRooters_;
     friend class JS::AutoGCRooter;
 
   public:
     RootLists() : autoGCRooters_(nullptr) {
-        mozilla::PodArrayZero(stackRoots_);
+        for (auto& stackRootPtr : stackRoots_) {
+            stackRootPtr = nullptr;
+        }
     }
 
-    template <class T>
-    inline JS::Rooted<T>* gcRooters() {
-        js::ThingRootKind kind = RootKind<T>::rootKind();
-        return reinterpret_cast<JS::Rooted<T>*>(stackRoots_[kind]);
-    }
+    void traceStackRoots(JSTracer* trc);
 
     void checkNoGCRooters();
 
     /* Allow inlining of PersistentRooted constructors and destructors. */
   private:
     template <typename Referent> friend class JS::PersistentRooted;
     friend void js::gc::MarkPersistentRootedChains(JSTracer*);
     friend void js::gc::MarkPersistentRootedChainsInLists(RootLists&, JSTracer*);
     friend void js::gc::FinishPersistentRootedChains(RootLists&);
 
-    mozilla::LinkedList<JS::PersistentRooted<void*>> heapRoots_[THING_ROOT_LIMIT];
+    mozilla::EnumeratedArray<JS::RootKind, JS::RootKind::Limit,
+                             mozilla::LinkedList<JS::PersistentRooted<void*>>> heapRoots_;
 
     /* Specializations of this return references to the appropriate list. */
     template<typename Referent>
     inline mozilla::LinkedList<JS::PersistentRooted<Referent>>& getPersistentRootedList();
 };
 
 template<>
 inline mozilla::LinkedList<JS::PersistentRootedFunction>&
 RootLists::getPersistentRootedList<JSFunction*>() {
     return reinterpret_cast<mozilla::LinkedList<JS::PersistentRooted<JSFunction*>>&>(
-        heapRoots_[THING_ROOT_OBJECT]);
+        heapRoots_[JS::RootKind::Object]);
 }
 
 template<>
 inline mozilla::LinkedList<JS::PersistentRootedObject>&
 RootLists::getPersistentRootedList<JSObject*>() {
     return reinterpret_cast<mozilla::LinkedList<JS::PersistentRooted<JSObject*>>&>(
-        heapRoots_[THING_ROOT_OBJECT]);
+        heapRoots_[JS::RootKind::Object]);
 }
 
 template<>
 inline mozilla::LinkedList<JS::PersistentRootedId>&
 RootLists::getPersistentRootedList<jsid>() {
     return reinterpret_cast<mozilla::LinkedList<JS::PersistentRooted<jsid>>&>(
-        heapRoots_[THING_ROOT_ID]);
+        heapRoots_[JS::RootKind::Id]);
 }
 
 template<>
 inline mozilla::LinkedList<JS::PersistentRootedScript>&
 RootLists::getPersistentRootedList<JSScript*>() {
     return reinterpret_cast<mozilla::LinkedList<JS::PersistentRooted<JSScript*>>&>(
-        heapRoots_[THING_ROOT_SCRIPT]);
+        heapRoots_[JS::RootKind::Script]);
 }
 
 template<>
 inline mozilla::LinkedList<JS::PersistentRootedString>&
 RootLists::getPersistentRootedList<JSString*>() {
     return reinterpret_cast<mozilla::LinkedList<JS::PersistentRooted<JSString*>>&>(
-        heapRoots_[THING_ROOT_STRING]);
+        heapRoots_[JS::RootKind::String]);
 }
 
 template<>
 inline mozilla::LinkedList<JS::PersistentRootedValue>&
 RootLists::getPersistentRootedList<JS::Value>() {
     return reinterpret_cast<mozilla::LinkedList<JS::PersistentRooted<JS::Value>>&>(
-        heapRoots_[THING_ROOT_VALUE]);
+        heapRoots_[JS::RootKind::Value]);
 }
 
 struct ContextFriendFields
 {
   protected:
     JSRuntime* const     runtime_;
 
     /* The current compartment. */
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -1942,17 +1942,17 @@ class JSScript : public js::gc::TenuredC
 
 #ifdef DEBUG
     uint32_t stepModeCount() { return hasDebugScript_ ? debugScript()->stepMode : 0; }
 #endif
 
     void finalize(js::FreeOp* fop);
     void fixupAfterMovingGC() {}
 
-    static inline js::ThingRootKind rootKind() { return js::THING_ROOT_SCRIPT; }
+    static const JS::TraceKind TraceKind = JS::TraceKind::Script;
 
     void traceChildren(JSTracer* trc);
 
     // A helper class to prevent relazification of the given function's script
     // while it's holding on to it.  This class automatically roots the script.
     class AutoDelazify;
     friend class AutoDelazify;
 
@@ -2384,17 +2384,17 @@ class LazyScript : public gc::TenuredCel
 
     bool hasUncompiledEnclosingScript() const;
 
     friend class GCMarker;
     void traceChildren(JSTracer* trc);
     void finalize(js::FreeOp* fop);
     void fixupAfterMovingGC() {}
 
-    static inline js::ThingRootKind rootKind() { return js::THING_ROOT_LAZY_SCRIPT; }
+    static const JS::TraceKind TraceKind = JS::TraceKind::LazyScript;
 
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
     {
         return mallocSizeOf(table_);
     }
 
     uint64_t packedFields() const {
         return packedFields_;
--- a/js/src/vm/ObjectGroup.h
+++ b/js/src/vm/ObjectGroup.h
@@ -416,17 +416,17 @@ class ObjectGroup : public gc::TenuredCe
         flags_ |= generation << OBJECT_FLAG_GENERATION_SHIFT;
     }
 
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
     void finalize(FreeOp* fop);
     void fixupAfterMovingGC() {}
 
-    static inline ThingRootKind rootKind() { return THING_ROOT_OBJECT_GROUP; }
+    static const JS::TraceKind TraceKind = JS::TraceKind::ObjectGroup;
 
     static inline uint32_t offsetOfClasp() {
         return offsetof(ObjectGroup, clasp_);
     }
 
     static inline uint32_t offsetOfProto() {
         return offsetof(ObjectGroup, proto_);
     }
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -441,17 +441,17 @@ class BaseShape : public gc::TenuredCell
     inline UnownedBaseShape* toUnowned();
 
     /* Check that an owned base shape is consistent with its unowned base. */
     void assertConsistency();
 
     /* For JIT usage */
     static inline size_t offsetOfFlags() { return offsetof(BaseShape, flags); }
 
-    static inline ThingRootKind rootKind() { return THING_ROOT_BASE_SHAPE; }
+    static const JS::TraceKind TraceKind = JS::TraceKind::BaseShape;
 
     void traceChildren(JSTracer* trc);
 
     void fixupAfterMovingGC() {}
 
   private:
     static void staticAsserts() {
         JS_STATIC_ASSERT(offsetof(BaseShape, clasp_) == offsetof(js::shadow::BaseShape, clasp_));
@@ -942,17 +942,17 @@ class Shape : public gc::TenuredCell
     void dump(JSContext* cx, FILE* fp) const;
     void dumpSubtree(JSContext* cx, int level, FILE* fp) const;
 #endif
 
     void sweep();
     void finalize(FreeOp* fop);
     void removeChild(Shape* child);
 
-    static inline ThingRootKind rootKind() { return THING_ROOT_SHAPE; }
+    static const JS::TraceKind TraceKind = JS::TraceKind::Shape;
 
     void traceChildren(JSTracer* trc);
 
     inline Shape* search(ExclusiveContext* cx, jsid id);
     inline Shape* searchLinear(jsid id);
 
     void fixupAfterMovingGC();
     void fixupGetterSetterForBarrier(JSTracer* trc);
@@ -1401,19 +1401,16 @@ Shape::searchNoHashify(Shape* start, jsi
 inline bool
 Shape::matches(const StackShape& other) const
 {
     return propid_.get() == other.propid &&
            matchesParamsAfterId(other.base, other.slot_, other.attrs, other.flags,
                                 other.rawGetter, other.rawSetter);
 }
 
-template<> struct RootKind<Shape*> : SpecificRootKind<Shape*, THING_ROOT_SHAPE> {};
-template<> struct RootKind<BaseShape*> : SpecificRootKind<BaseShape*, THING_ROOT_BASE_SHAPE> {};
-
 // Property lookup hooks on objects are required to return a non-nullptr shape
 // to signify that the property has been found. For cases where the property is
 // not actually represented by a Shape, use a dummy value. This includes all
 // properties of non-native objects, and dense elements for native objects.
 // Use separate APIs for these two cases.
 
 template <AllowGC allowGC>
 static inline void
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -487,17 +487,17 @@ class JSString : public js::gc::TenuredC
 
     static size_t offsetOfNonInlineChars() {
         static_assert(offsetof(JSString, d.s.u2.nonInlineCharsTwoByte) ==
                       offsetof(JSString, d.s.u2.nonInlineCharsLatin1),
                       "nonInlineCharsTwoByte and nonInlineCharsLatin1 must have same offset");
         return offsetof(JSString, d.s.u2.nonInlineCharsTwoByte);
     }
 
-    static inline js::ThingRootKind rootKind() { return js::THING_ROOT_STRING; }
+    static const JS::TraceKind TraceKind = JS::TraceKind::String;
 
 #ifdef DEBUG
     void dump();
     void dumpCharsNoNewline(FILE* fp=stderr);
     void dumpRepresentation(FILE* fp, int indent) const;
     void dumpRepresentationHeader(FILE* fp, int indent, const char* subclass) const;
 
     template <typename CharT>
--- a/js/src/vm/Symbol.h
+++ b/js/src/vm/Symbol.h
@@ -51,17 +51,17 @@ class Symbol : public js::gc::TenuredCel
     static Symbol* new_(js::ExclusiveContext* cx, SymbolCode code, JSString* description);
     static Symbol* for_(js::ExclusiveContext* cx, js::HandleString description);
 
     JSAtom* description() const { return description_; }
     SymbolCode code() const { return code_; }
 
     bool isWellKnownSymbol() const { return uint32_t(code_) < WellKnownSymbolLimit; }
 
-    static inline js::ThingRootKind rootKind() { return js::THING_ROOT_SYMBOL; }
+    static const JS::TraceKind TraceKind = JS::TraceKind::Symbol;
     inline void traceChildren(JSTracer* trc) {
         if (description_)
             js::TraceManuallyBarrieredEdge(trc, &description_, "description");
     }
     inline void finalize(js::FreeOp*) {}
 
     static MOZ_ALWAYS_INLINE void writeBarrierPre(Symbol* thing) {
         if (thing && !thing->isWellKnownSymbol())