Bug 706885 - Rework Rooting APIs to preserve all available type information; r=billm
authorTerrence Cole <terrence@mozilla.com>
Mon, 28 Jan 2013 18:25:23 -0800
changeset 130131 c6e0a815401ac48b9d22cf58a5ad586f4372faaa
parent 130130 2313ce68cff0a616d76029124a19846abc55209e
child 130132 abba5495035465c99f0cdb2518124238e6415f15
push id2323
push userbbajaj@mozilla.com
push dateMon, 01 Apr 2013 19:47:02 +0000
treeherdermozilla-beta@7712be144d91 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm
bugs706885
milestone21.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 706885 - Rework Rooting APIs to preserve all available type information; r=billm
dom/workers/EventListenerManager.cpp
js/public/GCAPI.h
js/src/gc/RootMarking.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsdbgapi.cpp
js/src/jsfriendapi.cpp
js/src/jsgc.cpp
js/src/jsgc.h
js/xpconnect/public/nsAutoJSValHolder.h
js/xpconnect/src/XPCWrappedNative.cpp
js/xpconnect/src/xpcprivate.h
--- a/dom/workers/EventListenerManager.cpp
+++ b/dom/workers/EventListenerManager.cpp
@@ -74,17 +74,17 @@ struct ListenerData : PRCList
     listenerData->mWantsUntrusted = aWantsUntrusted;
     return listenerData;
   }
 
   static void
   Remove(JSContext* aCx, ListenerData* aListenerData)
   {
     if (JS::IsIncrementalBarrierNeeded(aCx)) {
-      JS:: IncrementalReferenceBarrier(aListenerData->mListener);
+      JS:: IncrementalObjectBarrier(aListenerData->mListener);
   }
 
     PR_REMOVE_LINK(aListenerData);
     JS_free(aCx, aListenerData);
   }
 };
 
 inline void
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -154,22 +154,25 @@ DisableIncrementalGC(JSRuntime *rt);
 
 extern JS_FRIEND_API(bool)
 IsIncrementalBarrierNeeded(JSRuntime *rt);
 
 extern JS_FRIEND_API(bool)
 IsIncrementalBarrierNeeded(JSContext *cx);
 
 extern JS_FRIEND_API(void)
-IncrementalReferenceBarrier(void *ptr);
+IncrementalReferenceBarrier(void *ptr, JSGCTraceKind kind);
 
 extern JS_FRIEND_API(void)
 IncrementalValueBarrier(const Value &v);
 
 extern JS_FRIEND_API(void)
+IncrementalObjectBarrier(JSObject *obj);
+
+extern JS_FRIEND_API(void)
 PokeGC(JSRuntime *rt);
 
 /* Was the most recent GC run incrementally? */
 extern JS_FRIEND_API(bool)
 WasIncrementalGC(JSRuntime *rt);
 
 class ObjectPtr
 {
@@ -180,30 +183,30 @@ class ObjectPtr
 
     ObjectPtr(JSObject *obj) : value(obj) {}
 
     /* Always call finalize before the destructor. */
     ~ObjectPtr() { JS_ASSERT(!value); }
 
     void finalize(JSRuntime *rt) {
         if (IsIncrementalBarrierNeeded(rt))
-            IncrementalReferenceBarrier(value);
+            IncrementalObjectBarrier(value);
         value = NULL;
     }
 
     void init(JSObject *obj) { value = obj; }
 
     JSObject *get() const { return value; }
 
     void writeBarrierPre(JSRuntime *rt) {
-        IncrementalReferenceBarrier(value);
+        IncrementalObjectBarrier(value);
     }
 
     ObjectPtr &operator=(JSObject *obj) {
-        IncrementalReferenceBarrier(value);
+        IncrementalObjectBarrier(value);
         value = obj;
         return *this;
     }
 
     JSObject &operator*() const { return *value; }
     JSObject *operator->() const { return value; }
     operator JSObject *() const { return value; }
 };
