Bug 1020690 - Type exact stack rooting machinery. r=sfink
☠☠ backed out by b2e6b3caa5a2 ☠ ☠
authorJon Coppeard <jcoppeard@mozilla.com>
Thu, 05 Jun 2014 10:38:00 -0400
changeset 194226 846ee7c7debfc1405079b2f981e061b7ec5598b3
parent 194225 f0e12744b2a9833e0ad283b7411f02f13f884a7d
child 194227 c0323d9a7ea32be54faf04fcd522ebb5a399127b
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-esr52@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1020690
milestone32.0a1
Bug 1020690 - Type exact stack rooting machinery. r=sfink
js/public/RootingAPI.h
js/src/NamespaceImports.h
js/src/gc/RootMarking.cpp
js/src/jsapi.h
js/src/jscntxt.cpp
js/src/jsinfer.h
js/src/jspubtd.h
js/src/jsscript.h
js/src/vm/Shape.h
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -629,21 +629,27 @@ class InternalHandle<T*>
       : holder(reinterpret_cast<void * const *>(&js::NullPtr::constNullValue)),
         offset(uintptr_t(field))
     {}
 
     void operator=(InternalHandle<T*> other) MOZ_DELETE;
 };
 
 /*
- * By default, pointers should use the inheritance hierarchy to find their
+ * 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 GCMethods<T *>
 {
@@ -787,17 +793,17 @@ class MOZ_STACK_CLASS Rooted : public js
 #ifdef JSGC_TRACK_EXACT_ROOTS
     ~Rooted() {
         MOZ_ASSERT(*stack == reinterpret_cast<Rooted<void*>*>(this));
         *stack = prev;
     }
 #endif
 
 #ifdef JSGC_TRACK_EXACT_ROOTS
-    Rooted<T> *previous() { return prev; }
+    Rooted<T> *previous() { return reinterpret_cast<Rooted<T>*>(prev); }
 #endif
 
     /*
      * Important: Return a reference here so passing a Rooted<T> to
      * something that takes a |const T&| is not a GC hazard.
      */
     operator const T&() const { return ptr; }
     T operator->() const { return ptr; }
@@ -822,17 +828,22 @@ class MOZ_STACK_CLASS Rooted : public js
         ptr = value;
     }
 
     bool operator!=(const T &other) const { return ptr != other; }
     bool operator==(const T &other) const { return ptr == other; }
 
   private:
 #ifdef JSGC_TRACK_EXACT_ROOTS
-    Rooted<void*> **stack, *prev;
+    /*
+     * These need to be templated on void* to avoid aliasing issues between, for
+     * example, Rooted<JSObject> and Rooted<JSFunction>, which use the same
+     * stack head pointer for different classes.
+     */
+    Rooted<void *> **stack, *prev;
 #endif
 
     /*
      * |ptr| must be the last field in Rooted because the analysis treats all
      * Rooted as Rooted<void*> during the analysis. See bug 829372.
      */
     T ptr;
 
