Bug 892643: Implement PersistentRooted<T>, an unrestricted-lifetime rooting type. r=terrence, jcoppeard
authorJim Blandy <jimb@mozilla.com>
Mon, 04 Nov 2013 13:35:08 -0800
changeset 169029 48577a74a5a1581aea4197f093c834980e780e93
parent 169028 076fb6b1d2f9bab816964150cb12090ad9fbd76d
child 169030 bad6c063b01f64a18986a847d71346cacf51a788
push idunknown
push userunknown
push dateunknown
reviewersterrence, jcoppeard
bugs892643
milestone28.0a1
Bug 892643: Implement PersistentRooted<T>, an unrestricted-lifetime rooting type. r=terrence, jcoppeard
js/public/RootingAPI.h
js/public/TypeDecls.h
js/src/NamespaceImports.h
js/src/gc/GCInternals.h
js/src/gc/RootMarking.cpp
js/src/jsapi-tests/moz.build
js/src/jsapi-tests/testPersistentRooted.cpp
js/src/jsgc.cpp
js/src/jspubtd.h
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -3,16 +3,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef js_RootingAPI_h
 #define js_RootingAPI_h
 
 #include "mozilla/GuardObjects.h"
+#include "mozilla/LinkedList.h"
 #include "mozilla/NullPtr.h"
 #include "mozilla/TypeTraits.h"
 
 #include "jspubtd.h"
 
 #include "js/TypeDecls.h"
 #include "js/Utility.h"
 