@@ -224,17 +227,17 @@ UnmarkGrayGCThingRecursively(void *thing
 static JS_ALWAYS_INLINE void
 ExposeGCThingToActiveJS(void *thing, JSGCTraceKind kind)
 {
     JS_ASSERT(kind != JSTRACE_SHAPE);
 
     if (GCThingIsMarkedGray(thing))
         UnmarkGrayGCThingRecursively(thing, kind);
     else if (IsIncrementalBarrierNeededOnGCThing(thing, kind))
-        IncrementalReferenceBarrier(thing);
+        IncrementalReferenceBarrier(thing, kind);
 }
 
 static JS_ALWAYS_INLINE void
 ExposeValueToActiveJS(const Value &v)
 {
     if (v.isMarkable())
         ExposeGCThingToActiveJS(v.toGCThing(), v.gcKind());
 }
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -684,18 +684,22 @@ js::gc::MarkRuntime(JSTracer *trc, bool 
         MarkConservativeStackRoots(trc, useSavedRoots);
 #endif
         rt->markSelfHostingGlobal(trc);
     }
 
     for (RootRange r = rt->gcRootsHash.all(); !r.empty(); r.popFront()) {
         const RootEntry &entry = r.front();
         const char *name = entry.value.name ? entry.value.name : "root";
-        if (entry.value.type == JS_GC_ROOT_GCTHING_PTR)
-            MarkGCThingRoot(trc, reinterpret_cast<void **>(entry.key), name);
+        if (entry.value.type == JS_GC_ROOT_STRING_PTR)
+            MarkStringRoot(trc, reinterpret_cast<JSString **>(entry.key), name);
+        else if (entry.value.type == JS_GC_ROOT_OBJECT_PTR)
+            MarkObjectRoot(trc, reinterpret_cast<JSObject **>(entry.key), name);
+        else if (entry.value.type == JS_GC_ROOT_SCRIPT_PTR)
+            MarkScriptRoot(trc, reinterpret_cast<JSScript **>(entry.key), name);
         else
             MarkValueRoot(trc, reinterpret_cast<Value *>(entry.key), name);
     }
 
     for (GCLocks::Range r = rt->gcLocksHash.all(); !r.empty(); r.popFront()) {
         const GCLocks::Entry &entry = r.front();
         JS_ASSERT(entry.value >= 1);
         JS_SET_TRACING_LOCATION(trc, (void *)&entry.key);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2317,81 +2317,71 @@ JS_strdup(JSContext *cx, const char *s)
 
 #undef JS_AddRoot
 
 JS_PUBLIC_API(JSBool)
 JS_AddValueRoot(JSContext *cx, jsval *vp)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    return js_AddRoot(cx, vp, NULL);
+    return AddValueRoot(cx, vp, NULL);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_AddStringRoot(JSContext *cx, JSString **rp)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    return js_AddGCThingRoot(cx, (void **)rp, NULL);
+    return AddStringRoot(cx, rp, NULL);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_AddObjectRoot(JSContext *cx, JSObject **rp)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    return js_AddGCThingRoot(cx, (void **)rp, NULL);
-}
-
-JS_PUBLIC_API(JSBool)
-JS_AddGCThingRoot(JSContext *cx, void **rp)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    return js_AddGCThingRoot(cx, (void **)rp, NULL);
+    return AddObjectRoot(cx, rp, NULL);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_AddNamedValueRoot(JSContext *cx, jsval *vp, const char *name)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    return js_AddRoot(cx, vp, name);
+    return AddValueRoot(cx, vp, name);
+}
+
+JS_PUBLIC_API(JSBool)
+JS_AddNamedValueRootRT(JSRuntime *rt, jsval *vp, const char *name)
+{
+    return AddValueRootRT(rt, vp, name);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_AddNamedStringRoot(JSContext *cx, JSString **rp, const char *name)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    return js_AddGCThingRoot(cx, (void **)rp, name);
+    return AddStringRoot(cx, rp, name);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_AddNamedObjectRoot(JSContext *cx, JSObject **rp, const char *name)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    return js_AddGCThingRoot(cx, (void **)rp, name);
+    return AddObjectRoot(cx, rp, name);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_AddNamedScriptRoot(JSContext *cx, JSScript **rp, const char *name)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    return js_AddGCThingRoot(cx, (void **)rp, name);
-}
-
-JS_PUBLIC_API(JSBool)
-JS_AddNamedGCThingRoot(JSContext *cx, void **rp, const char *name)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    return js_AddGCThingRoot(cx, (void **)rp, name);
+    return AddScriptRoot(cx, rp, name);
 }
 
 /* We allow unrooting from finalizers within the GC */
 
 JS_PUBLIC_API(void)
 JS_RemoveValueRoot(JSContext *cx, jsval *vp)
 {
     CHECK_REQUEST(cx);
@@ -2415,23 +2405,16 @@ JS_RemoveObjectRoot(JSContext *cx, JSObj
 JS_PUBLIC_API(void)
 JS_RemoveScriptRoot(JSContext *cx, JSScript **rp)
 {
     CHECK_REQUEST(cx);
     js_RemoveRoot(cx->runtime, (void *)rp);
 }
 
 JS_PUBLIC_API(void)
-JS_RemoveGCThingRoot(JSContext *cx, void **rp)
-{
-    CHECK_REQUEST(cx);
-    js_RemoveRoot(cx->runtime, (void *)rp);
-}
-
-JS_PUBLIC_API(void)
 JS_RemoveValueRootRT(JSRuntime *rt, jsval *vp)
 {
     js_RemoveRoot(rt, (void *)vp);
 }
 
 JS_PUBLIC_API(void)
 JS_RemoveStringRootRT(JSRuntime *rt, JSString **rp)
 {
@@ -2451,47 +2434,25 @@ JS_RemoveScriptRootRT(JSRuntime *rt, JSS
 }
 
 JS_NEVER_INLINE JS_PUBLIC_API(void)
 JS_AnchorPtr(void *p)
 {
 }
 
 JS_PUBLIC_API(JSBool)
-JS_LockGCThing(JSContext *cx, void *thing)
-{
-    JSBool ok;
-
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    ok = js_LockGCThingRT(cx->runtime, thing);
-    if (!ok)
-        JS_ReportOutOfMemory(cx);
-    return ok;
-}
-
-JS_PUBLIC_API(JSBool)
-JS_LockGCThingRT(JSRuntime *rt, void *thing)
-{
-    return js_LockGCThingRT(rt, thing);
-}
-
-JS_PUBLIC_API(JSBool)
-JS_UnlockGCThing(JSContext *cx, void *thing)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    js_UnlockGCThingRT(cx->runtime, thing);
-    return true;
-}
-
-JS_PUBLIC_API(JSBool)
-JS_UnlockGCThingRT(JSRuntime *rt, void *thing)
-{
-    js_UnlockGCThingRT(rt, thing);
+JS_LockGCThingRT(JSRuntime *rt, void *gcthing)
+{
+    return js_LockThing(rt, gcthing);
+}
+
+JS_PUBLIC_API(JSBool)
+JS_UnlockGCThingRT(JSRuntime *rt, void *gcthing)
+{
+    js_UnlockThing(rt, gcthing);
     return true;
 }
 
 JS_PUBLIC_API(void)
 JS_SetExtraGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data)
 {
     AssertHeapIsIdle(rt);
     rt->gcBlackRootsTraceOp = traceOp;
@@ -6917,17 +6878,17 @@ JS_SaveExceptionState(JSContext *cx)
     JSExceptionState *state;
 
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     state = cx->pod_malloc<JSExceptionState>();
     if (state) {
         state->throwing = JS_GetPendingException(cx, &state->exception);
         if (state->throwing && JSVAL_IS_GCTHING(state->exception))
-            js_AddRoot(cx, &state->exception, "JSExceptionState.exception");
+            AddValueRoot(cx, &state->exception, "JSExceptionState.exception");
     }
     return state;
 }
 
 JS_PUBLIC_API(void)
 JS_RestoreExceptionState(JSContext *cx, JSExceptionState *state)
 {
     AssertHeapIsIdle(cx);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3599,104 +3599,80 @@ extern JS_PUBLIC_API(JSBool)
 JS_AddValueRoot(JSContext *cx, jsval *vp);
 
 extern JS_PUBLIC_API(JSBool)
 JS_AddStringRoot(JSContext *cx, JSString **rp);
 
 extern JS_PUBLIC_API(JSBool)
 JS_AddObjectRoot(JSContext *cx, JSObject **rp);
 
-extern JS_PUBLIC_API(JSBool)
-JS_AddGCThingRoot(JSContext *cx, void **rp);
-
 #ifdef NAME_ALL_GC_ROOTS
 #define JS_DEFINE_TO_TOKEN(def) #def
 #define JS_DEFINE_TO_STRING(def) JS_DEFINE_TO_TOKEN(def)
 #define JS_AddValueRoot(cx,vp) JS_AddNamedValueRoot((cx), (vp), (__FILE__ ":" JS_TOKEN_TO_STRING(__LINE__))
 #define JS_AddStringRoot(cx,rp) JS_AddNamedStringRoot((cx), (rp), (__FILE__ ":" JS_TOKEN_TO_STRING(__LINE__))
 #define JS_AddObjectRoot(cx,rp) JS_AddNamedObjectRoot((cx), (rp), (__FILE__ ":" JS_TOKEN_TO_STRING(__LINE__))
-#define JS_AddGCThingRoot(cx,rp) JS_AddNamedGCThingRoot((cx), (rp), (__FILE__ ":" JS_TOKEN_TO_STRING(__LINE__))
 #endif
 
 extern JS_PUBLIC_API(JSBool)
 JS_AddNamedValueRoot(JSContext *cx, jsval *vp, const char *name);
 
 extern JS_PUBLIC_API(JSBool)
+JS_AddNamedValueRootRT(JSRuntime *rt, jsval *vp, const char *name);
+
+extern JS_PUBLIC_API(JSBool)
 JS_AddNamedStringRoot(JSContext *cx, JSString **rp, const char *name);
 
 extern JS_PUBLIC_API(JSBool)
 JS_AddNamedObjectRoot(JSContext *cx, JSObject **rp, const char *name);
 
 extern JS_PUBLIC_API(JSBool)
 JS_AddNamedScriptRoot(JSContext *cx, JSScript **rp, const char *name);
 
-extern JS_PUBLIC_API(JSBool)
-JS_AddNamedGCThingRoot(JSContext *cx, void **rp, const char *name);
-
 extern JS_PUBLIC_API(void)
 JS_RemoveValueRoot(JSContext *cx, jsval *vp);
 
 extern JS_PUBLIC_API(void)
 JS_RemoveStringRoot(JSContext *cx, JSString **rp);
 
 extern JS_PUBLIC_API(void)
 JS_RemoveObjectRoot(JSContext *cx, JSObject **rp);
 
 extern JS_PUBLIC_API(void)
 JS_RemoveScriptRoot(JSContext *cx, JSScript **rp);
 
 extern JS_PUBLIC_API(void)
-JS_RemoveGCThingRoot(JSContext *cx, void **rp);
-
-extern JS_PUBLIC_API(void)
 JS_RemoveValueRootRT(JSRuntime *rt, jsval *vp);
 
 extern JS_PUBLIC_API(void)
 JS_RemoveStringRootRT(JSRuntime *rt, JSString **rp);
 
 extern JS_PUBLIC_API(void)
 JS_RemoveObjectRootRT(JSRuntime *rt, JSObject **rp);
 
 extern JS_PUBLIC_API(void)
 JS_RemoveScriptRootRT(JSRuntime *rt, JSScript **rp);
 
 /* TODO: remove these APIs */
 
-extern JS_FRIEND_API(JSBool)
-js_AddRootRT(JSRuntime *rt, jsval *vp, const char *name);
-
-extern JS_FRIEND_API(JSBool)
-js_AddGCThingRootRT(JSRuntime *rt, void **rp, const char *name);
-
 extern JS_FRIEND_API(void)
 js_RemoveRoot(JSRuntime *rt, void *rp);
 
 /*
  * C-compatible version of the Anchor class. It should be called after the last
  * use of the variable it protects.
  */
 extern JS_NEVER_INLINE JS_PUBLIC_API(void)
 JS_AnchorPtr(void *p);
 
-typedef enum JSGCRootType {
-    JS_GC_ROOT_VALUE_PTR,
-    JS_GC_ROOT_GCTHING_PTR
-} JSGCRootType;
-
-extern JS_PUBLIC_API(JSBool)
-JS_LockGCThing(JSContext *cx, void *thing);
-
-extern JS_PUBLIC_API(JSBool)
-JS_LockGCThingRT(JSRuntime *rt, void *thing);
-
-extern JS_PUBLIC_API(JSBool)
-JS_UnlockGCThing(JSContext *cx, void *thing);
-
-extern JS_PUBLIC_API(JSBool)
-JS_UnlockGCThingRT(JSRuntime *rt, void *thing);
+extern JS_PUBLIC_API(JSBool)
+JS_LockGCThingRT(JSRuntime *rt, void *gcthing);
+
+extern JS_PUBLIC_API(JSBool)
+JS_UnlockGCThingRT(JSRuntime *rt, void *gcthing);
 
 /*
  * Register externally maintained GC roots.
  *
  * traceOp: the trace operation. For each root the implementation should call
  *          JS_CallTracer whenever the root contains a traceable thing.
  * data:    the data argument to pass to each invocation of traceOp.
  */
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -689,20 +689,20 @@ JS_GetPropertyDescArray(JSContext *cx, J
 
         pd = cx->pod_calloc<JSPropertyDesc>(props.length());
         if (!pd)
             return false;
 
         for (i = 0; i < props.length(); ++i) {
             pd[i].id = JSVAL_NULL;
             pd[i].value = JSVAL_NULL;
-            if (!js_AddRoot(cx, &pd[i].id, NULL))
+            if (!AddValueRoot(cx, &pd[i].id, NULL))
                 goto bad;
             pd[i].id = IdToValue(props[i]);
-            if (!js_AddRoot(cx, &pd[i].value, NULL))
+            if (!AddValueRoot(cx, &pd[i].value, NULL))
                 goto bad;
             if (!Proxy::get(cx, obj, obj, props.handleAt(i), MutableHandleValue::fromMarkedLocation(&pd[i].value)))
                 goto bad;
         }
 
         pda->length = props.length();
         pda->array = pd;
         return true;
@@ -732,24 +732,24 @@ JS_GetPropertyDescArray(JSContext *cx, J
     {
         Shape::Range r(obj->lastProperty()->all());
         Shape::Range::AutoRooter rooter(cx, &r);
         RootedShape shape(cx);
         for (; !r.empty(); r.popFront()) {
             pd[i].id = JSVAL_NULL;
             pd[i].value = JSVAL_NULL;
             pd[i].alias = JSVAL_NULL;
-            if (!js_AddRoot(cx, &pd[i].id, NULL))
+            if (!AddValueRoot(cx, &pd[i].id, NULL))
                 goto bad;
-            if (!js_AddRoot(cx, &pd[i].value, NULL))
+            if (!AddValueRoot(cx, &pd[i].value, NULL))
                 goto bad;
             shape = const_cast<Shape *>(&r.front());
             if (!GetPropertyDesc(cx, obj, shape, &pd[i]))
                 goto bad;
-            if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, &pd[i].alias, NULL))
+            if ((pd[i].flags & JSPD_ALIAS) && !AddValueRoot(cx, &pd[i].alias, NULL))
                 goto bad;
             if (++i == obj->propertyCount())
                 break;
         }
     }
 
     pda->length = i;
     pda->array = pd;
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -6,16 +6,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/GuardObjects.h"
 #include "mozilla/StandardInteger.h"
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jsfriendapi.h"
+#include "jsgc.h"
 #include "jswrapper.h"
 #include "jsweakmap.h"
 #include "jswatchpoint.h"
 
 #include "builtin/TestingFunctions.h"
 
 #include "jsobjinlines.h"
 
@@ -866,37 +867,51 @@ JS::IsIncrementalBarrierNeeded(JSRuntime
 
 JS_FRIEND_API(bool)
 JS::IsIncrementalBarrierNeeded(JSContext *cx)
 {
     return IsIncrementalBarrierNeeded(cx->runtime);
 }
 
 JS_FRIEND_API(void)
-JS::IncrementalReferenceBarrier(void *ptr)
+JS::IncrementalObjectBarrier(JSObject *obj)
+{
+    if (!obj)
+        return;
+
+    JS_ASSERT(!obj->compartment()->rt->isHeapBusy());
+
+    AutoMarkInDeadCompartment amn(obj->compartment());
+
+    JSObject::writeBarrierPre(obj);
+}
+
+JS_FRIEND_API(void)
+JS::IncrementalReferenceBarrier(void *ptr, JSGCTraceKind kind)
 {
     if (!ptr)
         return;
 
     gc::Cell *cell = static_cast<gc::Cell *>(ptr);
-    JS_ASSERT(!cell->compartment()->rt->isHeapBusy());
+    JSCompartment *comp = cell->compartment();
 
-    AutoMarkInDeadCompartment amn(cell->compartment());
+    JS_ASSERT(!comp->rt->isHeapBusy());
 
-    uint32_t kind = gc::GetGCThingTraceKind(ptr);
+    AutoMarkInDeadCompartment amn(comp);
+
     if (kind == JSTRACE_OBJECT)
-        JSObject::writeBarrierPre(reinterpret_cast<RawObject>(ptr));
+        JSObject::writeBarrierPre(static_cast<JSObject*>(cell));
     else if (kind == JSTRACE_STRING)
-        JSString::writeBarrierPre(reinterpret_cast<RawString>(ptr));
+        JSString::writeBarrierPre(static_cast<JSString*>(cell));
     else if (kind == JSTRACE_SCRIPT)
-        JSScript::writeBarrierPre(reinterpret_cast<RawScript>(ptr));
+        JSScript::writeBarrierPre(static_cast<JSScript*>(cell));
     else if (kind == JSTRACE_SHAPE)
-        Shape::writeBarrierPre(reinterpret_cast<RawShape>(ptr));
+        Shape::writeBarrierPre(static_cast<Shape*>(cell));
     else if (kind == JSTRACE_BASE_SHAPE)
-        BaseShape::writeBarrierPre(reinterpret_cast<RawBaseShape>(ptr));
+        BaseShape::writeBarrierPre(static_cast<BaseShape*>(cell));
     else if (kind == JSTRACE_TYPE_OBJECT)
         types::TypeObject::writeBarrierPre((types::TypeObject *) ptr);
     else
         JS_NOT_REACHED("invalid trace kind");
 }
 
 JS_FRIEND_API(void)
 JS::IncrementalValueBarrier(const Value &v)
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1025,64 +1025,74 @@ js_FinishGC(JSRuntime *rt)
     rt->gcChunkSet.clear();
 
     rt->gcChunkPool.expireAndFree(rt, true);
 
     rt->gcRootsHash.clear();
     rt->gcLocksHash.clear();
 }
 
-JSBool
-js_AddRoot(JSContext *cx, Value *vp, const char *name)
-{
-    JSBool ok = js_AddRootRT(cx->runtime, vp, name);
-    if (!ok)
-        JS_ReportOutOfMemory(cx);
-    return ok;
-}
-
-JSBool
-js_AddGCThingRoot(JSContext *cx, void **rp, const char *name)
-{
-    JSBool ok = js_AddGCThingRootRT(cx->runtime, rp, name);
-    if (!ok)
-        JS_ReportOutOfMemory(cx);
-    return ok;
-}
-
-JS_FRIEND_API(JSBool)
-js_AddRootRT(JSRuntime *rt, jsval *vp, const char *name)
+template <typename T> struct BarrierOwner {};
+template <typename T> struct BarrierOwner<T *> { typedef T result; };
+template <> struct BarrierOwner<Value> { typedef HeapValue result; };
+
+template <typename T>
+static bool
+AddRoot(JSRuntime *rt, T *rp, const char *name, JSGCRootType rootType)
 {
     /*
      * Sometimes Firefox will hold weak references to objects and then convert
      * them to strong references by calling AddRoot (e.g., via PreserveWrapper,
      * or ModifyBusyCount in workers). We need a read barrier to cover these
      * cases.
      */
     if (rt->gcIncrementalState != NO_INCREMENTAL)
-        IncrementalValueBarrier(*vp);
-
-    return !!rt->gcRootsHash.put((void *)vp,
-                                 RootInfo(name, JS_GC_ROOT_VALUE_PTR));
+        BarrierOwner<T>::result::writeBarrierPre(*rp);
+
+    return rt->gcRootsHash.put((void *)rp, RootInfo(name, rootType));
 }
 
-JS_FRIEND_API(JSBool)
-js_AddGCThingRootRT(JSRuntime *rt, void **rp, const char *name)
+template <typename T>
+static bool
+AddRoot(JSContext *cx, T *rp, const char *name, JSGCRootType rootType)
+{
+    bool ok = AddRoot(cx->runtime, rp, name, rootType);
+    if (!ok)
+        JS_ReportOutOfMemory(cx);
+    return ok;
+}
+
+JSBool
+js::AddValueRoot(JSContext *cx, Value *vp, const char *name)
 {
-    /*
-     * Sometimes Firefox will hold weak references to objects and then convert
-     * them to strong references by calling AddRoot (e.g., via PreserveWrapper,
-     * or ModifyBusyCount in workers). We need a read barrier to cover these
-     * cases.
-     */
-    if (rt->gcIncrementalState != NO_INCREMENTAL)
-        IncrementalReferenceBarrier(*rp);
-
-    return !!rt->gcRootsHash.put((void *)rp,
-                                 RootInfo(name, JS_GC_ROOT_GCTHING_PTR));
+    return AddRoot(cx, vp, name, JS_GC_ROOT_VALUE_PTR);
+}
+
+extern JSBool
+js::AddValueRootRT(JSRuntime *rt, js::Value *vp, const char *name)
+{
+    return AddRoot(rt, vp, name, JS_GC_ROOT_VALUE_PTR);
+}
+
+extern JSBool
+js::AddStringRoot(JSContext *cx, JSString **rp, const char *name)
+{
+    return AddRoot(cx, rp, name, JS_GC_ROOT_STRING_PTR);
+}
+
+extern JSBool
+js::AddObjectRoot(JSContext *cx, JSObject **rp, const char *name)
+{
+    return AddRoot(cx, rp, name, JS_GC_ROOT_OBJECT_PTR);
+}
+
+extern JSBool
+js::AddScriptRoot(JSContext *cx, JSScript **rp, const char *name)
+{
+    return AddRoot(cx, rp, name, JS_GC_ROOT_SCRIPT_PTR);
 }
 
 JS_FRIEND_API(void)
 js_RemoveRoot(JSRuntime *rt, void *rp)
 {
     rt->gcRootsHash.remove(rp);
     rt->gcPoke = true;
 }
@@ -1561,40 +1571,40 @@ ArenaLists::refillFreeList<CanGC>(JSCont
 
 JSGCTraceKind
 js_GetGCThingTraceKind(void *thing)
 {
     return GetGCThingTraceKind(thing);
 }
 
 JSBool
-js_LockGCThingRT(JSRuntime *rt, void *thing)
+js_LockThing(JSRuntime *rt, void *thing)
 {
     if (!thing)
         return true;
 
     /*
      * Sometimes Firefox will hold weak references to objects and then convert
      * them to strong references by calling AddRoot (e.g., via PreserveWrapper,
      * or ModifyBusyCount in workers). We need a read barrier to cover these
      * cases.
      */
     if (rt->gcIncrementalState != NO_INCREMENTAL)
-        IncrementalReferenceBarrier(thing);
+        IncrementalReferenceBarrier(thing, GetGCThingTraceKind(thing));
 
     if (GCLocks::Ptr p = rt->gcLocksHash.lookupWithDefault(thing, 0)) {
         p->value++;
         return true;
     }
 
     return false;
 }
 
 void
-js_UnlockGCThingRT(JSRuntime *rt, void *thing)
+js_UnlockThing(JSRuntime *rt, void *thing)
 {
     if (!thing)
         return;
 
     if (GCLocks::Ptr p = rt->gcLocksHash.lookup(thing)) {
         rt->gcPoke = true;
         if (--p->value == 0)
             rt->gcLocksHash.remove(p);
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -115,16 +115,17 @@ MapAllocToTraceKind(AllocKind kind)
         JSTRACE_STRING,     /* FINALIZE_STRING */
         JSTRACE_STRING,     /* FINALIZE_EXTERNAL_STRING */
         JSTRACE_IONCODE,    /* FINALIZE_IONCODE */
     };
     JS_STATIC_ASSERT(JS_ARRAY_LENGTH(map) == FINALIZE_LIMIT);
     return map[kind];
 }
 
+#ifdef JSGC_GENERATIONAL
 static inline bool
 IsNurseryAllocable(AllocKind kind)
 {
     JS_ASSERT(kind >= 0 && unsigned(kind) < FINALIZE_LIMIT);
     static const bool map[] = {
         false,     /* FINALIZE_OBJECT0 */
         true,      /* FINALIZE_OBJECT0_BACKGROUND */
         false,     /* FINALIZE_OBJECT2 */
@@ -147,16 +148,17 @@ IsNurseryAllocable(AllocKind kind)
         true,      /* FINALIZE_SHORT_STRING */
         true,      /* FINALIZE_STRING */
         false,     /* FINALIZE_EXTERNAL_STRING */
         false,     /* FINALIZE_IONCODE */
     };
     JS_STATIC_ASSERT(JS_ARRAY_LENGTH(map) == FINALIZE_LIMIT);
     return map[kind];
 }
+#endif
 
 static inline bool
 IsBackgroundFinalized(AllocKind kind)
 {
     JS_ASSERT(kind >= 0 && unsigned(kind) < FINALIZE_LIMIT);
     static const bool map[] = {
         false,     /* FINALIZE_OBJECT0 */
         true,      /* FINALIZE_OBJECT0_BACKGROUND */
@@ -482,53 +484,69 @@ struct GCPtrHasher
         return HashNumber(uintptr_t(key) >> JS_GCTHING_ZEROBITS);
     }
 
     static bool match(void *l, void *k) { return l == k; }
 };
 
 typedef HashMap<void *, uint32_t, GCPtrHasher, SystemAllocPolicy> GCLocks;
 
+typedef enum JSGCRootType {
+    JS_GC_ROOT_VALUE_PTR,
+    JS_GC_ROOT_STRING_PTR,
+    JS_GC_ROOT_OBJECT_PTR,
+    JS_GC_ROOT_SCRIPT_PTR
+} JSGCRootType;
+
 struct RootInfo {
     RootInfo() {}
     RootInfo(const char *name, JSGCRootType type) : name(name), type(type) {}
     const char *name;
     JSGCRootType type;
 };
 
 typedef js::HashMap<void *,
                     RootInfo,
                     js::DefaultHasher<void *>,
                     js::SystemAllocPolicy> RootedValueMap;
 
+extern JSBool
+AddValueRoot(JSContext *cx, js::Value *vp, const char *name);
+
+extern JSBool
+AddValueRootRT(JSRuntime *rt, js::Value *vp, const char *name);
+
+extern JSBool
+AddStringRoot(JSContext *cx, JSString **rp, const char *name);
+
+extern JSBool
+AddObjectRoot(JSContext *cx, JSObject **rp, const char *name);
+
+extern JSBool
+AddScriptRoot(JSContext *cx, JSScript **rp, const char *name);
+
 } /* namespace js */
 
 extern JSBool
 js_InitGC(JSRuntime *rt, uint32_t maxbytes);
 
 extern void
 js_FinishGC(JSRuntime *rt);
 
-extern JSBool
-js_AddRoot(JSContext *cx, js::Value *vp, const char *name);
-
-extern JSBool
-js_AddGCThingRoot(JSContext *cx, void **rp, const char *name);
-
 /* Table of pointers with count valid members. */
 typedef struct JSPtrTable {
     size_t      count;
     void        **array;
 } JSPtrTable;
 
 extern JSBool
-js_LockGCThingRT(JSRuntime *rt, void *thing);
+js_LockThing(JSRuntime *rt, void *thing);
 
 extern void
-js_UnlockGCThingRT(JSRuntime *rt, void *thing);
+js_UnlockThing(JSRuntime *rt, void *thing);
 
 namespace js {
 
 extern void
 MarkCompartmentActive(js::StackFrame *fp);
 
 extern void
 TraceRuntime(JSTracer *trc);
--- a/js/xpconnect/public/nsAutoJSValHolder.h
+++ b/js/xpconnect/public/nsAutoJSValHolder.h
@@ -64,32 +64,32 @@ public:
    */
   bool Hold(JSRuntime* aRt) {
     // Do we really care about different runtimes?
     if (mRt && aRt != mRt) {
       js_RemoveRoot(mRt, &mVal);
       mRt = nullptr;
     }
 
-    if (!mRt && js_AddRootRT(aRt, &mVal, "nsAutoJSValHolder")) {
+    if (!mRt && JS_AddNamedValueRootRT(aRt, &mVal, "nsAutoJSValHolder")) {
       mRt = aRt;
     }
 
     return !!mRt;
   }
 
   /**
    * Manually release, nullifying mVal, and mRt, but returning
    * the original jsval.
    */
   jsval Release() {
     jsval oldval = mVal;
 
     if (mRt) {
-      js_RemoveRoot(mRt, &mVal); // infallible
+      JS_RemoveValueRootRT(mRt, &mVal); // infallible
       mRt = nullptr;
     }
 
     mVal = JSVAL_NULL;
 
     return oldval;
   }
 
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -939,17 +939,17 @@ XPCWrappedNative::Destroy()
     /*
      * The only time GetRuntime() will be NULL is if Destroy is called a second
      * time on a wrapped native. Since we already unregistered the pointer the
      * first time, there's no need to unregister again. Unregistration is safe
      * the first time because mWrapperWord isn't used afterwards.
      */
     if (XPCJSRuntime *rt = GetRuntime()) {
         if (js::IsIncrementalBarrierNeeded(rt->GetJSRuntime()))
-            js::IncrementalReferenceBarrier(GetWrapperPreserveColor());
+            js::IncrementalObjectBarrier(GetWrapperPreserveColor());
         mWrapperWord = WRAPPER_WORD_POISON;
     } else {
         MOZ_ASSERT(mWrapperWord == WRAPPER_WORD_POISON);
     }
 
     mMaybeScope = nullptr;
 }
 
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -2905,17 +2905,17 @@ public:
             xpc_UnmarkGrayObject(wrapper);
             // Call this to unmark mFlatJSObject.
             GetFlatJSObject();
         }
         return wrapper;
     }
     void SetWrapper(JSObject *obj)
     {
-        js::IncrementalReferenceBarrier(GetWrapperPreserveColor());
+        js::IncrementalObjectBarrier(GetWrapperPreserveColor());
         intptr_t newval = intptr_t(obj) | (mWrapperWord & FLAG_MASK);
         mWrapperWord = newval;
     }
 
     // Returns the relevant same-compartment security if applicable, or
     // mFlatJSObject otherwise.
     //
     // This takes care of checking mWrapperWord to see if we already have such