@@ -858,71 +869,16 @@ namespace js {
 template <>
 class RootedBase<JSObject*>
 {
   public:
     template <class U>
     JS::Handle<U*> as() const;
 };
 
-
-/*
- * RootedGeneric<T> allows a class to instantiate its own Rooted type by
- * including the following two methods:
- *
- *    static inline js::ThingRootKind rootKind() { return js::THING_ROOT_CUSTOM; }
- *    void trace(JSTracer *trc);
- *
- * The trace() method must trace all of the class's fields.
- *
- * Implementation:
- *
- * RootedGeneric<T> works by placing a pointer to its 'rooter' field into the
- * usual list of rooters when it is instantiated. When marking, it backs up
- * from this pointer to find a vtable containing a type-appropriate trace()
- * method.
- */
-template <typename GCType>
-class JS_PUBLIC_API(RootedGeneric)
-{
-  public:
-    JS::Rooted<GCType> rooter;
-
-    explicit RootedGeneric(js::ContextFriendFields *cx)
-        : rooter(cx)
-    {
-    }
-
-    RootedGeneric(js::ContextFriendFields *cx, const GCType &initial)
-        : rooter(cx, initial)
-    {
-    }
-
-    virtual inline void trace(JSTracer *trc);
-
-    operator const GCType&() const { return rooter.get(); }
-    GCType operator->() const { return rooter.get(); }
-};
-
-template <typename GCType>
-inline void RootedGeneric<GCType>::trace(JSTracer *trc)
-{
-    rooter->trace(trc);
-}
-
-// We will instantiate RootedGeneric<void*> in RootMarking.cpp, and MSVC will
-// notice that void*s have no trace() method defined on them and complain (even
-// though it's never called.) MSVC's complaint is not unreasonable, so
-// specialize for void*.
-template <>
-inline void RootedGeneric<void*>::trace(JSTracer *trc)
-{
-    MOZ_ASSUME_UNREACHABLE("RootedGeneric<void*>::trace()");
-}
-
 /* Interface substitute for Rooted<T> which does not root the variable's memory. */
 template <typename T>
 class FakeRooted : public RootedBase<T>
 {
   public:
     template <typename CX>
     FakeRooted(CX *cx
                MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
--- a/js/src/NamespaceImports.h
+++ b/js/src/NamespaceImports.h
@@ -31,16 +31,17 @@ class AutoScriptVector;
 class AutoValueVector;
 
 class AutoIdArray;
 
 class JS_PUBLIC_API(AutoGCRooter);
 template <typename T> class AutoVectorRooter;
 template<typename K, typename V> class AutoHashMapRooter;
 template<typename T> class AutoHashSetRooter;
+template<typename T> class RootedGeneric;
 
 class MOZ_STACK_CLASS SourceBufferHolder;
 
 class HandleValueArray;
 
 class JS_PUBLIC_API(AutoCheckCannotGC);
 
 }
@@ -78,16 +79,17 @@ using JS::AutoScriptVector;
 using JS::AutoValueVector;
 
 using JS::AutoIdArray;
 
 using JS::AutoGCRooter;
 using JS::AutoHashMapRooter;
 using JS::AutoHashSetRooter;
 using JS::AutoVectorRooter;
+using JS::RootedGeneric;
 
 using JS::CallArgs;
 using JS::CallNonGenericMethod;
 using JS::CallReceiver;
 using JS::CompileOptions;
 using JS::IsAcceptableThis;
 using JS::NativeImpl;
 using JS::OwningCompileOptions;
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -35,69 +35,87 @@ using namespace js::gc;
 
 using mozilla::ArrayEnd;
 
 typedef RootedValueMap::Range RootRange;
 typedef RootedValueMap::Entry RootEntry;
 typedef RootedValueMap::Enum RootEnum;
 
 #ifdef JSGC_USE_EXACT_ROOTING
-static inline void
-MarkExactStackRoot(JSTracer *trc, Rooted<void*> *rooter, ThingRootKind kind)
+static void
+MarkBindingsRoot(JSTracer *trc, Bindings *bindings, const char *name)
+{
+    bindings->trace(trc);
+}
+
+static void
+MarkPropertyDescriptorRoot(JSTracer *trc, JSPropertyDescriptor *pd, const char *name)
 {
-    void **addr = (void **)rooter->address();
-    if (IsNullTaggedPointer(*addr))
-        return;
+    pd->trace(trc);
+}
 
-    if (kind == THING_ROOT_OBJECT && *addr == TaggedProto::LazyProto)
-        return;
+template <class T>
+static inline bool
+IgnoreExactRoot(T *thingp)
+{
+    return false;
+}
 
-    switch (kind) {
-      case THING_ROOT_OBJECT:      MarkObjectRoot(trc, (JSObject **)addr, "exact-object"); break;
-      case THING_ROOT_STRING:      MarkStringRoot(trc, (JSString **)addr, "exact-string"); break;
-      case THING_ROOT_SCRIPT:      MarkScriptRoot(trc, (JSScript **)addr, "exact-script"); break;
-      case THING_ROOT_LAZY_SCRIPT: MarkLazyScriptRoot(trc, (LazyScript **)addr, "exact-lazy-script"); break;
-      case THING_ROOT_SHAPE:       MarkShapeRoot(trc, (Shape **)addr, "exact-shape"); break;
-      case THING_ROOT_BASE_SHAPE:  MarkBaseShapeRoot(trc, (BaseShape **)addr, "exact-baseshape"); break;
-      case THING_ROOT_TYPE:        MarkTypeRoot(trc, (types::Type *)addr, "exact-type"); break;
-      case THING_ROOT_TYPE_OBJECT: MarkTypeObjectRoot(trc, (types::TypeObject **)addr, "exact-typeobject"); break;
-      case THING_ROOT_JIT_CODE:    MarkJitCodeRoot(trc, (jit::JitCode **)addr, "exact-jitcode"); break;
-      case THING_ROOT_VALUE:       MarkValueRoot(trc, (Value *)addr, "exact-value"); break;
-      case THING_ROOT_ID:          MarkIdRoot(trc, (jsid *)addr, "exact-id"); break;
-      case THING_ROOT_BINDINGS:    ((Bindings *)addr)->trace(trc); break;
-      case THING_ROOT_PROPERTY_DESCRIPTOR: ((JSPropertyDescriptor *)addr)->trace(trc); break;
-      case THING_ROOT_CUSTOM: {
-          // 'rooter' is a member within a class containing a vtable. Back up
-          // to the vtable and call trace() through it.
-          const size_t rooterOffset = offsetof(RootedGeneric<void*>, rooter);
-          reinterpret_cast< RootedGeneric<void*>* >(uintptr_t(rooter) - rooterOffset)->trace(trc);
-          break;
-      }
-      default: MOZ_ASSUME_UNREACHABLE("Invalid THING_ROOT kind"); break;
+template <class T>
+inline bool
+IgnoreExactRoot(T **thingp)
+{
+    return IsNullTaggedPointer(*thingp);
+}
+
+template <>
+inline bool
+IgnoreExactRoot(JSObject **thingp)
+{
+    return IsNullTaggedPointer(*thingp) || *thingp == TaggedProto::LazyProto;
+}
+
+template <class T, void (MarkFunc)(JSTracer *trc, T *ref, const char *name), class Source>
+static inline void
+MarkExactStackRootList(JSTracer *trc, Source *s, const char *name)
+{
+    Rooted<T> *rooter = s->template gcRooters<T>();
+    while (rooter) {
+        T *addr = rooter->address();
+        if (!IgnoreExactRoot(addr))
+            MarkFunc(trc, addr, name);
+        rooter = rooter->previous();
     }
 }
 
+template <class T, void (MarkFunc)(JSTracer *trc, T *ref, const char *name)>
 static inline void
-MarkExactStackRootList(JSTracer *trc, Rooted<void*> *rooter, ThingRootKind kind)
+MarkExactStackRootsForType(JSTracer *trc, const char *name = nullptr)
 {
-    while (rooter) {
-        MarkExactStackRoot(trc, rooter, kind);
-        rooter = rooter->previous();
-    }
+    for (ContextIter cx(trc->runtime()); !cx.done(); cx.next())
+        MarkExactStackRootList<T, MarkFunc>(trc, cx.get(), name);
+    MarkExactStackRootList<T, MarkFunc>(trc, &trc->runtime()->mainThread, name);
 }
 
 static void
 MarkExactStackRoots(JSTracer *trc)
 {
-    for (unsigned i = 0; i < THING_ROOT_LIMIT; i++) {
-        for (ContextIter cx(trc->runtime()); !cx.done(); cx.next())
-            MarkExactStackRootList(trc, cx->thingGCRooters[i], ThingRootKind(i));
-
-        MarkExactStackRootList(trc, trc->runtime()->mainThread.thingGCRooters[i], ThingRootKind(i));
-    }
+    MarkExactStackRootsForType<JSObject *, MarkObjectRoot>(trc, "exact-object");
+    MarkExactStackRootsForType<Shape *, MarkShapeRoot>(trc, "exact-shape");
+    MarkExactStackRootsForType<BaseShape *, MarkBaseShapeRoot>(trc, "exact-baseshape");
+    MarkExactStackRootsForType<types::TypeObject *, MarkTypeObjectRoot>(trc, "exact-typeobject");
+    MarkExactStackRootsForType<JSString *, MarkStringRoot>(trc, "exact-string");
+    MarkExactStackRootsForType<jit::JitCode *, MarkJitCodeRoot>(trc, "exact-jitcode");
+    MarkExactStackRootsForType<JSScript *, MarkScriptRoot>(trc, "exact-script");
+    MarkExactStackRootsForType<LazyScript *, MarkLazyScriptRoot>(trc, "exact-lazy-script");
+    MarkExactStackRootsForType<jsid, MarkIdRoot>(trc, "exact-id");
+    MarkExactStackRootsForType<Value, MarkValueRoot>(trc, "exact-value");
+    MarkExactStackRootsForType<types::Type, MarkTypeRoot>(trc, "exact-type");
+    MarkExactStackRootsForType<Bindings, MarkBindingsRoot>(trc);
+    MarkExactStackRootsForType<JSPropertyDescriptor, MarkPropertyDescriptorRoot>(trc);
 }
 #endif /* JSGC_USE_EXACT_ROOTING */
 
 enum ConservativeGCTest
 {
     CGCT_VALID,
     CGCT_LOWBITSET, /* excluded because one of the low bits was set */
     CGCT_NOTARENA,  /* not within arena range in a chunk */
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -586,16 +586,53 @@ class JS_PUBLIC_API(CustomAutoRooter) : 
   protected:
     /* Supplied by derived class to trace roots. */
     virtual void trace(JSTracer *trc) = 0;
 
   private:
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
+/*
+ * RootedGeneric<T> allows a class to instantiate its own Rooted type by
+ * including the method:
+ *
+ *    void trace(JSTracer *trc);
+ *
+ * The trace() method must trace all of the class's fields.
+ */
+template <class T>
+class RootedGeneric : private CustomAutoRooter
+{
+  public:
+    template <typename CX>
+    explicit RootedGeneric(CX *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : CustomAutoRooter(cx)
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+    }
+
+    template <typename CX>
+    explicit RootedGeneric(CX *cx, const T& initial MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : CustomAutoRooter(cx), value(initial)
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+    }
+
+    operator const T&() const { return value; }
+    T operator->() const { return value; }
+
+  private:
+    virtual void trace(JSTracer *trc) { value->trace(trc); }
+
+    T value;
+
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
 /* A handle to an array of rooted values. */
 class HandleValueArray
 {
     const size_t length_;
     const Value * const elements_;
 
     HandleValueArray(size_t len, const Value *elements) : length_(len), elements_(elements) {}
 
@@ -2873,16 +2910,18 @@ struct JSPropertyDescriptor {
     JSStrictPropertyOp setter;
     JS::Value          value;
 
     JSPropertyDescriptor()
       : obj(nullptr), attrs(0), getter(nullptr), setter(nullptr), value(JSVAL_VOID)
     {}
 
     void trace(JSTracer *trc);
+
+    static js::ThingRootKind rootKind() { return js::THING_ROOT_PROPERTY_DESCRIPTOR; }
 };
 
 namespace JS {
 
 template <typename Outer>
 class PropertyDescriptorOperations
 {
     const JSPropertyDescriptor * desc() const { return static_cast<const Outer*>(this)->extract(); }
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -226,20 +226,17 @@ js::DestroyContext(JSContext *cx, Destro
     JSRuntime *rt = cx->runtime();
     JS_AbortIfWrongThread(rt);
 
 #ifdef JS_THREADSAFE
     if (cx->outstandingRequests != 0)
         MOZ_CRASH();
 #endif
 
-#if defined(JSGC_USE_EXACT_ROOTING) && defined(DEBUG)
-    for (int i = 0; i < THING_ROOT_LIMIT; ++i)
-        JS_ASSERT(cx->thingGCRooters[i] == nullptr);
-#endif
+    cx->checkNoGCRooters();
 
     if (mode != DCM_NEW_FAILED) {
         if (JSContextCallback cxCallback = rt->cxCallback) {
             /*
              * JSCONTEXT_DESTROY callback is not allowed to fail and must
              * return true.
              */
             JS_ALWAYS_TRUE(cxCallback(cx, JSCONTEXT_DESTROY,
@@ -260,16 +257,24 @@ js::DestroyContext(JSContext *cx, Destro
     if (mode == DCM_FORCE_GC) {
         JS_ASSERT(!rt->isHeapBusy());
         JS::PrepareForFullGC(rt);
         GC(rt, GC_NORMAL, JS::gcreason::DESTROY_CONTEXT);
     }
     js_delete_poison(cx);
 }
 
+void
+ContextFriendFields::checkNoGCRooters() {
+#if defined(JSGC_USE_EXACT_ROOTING) && defined(DEBUG)
+    for (int i = 0; i < THING_ROOT_LIMIT; ++i)
+        JS_ASSERT(thingGCRooters[i] == nullptr);
+#endif
+}
+
 bool
 AutoResolving::alreadyStartedSlow() const
 {
     JS_ASSERT(link);
     AutoResolving *cursor = link;
     do {
         JS_ASSERT(this != cursor);
         if (object.get() == cursor->object && id.get() == cursor->id && kind == cursor->kind)
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -304,16 +304,18 @@ class Type
     static inline Type PrimitiveType(JSValueType type) {
         JS_ASSERT(type < JSVAL_TYPE_UNKNOWN);
         return Type(type);
     }
 
     static inline Type ObjectType(JSObject *obj);
     static inline Type ObjectType(TypeObject *obj);
     static inline Type ObjectType(TypeObjectKey *obj);
+
+    static js::ThingRootKind rootKind() { return js::THING_ROOT_TYPE; }
 };
 
 /* Get the type of a jsval, or zero for an unknown special value. */
 inline Type GetValueType(const Value &val);
 
 /*
  * Get the type of a possibly optimized out value. This generally only
  * happens on unconditional type monitors on bailing out of Ion, such as
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -274,17 +274,16 @@ enum ThingRootKind
     THING_ROOT_JIT_CODE,
     THING_ROOT_SCRIPT,
     THING_ROOT_LAZY_SCRIPT,
     THING_ROOT_ID,
     THING_ROOT_VALUE,
     THING_ROOT_TYPE,
     THING_ROOT_BINDINGS,
     THING_ROOT_PROPERTY_DESCRIPTOR,
-    THING_ROOT_CUSTOM,
     THING_ROOT_LIMIT
 };
 
 /*
  * This list enumerates the different types of conceptual stacks we have in
  * SpiderMonkey. In reality, they all share the C stack, but we allow different
  * stack limits depending on the type of code running.
  */
@@ -342,29 +341,41 @@ struct ContextFriendFields
         return reinterpret_cast<const ContextFriendFields *>(cx);
     }
 
     static ContextFriendFields *get(JSContext *cx) {
         return reinterpret_cast<ContextFriendFields *>(cx);
     }
 
 #ifdef JSGC_TRACK_EXACT_ROOTS
+  private:
     /*
      * Stack allocated GC roots for stack GC heap pointers, which may be
      * overwritten if moved during a GC.
      */
     JS::Rooted<void*> *thingGCRooters[THING_ROOT_LIMIT];
+
+  public:
+    template <class T>
+    inline JS::Rooted<T> *gcRooters() {
+        js::ThingRootKind kind = RootKind<T>::rootKind();
+        return reinterpret_cast<JS::Rooted<T> *>(thingGCRooters[kind]);
+    }
+
 #endif
 
+    void checkNoGCRooters();
+
     /* Stack of thread-stack-allocated GC roots. */
     JS::AutoGCRooter   *autoGCRooters;
 
     friend JSRuntime *GetRuntime(const JSContext *cx);
     friend JSCompartment *GetContextCompartment(const JSContext *cx);
     friend JS::Zone *GetContextZone(const JSContext *cx);
+    template <typename T> friend class JS::Rooted;
 };
 
 /*
  * Inlinable accessors for JSContext.
  *
  * - These must not be available on the more restricted superclasses of
  *   JSContext, so we can't simply define them on ContextFriendFields.
  *
@@ -409,21 +420,29 @@ struct PerThreadDataFriendFields
         } mainThread;
     };
 
   public:
 
     PerThreadDataFriendFields();
 
 #ifdef JSGC_TRACK_EXACT_ROOTS
+  private:
     /*
      * Stack allocated GC roots for stack GC heap pointers, which may be
      * overwritten if moved during a GC.
      */
     JS::Rooted<void*> *thingGCRooters[THING_ROOT_LIMIT];
+
+  public:
+    template <class T>
+    inline JS::Rooted<T> *gcRooters() {
+        js::ThingRootKind kind = RootKind<T>::rootKind();
+        return reinterpret_cast<JS::Rooted<T> *>(thingGCRooters[kind]);
+    }
 #endif
 
     /* Limit pointer for checking native stack consumption. */
     uintptr_t nativeStackLimit[StackKindCount];
 
     static const size_t RuntimeMainThreadOffset = offsetof(RuntimeDummy, mainThread);
 
     static inline PerThreadDataFriendFields *get(js::PerThreadData *pt) {
@@ -438,13 +457,15 @@ struct PerThreadDataFriendFields
     }
 
     static inline const PerThreadDataFriendFields *getMainThread(const JSRuntime *rt) {
         // mainThread must always appear directly after |JS::shadow::Runtime|.
         // Tested by a JS_STATIC_ASSERT in |jsfriendapi.cpp|
         return reinterpret_cast<const PerThreadDataFriendFields *>(
             reinterpret_cast<const char*>(rt) + RuntimeMainThreadOffset);
     }
+
+    template <typename T> friend class JS::Rooted;
 };
 
 } /* namespace js */
 
 #endif /* jspubtd_h */
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -261,16 +261,17 @@ class Bindings
     /* Return whether this scope has any aliased bindings. */
     bool hasAnyAliasedBindings() const {
         if (!callObjShape_)
             return false;
 
         return !callObjShape_->isEmptyShape();
     }
 
+    static js::ThingRootKind rootKind() { return js::THING_ROOT_BINDINGS; }
     void trace(JSTracer *trc);
 };
 
 template <>
 struct GCMethods<Bindings> {
     static Bindings initial();
     static ThingRootKind kind() { return THING_ROOT_BINDINGS; }
     static bool poisoned(const Bindings &bindings) {
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -848,17 +848,16 @@ struct StackBaseShape
         this->rawGetter = rawGetter;
         this->rawSetter = rawSetter;
     }
 
     static inline HashNumber hash(const StackBaseShape *lookup);
     static inline bool match(UnownedBaseShape *key, const StackBaseShape *lookup);
 
     // For RootedGeneric<StackBaseShape*>
-    static inline js::ThingRootKind rootKind() { return js::THING_ROOT_CUSTOM; }
     void trace(JSTracer *trc);
 };
 
 inline
 BaseShape::BaseShape(const StackBaseShape &base)
 {
     mozilla::PodZero(this);
     this->clasp_ = base.clasp;
@@ -1500,17 +1499,16 @@ struct StackShape
         /* Accumulate from least to most random so the low bits are most random. */
         hash = mozilla::RotateLeft(hash, 4) ^ attrs;
         hash = mozilla::RotateLeft(hash, 4) ^ slot_;
         hash = mozilla::RotateLeft(hash, 4) ^ JSID_BITS(propid);
         return hash;
     }
 
     // For RootedGeneric<StackShape*>
-    static inline js::ThingRootKind rootKind() { return js::THING_ROOT_CUSTOM; }
     void trace(JSTracer *trc);
 };
 
 } /* namespace js */
 
 /* js::Shape pointer tag bit indicating a collision. */
 #define SHAPE_COLLISION                 (uintptr_t(1))
 #define SHAPE_REMOVED                   ((js::Shape *) SHAPE_COLLISION)