@@ -138,16 +139,17 @@ namespace gc {
 struct Cell;
 } /* namespace gc */
 
 } /* namespace js */
 
 namespace JS {
 
 template <typename T> class Rooted;
+template <typename T> class PersistentRooted;
 
 /* This is exposing internal state of the GC for inlining purposes. */
 JS_FRIEND_API(bool) isGCEnabled();
 
 #if defined(DEBUG) && defined(JS_GC_ZEAL) && defined(JSGC_ROOT_ANALYSIS) && !defined(JS_THREADSAFE)
 extern void
 CheckStackRoots(JSContext *cx);
 #endif
@@ -435,16 +437,21 @@ class MOZ_NONHEAP_CLASS Handle : public 
      * Construct a handle from an explicitly rooted location. This is the
      * normal way to create a handle, and normally happens implicitly.
      */
     template <typename S>
     inline
     Handle(const Rooted<S> &root,
            typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0);
 
+    template <typename S>
+    inline
+    Handle(const PersistentRooted<S> &root,
+           typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0);
+
     /* Construct a read only handle from a mutable handle. */
     template <typename S>
     inline
     Handle(MutableHandle<S> &root,
            typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0);
 
     const T *address() const { return ptr; }
     const T& get() const { return *ptr; }
@@ -479,16 +486,17 @@ class MOZ_NONHEAP_CLASS Handle : public 
  * specialization, define a MutableHandleBase<T> specialization containing
  * them.
  */
 template <typename T>
 class MOZ_STACK_CLASS MutableHandle : public js::MutableHandleBase<T>
 {
   public:
     inline MutableHandle(Rooted<T> *root);
+    inline MutableHandle(PersistentRooted<T> *root);
     MutableHandle(int) MOZ_DELETE;
 #ifdef MOZ_HAVE_CXX11_NULLPTR
     MutableHandle(decltype(nullptr)) MOZ_DELETE;
 #endif
 
     void set(T v) {
         JS_ASSERT(!js::GCMethods<T>::poisoned(v));
         *ptr = v;
@@ -792,23 +800,16 @@ class MOZ_STACK_CLASS Rooted : public js
 };
 
 #if !(defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING))
 // Defined in vm/String.h.
 template <>
 class Rooted<JSStableString *>;
 #endif
 
-typedef Rooted<JSObject*>                   RootedObject;
-typedef Rooted<JSFunction*>                 RootedFunction;
-typedef Rooted<JSScript*>                   RootedScript;
-typedef Rooted<JSString*>                   RootedString;
-typedef Rooted<jsid>                        RootedId;
-typedef Rooted<JS::Value>                   RootedValue;
-
 } /* namespace JS */
 
 namespace js {
 
 /*
  * Mark a stack location as a root for the rooting analysis, without actually
  * rooting it in release builds. This should only be used for stack locations
  * of GC things that cannot be relocated by a garbage collection, and that
@@ -1021,31 +1022,156 @@ inline
 Handle<T>::Handle(const Rooted<S> &root,
                   typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy)
 {
     ptr = reinterpret_cast<const T *>(root.address());
 }
 
 template <typename T> template <typename S>
 inline
+Handle<T>::Handle(const PersistentRooted<S> &root,
+                  typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy)
+{
+    ptr = reinterpret_cast<const T *>(root.address());
+}
+
+template <typename T> template <typename S>
+inline
 Handle<T>::Handle(MutableHandle<S> &root,
                   typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy)
 {
     ptr = reinterpret_cast<const T *>(root.address());
 }
 
 template <typename T>
 inline
 MutableHandle<T>::MutableHandle(Rooted<T> *root)
 {
     static_assert(sizeof(MutableHandle<T>) == sizeof(T *),
                   "MutableHandle must be binary compatible with T*.");
     ptr = root->address();
 }
 
+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();
+}
+
+
+/*
+ * 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, so they require a full JSContext to be
+ * constructed, not one of its more restricted superclasses.
+ *
+ * Note that you must not use an PersistentRooted in an object owned by a JS
+ * object:
+ *
+ * Whenever one object whose lifetime is decided by the GC refers to another
+ * such object, that edge must be traced only if the owning JS object is traced.
+ * This applies not only to JS objects (which obviously are managed by the GC)
+ * but also to C++ objects owned by JS objects.
+ *
+ * If you put a PersistentRooted in such a C++ object, that is almost certainly
+ * a leak. When a GC begins, the referent of the PersistentRooted is treated as
+ * live, unconditionally (because a PersistentRooted is a *root*), even if the
+ * JS object that owns it is unreachable. If there is any path from that
+ * referent back to the JS object, then the C++ object containing the
+ * PersistentRooted will not be destructed, and the whole blob of objects will
+ * not be freed, even if there are no references to them from the outside.
+ *
+ * In the context of Firefox, this is a severe restriction: almost everything in
+ * Firefox is owned by some JS object or another, so using PersistentRooted in
+ * such objects would introduce leaks. For these kinds of edges, Heap<T> or
+ * TenuredHeap<T> would be better types. It's up to the implementor of the type
+ * containing Heap<T> or TenuredHeap<T> members to make sure their referents get
+ * marked when the object itself is marked.
+ */
+template<typename T>
+class PersistentRooted : public mozilla::LinkedListElement<PersistentRooted<T> > {
+    typedef mozilla::LinkedList<PersistentRooted> List;
+    typedef mozilla::LinkedListElement<PersistentRooted> Element;
+
+    void registerWithRuntime(JSRuntime *rt) {
+        JS::shadow::Runtime *srt = JS::shadow::Runtime::asShadowRuntime(rt);
+        srt->getPersistentRootedList<T>().insertBack(this);
+    }
+
+  public:
+    PersistentRooted(JSContext *cx) : ptr(js::GCMethods<T>::initial())
+    {
+        registerWithRuntime(js::GetRuntime(cx));
+    }
+
+    PersistentRooted(JSContext *cx, T initial) : ptr(initial)
+    {
+        registerWithRuntime(js::GetRuntime(cx));
+    }
+
+    PersistentRooted(JSRuntime *rt) : ptr(js::GCMethods<T>::initial())
+    {
+        registerWithRuntime(rt);
+    }
+
+    PersistentRooted(JSRuntime *rt, T initial) : ptr(initial)
+    {
+        registerWithRuntime(rt);
+    }
+
+    PersistentRooted(PersistentRooted &rhs) : 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.
+         */
+        rhs.setNext(this);
+    }
+
+    /*
+     * 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; }
+    T *address() { return &ptr; }
+    const T *address() const { return &ptr; }
+    T &get() { return ptr; }
+    const T &get() const { return ptr; }
+
+    T &operator=(T value) {
+        JS_ASSERT(!js::GCMethods<T>::poisoned(value));
+        ptr = value;
+        return ptr;
+    }
+
+    T &operator=(const PersistentRooted &value) {
+        ptr = value;
+        return ptr;
+    }
+
+    void set(T value) {
+        JS_ASSERT(!js::GCMethods<T>::poisoned(value));
+        ptr = value;
+    }
+
+    bool operator!=(const T &other) const { return ptr != other; }
+    bool operator==(const T &other) const { return ptr == other; }
+
+  private:
+    T ptr;
+};
+
 } /* namespace JS */
 
 namespace js {
 
 /*
  * Hook for dynamic root analysis. Checks the native stack and poisons
  * references to GC things which have not been rooted.
  */
--- a/js/public/TypeDecls.h
+++ b/js/public/TypeDecls.h
@@ -52,26 +52,42 @@ typedef ptrdiff_t jsid;
 
 typedef char16_t jschar;
 
 namespace JS {
 
 class Value;
 template <typename T> class Handle;
 template <typename T> class MutableHandle;
+template <typename T> class Rooted;
+template <typename T> class PersistentRooted;
 
 typedef Handle<JSFunction*> HandleFunction;
 typedef Handle<jsid>        HandleId;
 typedef Handle<JSObject*>   HandleObject;
 typedef Handle<JSScript*>   HandleScript;
 typedef Handle<JSString*>   HandleString;
 typedef Handle<Value>       HandleValue;
 
 typedef MutableHandle<JSFunction*> MutableHandleFunction;
 typedef MutableHandle<jsid>        MutableHandleId;
 typedef MutableHandle<JSObject*>   MutableHandleObject;
 typedef MutableHandle<JSScript*>   MutableHandleScript;
 typedef MutableHandle<JSString*>   MutableHandleString;
 typedef MutableHandle<Value>       MutableHandleValue;
 
+typedef Rooted<JSObject*>       RootedObject;
+typedef Rooted<JSFunction*>     RootedFunction;
+typedef Rooted<JSScript*>       RootedScript;
+typedef Rooted<JSString*>       RootedString;
+typedef Rooted<jsid>            RootedId;
+typedef Rooted<JS::Value>       RootedValue;
+
+typedef PersistentRooted<JSFunction*> PersistentRootedFunction;
+typedef PersistentRooted<jsid>        PersistentRootedId;
+typedef PersistentRooted<JSObject*>   PersistentRootedObject;
+typedef PersistentRooted<JSScript*>   PersistentRootedScript;
+typedef PersistentRooted<JSString*>   PersistentRootedString;
+typedef PersistentRooted<Value>       PersistentRootedValue;
+
 } // namespace JS
 
 #endif /* js_TypeDecls_h */
--- a/js/src/NamespaceImports.h
+++ b/js/src/NamespaceImports.h
@@ -89,16 +89,24 @@ using JS::NativeImpl;
 using JS::Rooted;
 using JS::RootedFunction;
 using JS::RootedId;
 using JS::RootedObject;
 using JS::RootedScript;
 using JS::RootedString;
 using JS::RootedValue;
 
+using JS::PersistentRooted;
+using JS::PersistentRootedFunction;
+using JS::PersistentRootedId;
+using JS::PersistentRootedObject;
+using JS::PersistentRootedScript;
+using JS::PersistentRootedString;
+using JS::PersistentRootedValue;
+
 using JS::Handle;
 using JS::HandleFunction;
 using JS::HandleId;
 using JS::HandleObject;
 using JS::HandleScript;
 using JS::HandleString;
 using JS::HandleValue;
 
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -10,16 +10,19 @@
 #include "jsworkers.h"
 
 #include "vm/Runtime.h"
 
 namespace js {
 namespace gc {
 
 void
+MarkPersistentRootedChains(JSTracer *trc);
+
+void
 MarkRuntime(JSTracer *trc, bool useSavedRoots = false);
 
 void
 BufferGrayRoots(GCMarker *gcmarker);
 
 class AutoCopyFreeListToArenas
 {
     JSRuntime *runtime;
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -612,16 +612,54 @@ JSPropertyDescriptor::trace(JSTracer *tr
     }
     if ((attrs & JSPROP_SETTER) && setter) {
         JSObject *tmp = JS_FUNC_TO_DATA_PTR(JSObject *, setter);
         MarkObjectRoot(trc, &tmp, "Descriptor::set");
         setter = JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, tmp);
     }
 }
 
+// Mark a chain of PersistentRooted pointers that might be null.
+template<typename Referent>
+static void
+MarkPersistentRootedChain(JSTracer *trc,
+                          mozilla::LinkedList<PersistentRooted<Referent *> > &list,
+                          void (*marker)(JSTracer *trc, Referent **ref, const char *name),
+                          const char *name)
+{
+    for (PersistentRooted<Referent *> *r = list.getFirst();
+         r != nullptr;
+         r = r->getNext())
+    {
+        if (r->get())
+            marker(trc, r->address(), name);
+    }
+}
+
+void
+js::gc::MarkPersistentRootedChains(JSTracer *trc)
+{
+    JSRuntime *rt = trc->runtime;
+
+    MarkPersistentRootedChain(trc, rt->functionPersistentRooteds, &MarkObjectRoot,
+                              "PersistentRooted<JSFunction *>");
+    MarkPersistentRootedChain(trc, rt->objectPersistentRooteds, &MarkObjectRoot,
+                              "PersistentRooted<JSObject *>");
+    MarkPersistentRootedChain(trc, rt->scriptPersistentRooteds, &MarkScriptRoot,
+                              "PersistentRooted<JSScript *>");
+    MarkPersistentRootedChain(trc, rt->stringPersistentRooteds, &MarkStringRoot, 
+                              "PersistentRooted<JSString *>");
+
+    // Mark the PersistentRooted chains of types that are never null.
+    for (JS::PersistentRootedId *r = rt->idPersistentRooteds.getFirst(); r != nullptr; r = r->getNext())
+        MarkIdRoot(trc, r->address(), "PersistentRooted<jsid>");
+    for (JS::PersistentRootedValue *r = rt->valuePersistentRooteds.getFirst(); r != nullptr; r = r->getNext())
+        MarkValueRoot(trc, r->address(), "PersistentRooted<Value>");
+}
+
 void
 js::gc::MarkRuntime(JSTracer *trc, bool useSavedRoots)
 {
     JSRuntime *rt = trc->runtime;
     JS_ASSERT(trc->callback != GCMarker::GrayCallback);
 
     JS_ASSERT(!rt->mainThread.suppressGC);
 
@@ -656,16 +694,18 @@ js::gc::MarkRuntime(JSTracer *trc, bool 
                 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
                 MOZ_ASSUME_UNREACHABLE("unexpected js::RootInfo::type value");
         }
     }
 
+    MarkPersistentRootedChains(trc);
+
     if (rt->scriptAndCountsVector) {
         ScriptAndCountsVector &vec = *rt->scriptAndCountsVector;
         for (size_t i = 0; i < vec.length(); i++)
             MarkScriptRoot(trc, &vec[i].script, "scriptAndCountsVector");
     }
 
     if (!rt->isBeingDestroyed() &&
         !trc->runtime->isHeapMinorCollecting() &&
--- a/js/src/jsapi-tests/moz.build
+++ b/js/src/jsapi-tests/moz.build
@@ -45,16 +45,17 @@ SOURCES += [
     'testLooselyEqual.cpp',
     'testNewObject.cpp',
     'testNullRoot.cpp',
     'testObjectEmulatingUndefined.cpp',
     'testOOM.cpp',
     'testOps.cpp',
     'testOriginPrincipals.cpp',
     'testParseJSON.cpp',
+    'testPersistentRooted.cpp',
     'testProfileStrings.cpp',
     'testPropCache.cpp',
     'testRegExp.cpp',
     'testResolveRecursion.cpp',
     'tests.cpp',
     'testSameValue.cpp',
     'testScriptInfo.cpp',
     'testScriptObject.cpp',
new file mode 100644
--- /dev/null
+++ b/js/src/jsapi-tests/testPersistentRooted.cpp
@@ -0,0 +1,185 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if defined(JSGC_USE_EXACT_ROOTING)
+
+#include "js/Class.h"
+#include "jsapi-tests/tests.h"
+
+using namespace JS;
+
+struct BarkWhenTracedClass {
+    static int finalizeCount;
+    static int traceCount;
+
+    static const JSClass class_;
+    static void finalize(JSFreeOp *fop, JSObject *obj) { finalizeCount++; }
+    static void trace(JSTracer *trc, JSObject *obj) { traceCount++; }
+    static void reset() { finalizeCount = 0; traceCount = 0; }
+};
+
+int BarkWhenTracedClass::finalizeCount;
+int BarkWhenTracedClass::traceCount;
+
+const JSClass BarkWhenTracedClass::class_ = {
+  "BarkWhenTracedClass", 0,
+  JS_PropertyStub,
+  JS_DeletePropertyStub,
+  JS_PropertyStub,
+  JS_StrictPropertyStub,
+  JS_EnumerateStub,
+  JS_ResolveStub,
+  JS_ConvertStub,
+  finalize,
+  nullptr,
+  nullptr,
+  nullptr,
+  nullptr,
+  trace
+};
+
+struct Kennel {
+    PersistentRootedObject obj;
+    Kennel(JSContext *cx) : obj(cx) { }
+    Kennel(JSContext *cx, const HandleObject &woof) : obj(cx, woof) { };
+};
+
+// A function for allocating a Kennel and a barker. Only allocating
+// PersistentRooteds on the heap, and in this function, helps ensure that the
+// conservative GC doesn't find stray references to the barker. Ugh.
+JS_NEVER_INLINE static Kennel *
+Allocate(JSContext *cx)
+{
+    RootedObject barker(cx, JS_NewObject(cx, &BarkWhenTracedClass::class_, nullptr, nullptr));
+    if (!barker)
+        return nullptr;
+
+    return new Kennel(cx, barker);
+}
+
+// Do a GC, expecting |n| barkers to be finalized.
+static bool
+GCFinalizesNBarkers(JSContext *cx, int n)
+{
+    int preGCTrace = BarkWhenTracedClass::traceCount;
+    int preGCFinalize = BarkWhenTracedClass::finalizeCount;
+
+    JS_GC(JS_GetRuntime(cx));
+
+    return (BarkWhenTracedClass::finalizeCount == preGCFinalize + n &&
+            BarkWhenTracedClass::traceCount > preGCTrace);
+}
+
+// PersistentRooted instances protect their contents from being recycled.
+BEGIN_TEST(test_PersistentRooted)
+{
+    BarkWhenTracedClass::reset();
+
+    Kennel *kennel = Allocate(cx);
+    CHECK(kennel);
+
+    // GC should be able to find our barker.
+    CHECK(GCFinalizesNBarkers(cx, 0));
+
+    delete(kennel);
+
+    // Now GC should not be able to find the barker.
+    JS_GC(JS_GetRuntime(cx));
+    CHECK(BarkWhenTracedClass::finalizeCount == 1);
+
+    return true;
+}
+END_TEST(test_PersistentRooted)
+
+// GC should not be upset by null PersistentRooteds.
+BEGIN_TEST(test_PersistentRootedNull)
+{
+    BarkWhenTracedClass::reset();
+
+    Kennel kennel(cx);
+    CHECK(!kennel.obj);
+
+    JS_GC(JS_GetRuntime(cx));
+    CHECK(BarkWhenTracedClass::finalizeCount == 0);
+
+    return true;
+}
+END_TEST(test_PersistentRootedNull)
+
+// Copy construction works.
+BEGIN_TEST(test_PersistentRootedCopy)
+{
+    BarkWhenTracedClass::reset();
+
+    Kennel *kennel = Allocate(cx);
+    CHECK(kennel);
+
+    CHECK(GCFinalizesNBarkers(cx, 0));
+
+    // Copy construction! AMAZING!
+    Kennel *newKennel = new Kennel(*kennel);
+
+    CHECK(GCFinalizesNBarkers(cx, 0));
+
+    delete(kennel);
+
+    CHECK(GCFinalizesNBarkers(cx, 0));
+
+    delete(newKennel);
+
+    // Now that kennel and nowKennel are both deallocated, GC should not be
+    // able to find the barker.
+    JS_GC(JS_GetRuntime(cx));
+    CHECK(BarkWhenTracedClass::finalizeCount == 1);
+
+    return true;
+}
+END_TEST(test_PersistentRootedCopy)
+
+// Assignment works.
+BEGIN_TEST(test_PersistentRootedAssign)
+{
+    BarkWhenTracedClass::reset();
+
+    Kennel *kennel = Allocate(cx);
+    CHECK(kennel);
+
+    CHECK(GCFinalizesNBarkers(cx, 0));
+
+    // Allocate a new, empty kennel.
+    Kennel *kennel2 = new Kennel(cx);
+
+    // Assignment! ASTONISHING!
+    *kennel2 = *kennel;
+
+    // With both kennels referring to the same barker, it is held alive.
+    CHECK(GCFinalizesNBarkers(cx, 0));
+
+    delete(kennel2);
+
+    // The destination of the assignment alone holds the barker alive.
+    CHECK(GCFinalizesNBarkers(cx, 0));
+
+    // Allocate a second barker.
+    kennel2 = Allocate(cx);
+    CHECK(kennel);
+
+    *kennel = *kennel2;
+
+    // Nothing refers to the first kennel any more.
+    CHECK(GCFinalizesNBarkers(cx, 1));
+
+    delete(kennel);
+    delete(kennel2);
+
+    // Now that kennel and kennel2 are both deallocated, GC should not be
+    // able to find the barker.
+    JS_GC(JS_GetRuntime(cx));
+    CHECK(BarkWhenTracedClass::finalizeCount == 2);
+
+    return true;
+}
+END_TEST(test_PersistentRootedAssign)
+
+#endif // defined(JSGC_USE_EXACT_ROOTING)
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1022,16 +1022,23 @@ js_FinishGC(JSRuntime *rt)
     rt->gcUserAvailableChunkListHead = nullptr;
     for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront())
         Chunk::release(rt, r.front());
     rt->gcChunkSet.clear();
 
     rt->gcChunkPool.expireAndFree(rt, true);
 
     rt->gcRootsHash.clear();
+
+    rt->functionPersistentRooteds.clear();
+    rt->idPersistentRooteds.clear();
+    rt->objectPersistentRooteds.clear();
+    rt->scriptPersistentRooteds.clear();
+    rt->stringPersistentRooteds.clear();
+    rt->valuePersistentRooteds.clear();
 }
 
 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
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -6,16 +6,17 @@
 
 #ifndef jspubtd_h
 #define jspubtd_h
 
 /*
  * JS public API typedefs.
  */
 
+#include "mozilla/LinkedList.h"
 #include "mozilla/NullPtr.h"
 #include "mozilla/PodOperations.h"
 
 #include "jsprototypes.h"
 #include "jstypes.h"
 
 #include "js/TypeDecls.h"
 
@@ -168,19 +169,22 @@ typedef bool                    (*JSInit
 
 /*
  * Generic trace operation that calls JS_CallTracer on each traceable thing
  * stored in data.
  */
 typedef void
 (* JSTraceDataOp)(JSTracer *trc, void *data);
 
+void js_FinishGC(JSRuntime *rt);
+
 namespace js {
 namespace gc {
 class StoreBuffer;
+void MarkPersistentRootedChains(JSTracer *);
 }
 }
 
 namespace JS {
 
 typedef void (*OffThreadCompileCallback)(void *token, void *callbackData);
 
 namespace shadow {
@@ -219,18 +223,59 @@ struct Runtime
 
 #ifdef JSGC_GENERATIONAL
     js::gc::StoreBuffer *gcStoreBufferPtr() { return gcStoreBufferPtr_; }
 #endif
 
     static JS::shadow::Runtime *asShadowRuntime(JSRuntime *rt) {
         return reinterpret_cast<JS::shadow::Runtime*>(rt);
     }
+
+    /* Allow inlining of PersistentRooted constructors and destructors. */
+  private:
+    template <typename Referent> friend class JS::PersistentRooted;
+    friend void js::gc::MarkPersistentRootedChains(JSTracer *);
+    friend void ::js_FinishGC(JSRuntime *rt);
+
+    mozilla::LinkedList<PersistentRootedFunction> functionPersistentRooteds;
+    mozilla::LinkedList<PersistentRootedId>       idPersistentRooteds;
+    mozilla::LinkedList<PersistentRootedObject>   objectPersistentRooteds;
+    mozilla::LinkedList<PersistentRootedScript>   scriptPersistentRooteds;
+    mozilla::LinkedList<PersistentRootedString>   stringPersistentRooteds;
+    mozilla::LinkedList<PersistentRootedValue>    valuePersistentRooteds;
+
+    /* Specializations of this return references to the appropriate list. */
+    template<typename Referent>
+    inline mozilla::LinkedList<PersistentRooted<Referent> > &getPersistentRootedList();
 };
 
+template<>
+inline mozilla::LinkedList<PersistentRootedFunction>
+&Runtime::getPersistentRootedList<JSFunction *>() { return functionPersistentRooteds; }
+
+template<>
+inline mozilla::LinkedList<PersistentRootedId>
+&Runtime::getPersistentRootedList<jsid>() { return idPersistentRooteds; }
+
+template<>
+inline mozilla::LinkedList<PersistentRootedObject>
+&Runtime::getPersistentRootedList<JSObject *>() { return objectPersistentRooteds; }
+
+template<>
+inline mozilla::LinkedList<PersistentRootedScript>
+&Runtime::getPersistentRootedList<JSScript *>() { return scriptPersistentRooteds; }
+
+template<>
+inline mozilla::LinkedList<PersistentRootedString>
+&Runtime::getPersistentRootedList<JSString *>() { return stringPersistentRooteds; }
+
+template<>
+inline mozilla::LinkedList<PersistentRootedValue>
+&Runtime::getPersistentRootedList<Value>() { return valuePersistentRooteds; }
+
 } /* namespace shadow */
 } /* namespace JS */
 
 namespace js {
 
 /*
  * Parallel operations in general can have one of three states. They may
  * succeed, fail, or "bail", where bail indicates that the code encountered an