Bug 811168 - Implement Unrooted<T> protect BaseShape; r=billm
authorTerrence Cole <terrence@mozilla.com>
Wed, 14 Nov 2012 16:06:27 -0800
changeset 123760 6a5af800fbad021f2a5a577b9ed8a72a7f6cf2a8
parent 123759 2880c2eca430349d80113e0c48c6c56cd577cc8d
child 123761 4d974d0508fa91c0a3858bc6ad81c7d4292212e4
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm
bugs811168
milestone20.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 811168 - Implement Unrooted<T> protect BaseShape; r=billm The new Unrooted<T> class adds static typing and dynamic debug-mode assertion to better protect our GC invarients when writing performance-sensitve code.
js/src/gc/Barrier.h
js/src/gc/Marking.cpp
js/src/gc/Root.h
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/jsfun.cpp
js/src/jsgcinlines.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jspropertytree.cpp
js/src/jsscope.cpp
js/src/jsscope.h
js/src/jsscopeinlines.h
js/src/jsscript.cpp
js/src/vm/ObjectImpl.h
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -607,16 +607,17 @@ class HeapId : public EncapsulatedId
 template<class T>
 class ReadBarriered
 {
     T *value;
 
   public:
     ReadBarriered() : value(NULL) {}
     ReadBarriered(T *value) : value(value) {}
+    ReadBarriered(Unrooted<T*> unrooted) : value(unrooted) {}
 
     T *get() const {
         if (!value)
             return NULL;
         T::readBarrier(value);
         return value;
     }
 
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -80,17 +80,17 @@ static inline void
 PushMarkStack(GCMarker *gcmarker, types::TypeObject *thing);
 
 namespace js {
 namespace gc {
 
 static void MarkChildren(JSTracer *trc, JSString *str);
 static void MarkChildren(JSTracer *trc, JSScript *script);
 static void MarkChildren(JSTracer *trc, Shape *shape);
-static void MarkChildren(JSTracer *trc, BaseShape *base);
+static void MarkChildren(JSTracer *trc, UnrootedBaseShape base);
 static void MarkChildren(JSTracer *trc, types::TypeObject *type);
 static void MarkChildren(JSTracer *trc, ion::IonCode *code);
 #if JS_HAS_XML_SUPPORT
 static void MarkChildren(JSTracer *trc, JSXML *xml);
 #endif
 
 } /* namespace gc */
 } /* namespace js */
@@ -133,16 +133,17 @@ AsGCMarker(JSTracer *trc)
     JS_ASSERT(IS_GC_MARKING_TRACER(trc));
     return static_cast<GCMarker *>(trc);
 }
 
 template<typename T>
 static void
 MarkInternal(JSTracer *trc, T **thingp)
 {
+    AutoAssertNoGC nogc;
     JS_ASSERT(thingp);
     T *thing = *thingp;
 
     CheckMarkedThing(trc, thing);
 
     /*
      * Don't mark things outside a compartment if we are in a per-compartment
      * GC.
@@ -330,29 +331,29 @@ DeclMarkerImpl(XML, JSXML)
 void
 gc::MarkKind(JSTracer *trc, void **thingp, JSGCTraceKind kind)
 {
     JS_ASSERT(thingp);
     JS_ASSERT(*thingp);
     JS_ASSERT(kind == GetGCThingTraceKind(*thingp));
     switch (kind) {
       case JSTRACE_OBJECT:
-        MarkInternal(trc, reinterpret_cast<JSObject **>(thingp));
+        MarkInternal(trc, reinterpret_cast<RawObject *>(thingp));
         break;
       case JSTRACE_STRING:
-        MarkInternal(trc, reinterpret_cast<JSString **>(thingp));
+        MarkInternal(trc, reinterpret_cast<RawString *>(thingp));
         break;
       case JSTRACE_SCRIPT:
-        MarkInternal(trc, reinterpret_cast<JSScript **>(thingp));
+        MarkInternal(trc, reinterpret_cast<RawScript *>(thingp));
         break;
       case JSTRACE_SHAPE:
-        MarkInternal(trc, reinterpret_cast<Shape **>(thingp));
+        MarkInternal(trc, reinterpret_cast<RawShape *>(thingp));
         break;
       case JSTRACE_BASE_SHAPE:
-        MarkInternal(trc, reinterpret_cast<BaseShape **>(thingp));
+        MarkInternal(trc, reinterpret_cast<RawBaseShape *>(thingp));
         break;
       case JSTRACE_TYPE_OBJECT:
         MarkInternal(trc, reinterpret_cast<types::TypeObject **>(thingp));
         break;
       case JSTRACE_IONCODE:
         MarkInternal(trc, reinterpret_cast<ion::IonCode **>(thingp));
         break;
 #if JS_HAS_XML_SUPPORT
@@ -736,20 +737,20 @@ PushMarkStack(GCMarker *gcmarker, ion::I
 {
     JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
 
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         gcmarker->pushIonCode(thing);
 }
 
 static inline void
-ScanBaseShape(GCMarker *gcmarker, BaseShape *base);
+ScanBaseShape(GCMarker *gcmarker, UnrootedBaseShape base);
 
 static void
-PushMarkStack(GCMarker *gcmarker, BaseShape *thing)
+PushMarkStack(GCMarker *gcmarker, UnrootedBaseShape thing)
 {
     JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
 
     /* We mark base shapes directly rather than pushing on the stack. */
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         ScanBaseShape(gcmarker, thing);
 }
 
@@ -766,17 +767,17 @@ ScanShape(GCMarker *gcmarker, Shape *sha
         PushMarkStack(gcmarker, JSID_TO_OBJECT(id));
 
     shape = shape->previous();
     if (shape && shape->markIfUnmarked(gcmarker->getMarkColor()))
         goto restart;
 }
 
 static inline void
-ScanBaseShape(GCMarker *gcmarker, BaseShape *base)
+ScanBaseShape(GCMarker *gcmarker, UnrootedBaseShape base)
 {
     base->assertConsistency();
 
     if (base->hasGetterObject())
         PushMarkStack(gcmarker, base->getterObject());
 
     if (base->hasSetterObject())
         PushMarkStack(gcmarker, base->setterObject());
@@ -788,17 +789,17 @@ ScanBaseShape(GCMarker *gcmarker, BaseSh
     }
 
     /*
      * All children of the owned base shape are consistent with its
      * unowned one, thus we do not need to trace through children of the
      * unowned base shape.
      */
     if (base->isOwned()) {
-        UnownedBaseShape *unowned = base->baseUnowned();
+        UnrootedUnownedBaseShape unowned = base->baseUnowned();
         JS_ASSERT(base->compartment() == unowned->compartment());
         unowned->markIfUnmarked(gcmarker->getMarkColor());
     }
 }
 
 static inline void
 ScanLinearString(GCMarker *gcmarker, JSLinearString *str)
 {
@@ -919,31 +920,31 @@ gc::MarkChildren(JSTracer *trc, JSScript
 
 static void
 gc::MarkChildren(JSTracer *trc, Shape *shape)
 {
     shape->markChildren(trc);
 }
 
 static void
-gc::MarkChildren(JSTracer *trc, BaseShape *base)
+gc::MarkChildren(JSTracer *trc, UnrootedBaseShape base)
 {
     base->markChildren(trc);
 }
 
 /*
  * This function is used by the cycle collector to trace through the
  * children of a BaseShape (and its baseUnowned(), if any). The cycle
  * collector does not directly care about BaseShapes, so only the
  * getter, setter, and parent are marked. Furthermore, the parent is
  * marked only if it isn't the same as prevParent, which will be
  * updated to the current shape's parent.
  */
 static inline void
-MarkCycleCollectorChildren(JSTracer *trc, BaseShape *base, JSObject **prevParent)
+MarkCycleCollectorChildren(JSTracer *trc, UnrootedBaseShape base, JSObject **prevParent)
 {
     JS_ASSERT(base);
 
     /*
      * The cycle collector does not need to trace unowned base shapes,
      * as they have the same getter, setter and parent as the original
      * base shape.
      */
@@ -1428,37 +1429,37 @@ GCMarker::drainMarkStack(SliceBudget &bu
     return true;
 }
 
 void
 js::TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind)
 {
     switch (kind) {
       case JSTRACE_OBJECT:
-        MarkChildren(trc, static_cast<JSObject *>(thing));
+        MarkChildren(trc, static_cast<RawObject>(thing));
         break;
 
       case JSTRACE_STRING:
-        MarkChildren(trc, static_cast<JSString *>(thing));
+        MarkChildren(trc, static_cast<RawString>(thing));
         break;
 
       case JSTRACE_SCRIPT:
-        MarkChildren(trc, static_cast<JSScript *>(thing));
+        MarkChildren(trc, static_cast<RawScript>(thing));
         break;
 
       case JSTRACE_SHAPE:
-        MarkChildren(trc, static_cast<Shape *>(thing));
+        MarkChildren(trc, static_cast<RawShape>(thing));
         break;
 
       case JSTRACE_IONCODE:
         MarkChildren(trc, (js::ion::IonCode *)thing);
         break;
 
       case JSTRACE_BASE_SHAPE:
-        MarkChildren(trc, static_cast<BaseShape *>(thing));
+        MarkChildren(trc, static_cast<RawBaseShape>(thing));
         break;
 
       case JSTRACE_TYPE_OBJECT:
         MarkChildren(trc, (types::TypeObject *)thing);
         break;
 
 #if JS_HAS_XML_SUPPORT
       case JSTRACE_XML:
--- a/js/src/gc/Root.h
+++ b/js/src/gc/Root.h
@@ -20,70 +20,134 @@
 /*
  * Moving GC Stack Rooting
  *
  * A moving GC may change the physical location of GC allocated things, even
  * when they are rooted, updating all pointers to the thing to refer to its new
  * location. The GC must therefore know about all live pointers to a thing,
  * not just one of them, in order to behave correctly.
  *
- * The classes below are used to root stack locations whose value may be held
- * live across a call that can trigger GC (i.e. a call which might allocate any
- * GC things). For a code fragment such as:
+ * The |Root| and |Handle| classes below are used to root stack locations
+ * whose value may be held live across a call that can trigger GC. For a
+ * code fragment such as:
  *
- * Foo();
+ * JSObject *obj = NewObject(cx);
+ * DoSomething(cx);
  * ... = obj->lastProperty();
  *
- * If Foo() can trigger a GC, the stack location of obj must be rooted to
- * ensure that the GC does not move the JSObject referred to by obj without
- * updating obj's location itself. This rooting must happen regardless of
- * whether there are other roots which ensure that the object itself will not
- * be collected.
+ * If |DoSomething()| can trigger a GC, the stack location of |obj| must be
+ * rooted to ensure that the GC does not move the JSObject referred to by
+ * |obj| without updating |obj|'s location itself. This rooting must happen
+ * regardless of whether there are other roots which ensure that the object
+ * itself will not be collected.
+ *
+ * If |DoSomething()| cannot trigger a GC, and the same holds for all other
+ * calls made between |obj|'s definitions and its last uses, then no rooting
+ * is required. The |Unrooted| class below is used to ensure that this
+ * property is true and remains true in the future.
  *
- * If Foo() cannot trigger a GC, and the same holds for all other calls made
- * between obj's definitions and its last uses, then no rooting is required.
+ * SpiderMonkey can trigger a GC at almost any time and in ways that are not
+ * always clear. For example, the following innocuous-looking actions can
+ * cause a GC: allocation of any new GC thing; JSObject::hasProperty;
+ * JS_ReportError and friends; and ToNumber, among many others. The following
+ * dangerous-looking actions cannot trigger a GC: js_malloc, cx->malloc_,
+ * rt->malloc_, and friends and JS_ReportOutOfMemory.
  *
- * Several classes are available for rooting stack locations. All are templated
- * on the type T of the value being rooted, for which RootMethods<T> must
- * have an instantiation.
+ * The following family of four classes will exactly root a stack location.
+ * Incorrect usage of these classes will result in a compile error in almost
+ * all cases. Therefore, it is very hard to be incorrectly rooted if you use
+ * these classes exclusively. These classes are all templated on the type T of
+ * the value being rooted.
  *
  * - Rooted<T> declares a variable of type T, whose value is always rooted.
  *   Rooted<T> may be automatically coerced to a Handle<T>, below. Rooted<T>
  *   should be used whenever a local variable's value may be held live across a
- *   call which can allocate GC things or otherwise trigger a GC.
+ *   call which can trigger a GC. This is generally true of
  *
  * - Handle<T> is a const reference to a Rooted<T>. Functions which take GC
  *   things or values as arguments and need to root those arguments should
  *   generally use handles for those arguments and avoid any explicit rooting.
  *   This has two benefits. First, when several such functions call each other
  *   then redundant rooting of multiple copies of the GC thing can be avoided.
  *   Second, if the caller does not pass a rooted value a compile error will be
  *   generated, which is quicker and easier to fix than when relying on a
  *   separate rooting analysis.
+ *
+ * - MutableHandle<T> is a non-const reference to Rooted<T>. It is used in the
+ *   same way as Handle<T> and includes a |set(const T &v)| method to allow
+ *   updating the value of the referenced Rooted<T>. A MutableHandle<T> can be
+ *   created from a Rooted<T> by using |Rooted<T>::operator&()|.
+ *
+ * - Return<T> is the type of a value returned from a function. Return<T> is
+ *   opaque and cannot be accessed unless correctly rooted. It is invalid to
+ *   create a named Return<T>, so the return value must be assigned to
+ *   Rooted<T> immediately, or discarded and not referenced again.
+ *
+ * In some cases the small performance overhead of exact rooting is too much.
+ * In these cases, try the following:
+ *
+ * - Move all Rooted<T> above inner loops: this allows you to re-use the root
+ *   on each iteration of the loop.
+ *
+ * - Pass Handle<T> through your hot call stack to avoid re-rooting costs at
+ *   every invocation.
+ *
+ * If this is not enough, the following family of two classes and two
+ * functions can provide partially type-safe and mostly runtime-safe access to
+ * GC things.
+ *
+ * - AutoAssertNoGC is a scoped guard that will trigger an assertion if a GC,
+ *   or an appropriately marked method that might GC, is entered when it is in
+ *   scope.  By convention the name given to instances of this guard is |nogc|.
+ *
+ * - AssertCanGC() will assert if an AutoAssertNoGC is in scope either locally
+ *   or anywhere in the call stack.
+ *
+ * - UnrootedT is a typedef for a pointer to thing of type T. In DEBUG builds
+ *   it gets replaced by a class that additionally acts as an AutoAssertNoGC
+ *   guard. Since there is only minimal compile-time protection against
+ *   mis-use, UnrootedT should only be used in places where there is adequate
+ *   coverage of AutoAssertNoGC and AssertCanGC guards to ensure that mis-use
+ *   is caught at runtime.
+ *
+ * - DropUnrooted(UnrootedT &v) will poison |v| and end its AutoAssertNoGC
+ *   scope. This can be used to force |v| out of scope before its C++ scope
+ *   would end naturally. The usage of braces C++ syntactical scopes |{...}|
+ *   is strongly perferred to this, but sometimes will not work because of
+ *   awkwardly overlapping lifetimes.
+ *
+ * There also exists a set of RawT typedefs for modules without rooting
+ * concerns, such as the GC. Do not use these as they provide no rooting
+ * protection whatsoever.
  */
 
 namespace js {
 
 template <typename T> class Rooted;
+template <typename T> class Unrooted;
 
 template <typename T>
 struct RootMethods {};
 
 template <typename T>
+class RootedBase {};
+
+template <typename T>
 class HandleBase {};
 
 template <typename T>
 class MutableHandleBase {};
 
 } /* namespace js */
 
 namespace JS {
 
 class AutoAssertNoGC;
 
+template <typename T> class Handle;
 template <typename T> class MutableHandle;
 
 JS_FRIEND_API(void) EnterAssertNoGCScope();
 JS_FRIEND_API(void) LeaveAssertNoGCScope();
 
 /* These are exposing internal state of the GC for inlining purposes. */
 JS_FRIEND_API(bool) InNoGCScope();
 JS_FRIEND_API(bool) isGCEnabled();
@@ -95,46 +159,44 @@ JS_FRIEND_API(bool) isGCEnabled();
  *   foo(NullPtr());
  * which avoids creating a Rooted<JSObject*> just to pass NULL.
  */
 struct NullPtr
 {
     static void * const constNullValue;
 };
 
-template <typename T>
-class MutableHandle;
-
 /*
  * Reference to a T that has been rooted elsewhere. This is most useful
  * as a parameter type, which guarantees that the T lvalue is properly
  * rooted. See "Move GC Stack Rooting" above.
  *
  * If you want to add additional methods to Handle for a specific
  * specialization, define a HandleBase<T> specialization containing them.
  */
 template <typename T>
 class Handle : public js::HandleBase<T>
 {
+    friend class MutableHandle<T>;
+
   public:
     /* Creates a handle from a handle of a type convertible to T. */
     template <typename S>
     Handle(Handle<S> handle,
            typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0)
     {
         ptr = reinterpret_cast<const T *>(handle.address());
     }
 
     /* Create a handle for a NULL pointer. */
     Handle(NullPtr) {
         typedef typename js::tl::StaticAssert<js::tl::IsPointerType<T>::result>::result _;
         ptr = reinterpret_cast<const T *>(&NullPtr::constNullValue);
     }
 
-    friend class MutableHandle<T>;
     Handle(MutableHandle<T> handle) {
         ptr = handle.address();
     }
 
     /*
      * This may be called only if the location of the T is guaranteed
      * to be marked (for some reason other than being a Rooted),
      * e.g., if it is guaranteed to be reachable from an implicit root.
@@ -160,26 +222,26 @@ class Handle : public js::HandleBase<T>
     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; }
     T get() const { return *ptr; }
 
-    operator T () const { return get(); }
-    T operator ->() const { return get(); }
+    operator T() const { return get(); }
+    T operator->() const { return get(); }
 
   private:
     Handle() {}
 
     const T *ptr;
 
     template <typename S>
-    void operator =(S v) MOZ_DELETE;
+    void operator=(S v) MOZ_DELETE;
 };
 
 typedef Handle<JSObject*>    HandleObject;
 typedef Handle<JSFunction*>  HandleFunction;
 typedef Handle<JSScript*>    HandleScript;
 typedef Handle<JSString*>    HandleString;
 typedef Handle<jsid>         HandleId;
 typedef Handle<Value>        HandleValue;
@@ -203,18 +265,17 @@ class MutableHandle : public js::Mutable
         this->ptr = reinterpret_cast<const T *>(handle.address());
     }
 
     template <typename S>
     inline
     MutableHandle(js::Rooted<S> *root,
                   typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0);
 
-    void set(T v)
-    {
+    void set(T v) {
         JS_ASSERT(!js::RootMethods<T>::poisoned(v));
         *ptr = v;
     }
 
     /*
      * This may be called only if the location of the T is guaranteed
      * to be marked (for some reason other than being a Rooted),
      * e.g., if it is guaranteed to be reachable from an implicit root.
@@ -225,26 +286,26 @@ class MutableHandle : public js::Mutable
         MutableHandle h;
         h.ptr = p;
         return h;
     }
 
     T *address() const { return ptr; }
     T get() const { return *ptr; }
 
-    operator T () const { return get(); }
-    T operator ->() const { return get(); }
+    operator T() const { return get(); }
+    T operator->() const { return get(); }
 
   private:
     MutableHandle() {}
 
     T *ptr;
 
     template <typename S>
-    void operator =(S v) MOZ_DELETE;
+    void operator=(S v) MOZ_DELETE;
 };
 
 typedef MutableHandle<JSObject*>   MutableHandleObject;
 typedef MutableHandle<JSFunction*> MutableHandleFunction;
 typedef MutableHandle<JSScript*>   MutableHandleScript;
 typedef MutableHandle<JSString*>   MutableHandleString;
 typedef MutableHandle<jsid>        MutableHandleId;
 typedef MutableHandle<Value>       MutableHandleValue;
@@ -266,48 +327,46 @@ namespace js {
 
 /*
  * InternalHandle is a handle to an internal pointer into a gcthing. Use
  * InternalHandle when you have a pointer to a direct field of a gcthing, or
  * when you need a parameter type for something that *may* be a pointer to a
  * direct field of a gcthing.
  */
 template <typename T>
-class InternalHandle { };
+class InternalHandle {};
 
 template <typename T>
 class InternalHandle<T*>
 {
     void * const *holder;
     size_t offset;
 
   public:
     /*
      * Create an InternalHandle using a Handle to the gcthing containing the
      * field in question, and a pointer to the field.
      */
     template<typename H>
     InternalHandle(const JS::Handle<H> &handle, T *field)
       : holder((void**)handle.address()), offset(uintptr_t(field) - uintptr_t(handle.get()))
-    {
-    }
+    {}
 
     /*
      * Create an InternalHandle to a field within a Rooted<>.
      */
     template<typename R>
     InternalHandle(const Rooted<R> &root, T *field)
       : holder((void**)root.address()), offset(uintptr_t(field) - uintptr_t(root.get()))
-    {
-    }
+    {}
 
     T *get() const { return reinterpret_cast<T*>(uintptr_t(*holder) + offset); }
 
-    const T& operator *() const { return *get(); }
-    T* operator ->() const { return get(); }
+    const T &operator*() const { return *get(); }
+    T *operator->() const { return get(); }
 
     static InternalHandle<T*> fromMarkedLocation(T *fieldPtr) {
         return InternalHandle(fieldPtr);
     }
 
   private:
     /*
      * Create an InternalHandle to something that is not a pointer to a
@@ -316,42 +375,19 @@ class InternalHandle<T*>
      * regular InternalHandles to gcthing fields.
      *
      * Make this private to prevent accidental misuse; this is only for
      * fromMarkedLocation().
      */
     InternalHandle(T *field)
       : holder(reinterpret_cast<void * const *>(&NullPtr::constNullValue)),
         offset(uintptr_t(field))
-    {
-    }
+    {}
 };
 
-#ifdef DEBUG
-template <typename T>
-class IntermediateNoGC
-{
-    T t_;
-
-  public:
-    IntermediateNoGC(const T &t) : t_(t) {
-        EnterAssertNoGCScope();
-    }
-    IntermediateNoGC(const IntermediateNoGC &) {
-        EnterAssertNoGCScope();
-    }
-    ~IntermediateNoGC() {
-        LeaveAssertNoGCScope();
-    }
-
-    const T &operator->() { return t_; }
-    operator const T &() { return t_; }
-};
-#endif
-
 /*
  * Return<T> wraps GC things that are returned from accessor methods.  The
  * wrapper helps to ensure correct rooting of the returned pointer and safe
  * access while unrooted.
  *
  * Example usage in a method declaration:
  *
  *     class Foo {
@@ -391,156 +427,372 @@ class IntermediateNoGC
  *
  * If Shape::parent were an accessor function returning a Return<Shape*>, this
  * could not happen: Return ensures either immediate rooting or no GC within
  * the same expression.
  */
 template <typename T>
 class Return
 {
-    friend class Rooted<T>;
-
-    const T ptr_;
+    typedef void (Return<T>::* ConvertibleToBool)();
+    void nonNull() {}
 
   public:
     template <typename S>
+    inline Return(const Unrooted<S> &unrooted,
+                  typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0);
+
+    template <typename S>
     Return(const S &ptr,
            typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0)
       : ptr_(ptr)
-    {}
+    {
+        EnterAssertNoGCScope();
+    }
+
+    Return(NullPtr) : ptr_(NULL) {
+        EnterAssertNoGCScope();
+    }
+
+    Return(const Return &ret) : ptr_(ret.ptr_) {
+        EnterAssertNoGCScope();
+    }
 
-    Return(NullPtr) : ptr_(NULL) {}
+    ~Return() {
+        LeaveAssertNoGCScope();
+    }
+
+#ifndef DEBUG
+    /*
+     * In DEBUG builds, |Unrooted<T>| has a constructor that accepts
+     * |Return<T>|, which allows direct assignment into a |Unrooted<T>|. This
+     * is safe because |Unrooted<T>| implies a NoGCScope. In optimized builds,
+     * however, |Unrooted<T>| does not exist, only the UnrootedT typedef to a
+     * raw T. Thus, this unsafe unpack is protected by a different mechanism
+     * in debug builds.
+     */
+    operator const T &() { return ptr_; }
+#endif /* DEBUG */
 
     /*
      * |get(AutoAssertNoGC &)| is the safest way to access a Return<T> without
      * rooting it first: it is impossible to call this method without an
      * AutoAssertNoGC in scope, so the compiler will automatically catch any
      * incorrect usage.
      *
      * Example:
      *     AutoAssertNoGC nogc;
-     *     RawScript script = fun->script().get(nogc);
+     *     UnrootedScript script = fun->script().get(nogc);
      */
     const T &get(AutoAssertNoGC &) const {
         return ptr_;
     }
 
     /*
-     * |operator->|'s result cannot be stored in a local variable, so it is safe
-     * to use in a CanGC context iff no GC can occur anywhere within the same
-     * expression (generally from one |;| to the next). |operator->| uses a
-     * temporary object as a guard and will assert if a CanGC context is
-     * encountered before the next C++ Sequence Point.
+     * |operator->|'s result cannot be stored in a local variable, so it is
+     * safe to use in a CanGC context iff no GC can occur anywhere within the
+     * same expression (generally from one |;| to the next). |operator->| is
+     * protected at runtime by the fact that |Return<T>| is an AutoAssertNoGC.
+     * Still, care must be taken to avoid having the |Return<T>| on the stack
+     * during a GC, which would result in a runtime assertion.
      *
      * INCORRECT:
      *    fun->script()->bindings = myBindings->clone(cx, ...);
      *
      * The compiler is allowed to reorder |fun->script()::operator->()| above
-     * the call to |clone(cx, ...)|. In this case, the RawScript C++ stores on
-     * the stack may be corrupted by a GC under |clone|. The subsequent
-     * dereference of this pointer to get |bindings| will result in an invalid
-     * access. This wrapper ensures that such usage asserts in DEBUG builds when
-     * it encounters this situation. Without this assertion, it is possible for
-     * such access to corrupt program state instead of crashing immediately.
+     * the call to |clone(cx, ...)|. In this case, the raw js::Script* C++
+     * stores on the stack may be corrupted by a GC under |clone|. The
+     * subsequent dereference of this pointer to get |bindings| will result in
+     * an invalid access. |Return<T>| ensures that such usage asserts in DEBUG
+     * builds when it encounters this situation. Without this assertion, it is
+     * possible for such access to corrupt program state instead of crashing
+     * immediately.
      *
      * CORRECT:
      *    RootedScript clone(cx, myBindings->clone(cx, ...));
      *    fun->script()->bindings = clone;
      */
-#ifdef DEBUG
-    IntermediateNoGC<T> operator->() const {
-        return IntermediateNoGC<T>(ptr_);
-    }
-#else
     const T &operator->() const {
         return ptr_;
     }
-#endif
 
     /*
-     * |unsafeGet()| is unsafe for most uses.  Although it performs similar
-     * checking to |operator->|, its result can be stored to a local variable.
-     * For this reason, it should only be used when it would be incorrect or
-     * absurd to create a new Rooted for its use: e.g. for assertions.
+     * |unsafeGet()| is unsafe for most uses.  Usage of this method should be
+     * restricted to GC internals, assertions, or include a comment explaining
+     * how its usage is protected.
      */
-#ifdef DEBUG
-    IntermediateNoGC<T> unsafeGet() const {
-        return IntermediateNoGC<T>(ptr_);
-    }
-#else
     const T &unsafeGet() const {
         return ptr_;
     }
-#endif
 
     /*
      * |operator==| is safe to use in any context.  It is present to allow:
      *     JS_ASSERT(myScript == fun->script().unsafeGet());
      *
      * To be rewritten as:
      *     JS_ASSERT(fun->script() == myScript);
      *
      * Note: the new order tells C++ to use |Return<JSScript*>::operator=|
      *       instead of direct pointer comparison.
      */
+    operator ConvertibleToBool() const { return ptr_ ? &Return<T>::nonNull : 0; }
     bool operator==(const T &other) { return ptr_ == other; }
     bool operator!=(const T &other) { return ptr_ != other; }
     bool operator==(const Return<T> &other) { return ptr_ == other.ptr_; }
     bool operator==(const JS::Handle<T> &other) { return ptr_ == other.get(); }
     inline bool operator==(const Rooted<T> &other);
+
+  private:
+    const T ptr_;
 };
 
 /*
+ * |Unrooted<T>| acts as an AutoAssertNoGC after it is initialized. It otherwise
+ * acts like as a normal pointer of type T.
+ */
+#ifdef DEBUG
+template <typename T>
+class Unrooted
+{
+  public:
+    Unrooted() : ptr_(UninitializedTag()) {}
+
+    /*
+     * |Unrooted<T>| can be initialized from a convertible |Rooted<S>| or
+     * |Handle<S>|. This is so that we can call AutoAssertNoGC methods that
+     * take |Unrooted<T>| parameters with a convertible rooted argument
+     * without explicit unpacking.
+     *
+     * Note: Even though this allows implicit conversion to |Unrooted<T>|
+     * type, this is safe because Unrooted<T> acts as an AutoAssertNoGC scope.
+     */
+    template <typename S>
+    inline Unrooted(Rooted<S> &root,
+               typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0);
+
+    template <typename S>
+    Unrooted(JS::Handle<S> &root,
+               typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0)
+      : ptr_(root.get())
+    {
+        JS_ASSERT(ptr_ != UninitializedTag());
+        EnterAssertNoGCScope();
+    }
+
+    /*
+     * |Unrooted<T>| can accept |Return<T>| without any casts. This is safe
+     * because |Unrooted<T>| acts as an |AutoAssertNoGC| scope. This is to
+     * enable usage such as:
+     *
+     * Return<Foo*>
+     * CreateFoo(JSContext *cx, ...)
+     * {
+     *     Unrooted<Foo*> foo = js_NewFoo(cx);
+     *     foo.initialize(...);
+     *     return foo;
+     * }
+     */
+    template <typename S>
+    Unrooted(const Return<S> &ret,
+        typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0)
+      : ptr_(ret.unsafeGet())
+    {
+        JS_ASSERT(ptr_ != UninitializedTag());
+        EnterAssertNoGCScope();
+    }
+
+    /*
+     * |Unrooted<T>| can initialize by copying from a convertible type
+     * |Unrooted<S>|. This enables usage such as:
+     *
+     * Unrooted<BaseShape*> base = js_NewBaseShape(cx);
+     * Unrooted<UnownedBaseShape*> ubase = static_cast<UnrootedUnownedBaseShape>(ubase);
+     */
+    template <typename S>
+    Unrooted(const Unrooted<S> &other)
+        /* Note: |static_cast<S>| acquires other.ptr_ in DEBUG builds. */
+      : ptr_(static_cast<T>(static_cast<S>(other)))
+    {
+        if (ptr_ != UninitializedTag())
+            EnterAssertNoGCScope();
+    }
+
+    Unrooted(const Unrooted &other) : ptr_(other.ptr_) {
+        if (ptr_ != UninitializedTag())
+            EnterAssertNoGCScope();
+    }
+
+    Unrooted(const T &p) : ptr_(p) {
+        JS_ASSERT(ptr_ != UninitializedTag());
+        EnterAssertNoGCScope();
+    }
+
+    ~Unrooted() {
+        if (ptr_ != UninitializedTag())
+            LeaveAssertNoGCScope();
+    }
+
+    void drop() {
+        if (ptr_ != UninitializedTag())
+            LeaveAssertNoGCScope();
+        ptr_ = UninitializedTag();
+    }
+
+    /* See notes for Unrooted::Unrooted(const Return<S> &) */
+    template <typename S>
+    Unrooted &operator=(const Return<S> &other) {
+        JS_ASSERT(other.unsafeGet() != UninitializedTag());
+        if (ptr_ == UninitializedTag())
+            EnterAssertNoGCScope();
+        ptr_ = other.unsafeGet();
+        return *this;
+    }
+
+    /* See notes for Unrooted::Unrooted(const T &) */
+    Unrooted &operator=(T other) {
+        JS_ASSERT(other != UninitializedTag());
+        if (ptr_ == UninitializedTag())
+            EnterAssertNoGCScope();
+        ptr_ = other;
+        return *this;
+    }
+
+    operator T() const { return (ptr_ == UninitializedTag()) ? NULL : ptr_; }
+    T *operator&() { return &ptr_; }
+    const T operator->() const { JS_ASSERT(ptr_ != UninitializedTag()); return ptr_; }
+    bool operator==(const T &other) { return ptr_ == other; }
+    bool operator!=(const T &other) { return ptr_ != other; }
+
+  private:
+    /*
+     * The after-initialization constraint is to handle the case:
+     *
+     *     Unrooted<Foo> foo = js_NewFoo(cx);
+     *
+     * In this case, C++ may run the default constructor, then call MaybeGC,
+     * and finally call the assignment operator. We cannot handle this case by
+     * simply checking if the pointer is NULL, since that would disable the
+     * NoGCScope on assignment. Instead we tag the pointer when we should
+     * disable the LeaveNoGCScope.
+     */
+    static inline T UninitializedTag() { return reinterpret_cast<T>(1); };
+
+    T ptr_;
+};
+
+/*
+ * This macro simplifies declaration of the required matching raw-pointer for
+ * optimized builds and Unrooted<T> template for debug builds.
+ */
+# define ForwardDeclare(type) \
+    class type; \
+    typedef Unrooted<type*> Unrooted##type; \
+    typedef type * Raw##type
+
+template <typename T>
+T DropUnrooted(Unrooted<T> &unrooted)
+{
+    T rv = unrooted;
+    unrooted.drop();
+    return rv;
+}
+
+template <typename T>
+T DropUnrooted(T &unrooted)
+{
+    T rv = unrooted;
+    JS::PoisonPtr(&unrooted);
+    return rv;
+}
+
+template <>
+inline RawId DropUnrooted(RawId &id) { return id; }
+
+#else /* NDEBUG */
+
+/* In opt builds |UnrootedFoo| is a real |Foo*|. */
+# define ForwardDeclare(type) \
+    class type; \
+    typedef type * Unrooted##type; \
+    typedef type * Raw##type
+
+/*
+ * Note: we still define Unrooted<T> in optimized builds so that we do not need
+ * #ifdef DEBUG around every debug specialization. We just ensure that the
+ * class is never initialized by deleting its constructors.
+ */
+template <typename T>
+class Unrooted
+{
+  private:
+    Unrooted() MOZ_DELETE;
+    Unrooted(const Unrooted &) MOZ_DELETE;
+    ~Unrooted() MOZ_DELETE;
+};
+
+template <typename T>
+T DropUnrooted(T &unrooted) { return unrooted; }
+
+#endif /* DEBUG */
+
+template <typename T> template <typename S>
+inline
+Return<T>::Return(const Unrooted<S> &unrooted,
+                  typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy)
+    /* Note: |static_cast| acquires raw.ptr_ in DEBUG builds. */
+  : ptr_(static_cast<S>(unrooted))
+{
+    EnterAssertNoGCScope();
+}
+
+/*
  * By default, pointers 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<T *> { static ThingRootKind rootKind() { return T::rootKind(); } };
+struct RootKind<T *>
+{
+    static ThingRootKind rootKind() { return T::rootKind(); }
+};
 
 template <typename T>
 struct RootMethods<T *>
 {
     static T *initial() { return NULL; }
     static ThingRootKind kind() { return RootKind<T *>::rootKind(); }
     static bool poisoned(T *v) { return IsPoisonedPtr(v); }
 };
 
-template <typename T>
-class RootedBase {};
-
 /*
  * Local variable of type T whose value is always rooted. This is typically
  * used for local variables, or for non-rooted values being passed to a
  * function that requires a handle, e.g. Foo(Root<T>(cx, x)).
  *
  * If you want to add additional methods to Rooted for a specific
  * specialization, define a RootedBase<T> specialization containing them.
  */
 template <typename T>
 class Rooted : public RootedBase<T>
 {
-    void init(JSContext *cxArg)
-    {
+    void init(JSContext *cxArg) {
 #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
         ContextFriendFields *cx = ContextFriendFields::get(cxArg);
         commonInit(cx->thingGCRooters);
 #endif
     }
 
-    void init(JSRuntime *rtArg)
-    {
+    void init(JSRuntime *rtArg) {
 #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
         PerThreadDataFriendFields *pt = PerThreadDataFriendFields::getMainThread(rtArg);
         commonInit(pt->thingGCRooters);
 #endif
     }
 
-    void init(js::PerThreadData *ptArg)
-    {
+    void init(js::PerThreadData *ptArg) {
 #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
         PerThreadDataFriendFields *pt = PerThreadDataFriendFields::get(ptArg);
         commonInit(pt->thingGCRooters);
 #endif
     }
 
   public:
     Rooted(JSRuntime *rt
@@ -601,16 +853,25 @@ class Rooted : public RootedBase<T>
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         init(pt);
     }
 
     template <typename S>
     Rooted(JSContext *cx, const Return<S> &initial
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : ptr(initial.unsafeGet())
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+        init(cx);
+    }
+
+    template <typename S>
+    Rooted(JSContext *cx, const Unrooted<S> &initial
+           MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : ptr(initial.ptr_)
 #if defined(JSGC_ROOT_ANALYSIS)
       , scanned(false)
 #endif
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         init(cx);
     }
@@ -619,52 +880,48 @@ class Rooted : public RootedBase<T>
     Rooted(js::PerThreadData *pt, const Return<S> &initial
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : ptr(initial.ptr_)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         init(pt);
     }
 
-    ~Rooted()
-    {
+    ~Rooted() {
 #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
         JS_ASSERT(*stack == this);
         *stack = prev;
 #endif
     }
 
 #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
     Rooted<T> *previous() { return prev; }
 #endif
 
-    operator 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; }
+    operator 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)
-    {
+    T &operator=(T value) {
         JS_ASSERT(!RootMethods<T>::poisoned(value));
         ptr = value;
         return ptr;
     }
 
-    T & operator =(const Rooted &value)
-    {
+    T &operator=(const Rooted &value) {
         ptr = value;
         return ptr;
     }
 
     template <typename S>
-    T & operator =(const Return<S> &value)
-    {
-        ptr = value.ptr_;
+    T &operator=(const Return<S> &value) {
+        ptr = value.unsafeGet();
         return ptr;
     }
 
   private:
     void commonInit(Rooted<void*> **thingGCRooters) {
 #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
         ThingRootKind kind = RootMethods<T>::kind();
         this->stack = reinterpret_cast<Rooted<T>**>(&thingGCRooters[kind]);
@@ -698,16 +955,28 @@ class Rooted<JSStableString *>;
 
 template <typename T>
 bool
 Return<T>::operator==(const Rooted<T> &other)
 {
     return ptr_ == other.get();
 }
 
+#ifdef DEBUG
+template <typename T> template <typename S>
+inline
+Unrooted<T>::Unrooted(Rooted<S> &root,
+            typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy)
+  : ptr_(root.get())
+{
+    JS_ASSERT(ptr_ != UninitializedTag());
+    EnterAssertNoGCScope();
+}
+#endif /* DEBUG */
+
 typedef Rooted<JSObject*>    RootedObject;
 typedef Rooted<JSFunction*>  RootedFunction;
 typedef Rooted<JSScript*>    RootedScript;
 typedef Rooted<JSString*>    RootedString;
 typedef Rooted<jsid>         RootedId;
 typedef Rooted<Value>        RootedValue;
 
 /*
@@ -720,36 +989,34 @@ class SkipRoot
 {
 #if defined(DEBUG) && defined(JS_GC_ZEAL) && defined(JSGC_ROOT_ANALYSIS) && !defined(JS_THREADSAFE)
 
     SkipRoot **stack, *prev;
     const uint8_t *start;
     const uint8_t *end;
 
     template <typename T>
-    void init(ContextFriendFields *cx, const T *ptr, size_t count)
-    {
+    void init(ContextFriendFields *cx, const T *ptr, size_t count) {
         this->stack = &cx->skipGCRooters;
         this->prev = *stack;
         *stack = this;
         this->start = (const uint8_t *) ptr;
         this->end = this->start + (sizeof(T) * count);
     }
 
   public:
     template <typename T>
     SkipRoot(JSContext *cx, const T *ptr, size_t count = 1
              JS_GUARD_OBJECT_NOTIFIER_PARAM)
     {
         init(ContextFriendFields::get(cx), ptr, count);
         JS_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
-    ~SkipRoot()
-    {
+    ~SkipRoot() {
         JS_ASSERT(*stack == this);
         *stack = prev;
     }
 
     SkipRoot *previous() { return prev; }
 
     bool contains(const uint8_t *v, size_t len) {
         return v >= start && v + len <= end;
@@ -769,33 +1036,33 @@ class SkipRoot
 
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 } /* namespace js */
 
 namespace JS {
 
-template<typename T> template <typename S>
+template <typename T> template <typename S>
 inline
 Handle<T>::Handle(js::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>
+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> template <typename S>
+template <typename T> template <typename S>
 inline
 MutableHandle<T>::MutableHandle(js::Rooted<S> *root,
                                 typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy)
 {
     ptr = root->address();
 }
 
 /*
@@ -859,19 +1126,17 @@ inline void MaybeCheckStackRoots(JSConte
 namespace gc {
 struct Cell;
 } /* namespace gc */
 
 /* Base class for automatic read-only object rooting during compilation. */
 class CompilerRootNode
 {
   protected:
-    CompilerRootNode(js::gc::Cell *ptr)
-      : next(NULL), ptr(ptr)
-    { }
+    CompilerRootNode(js::gc::Cell *ptr) : next(NULL), ptr(ptr) {}
 
   public:
     void **address() { return (void **)&ptr; }
 
   public:
     CompilerRootNode *next;
 
   protected:
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -913,25 +913,25 @@ js::IncrementalReferenceBarrier(void *pt
 
     gc::Cell *cell = static_cast<gc::Cell *>(ptr);
     JS_ASSERT(!cell->compartment()->rt->isHeapBusy());
 
     AutoMarkInDeadCompartment amn(cell->compartment());
 
     uint32_t kind = gc::GetGCThingTraceKind(ptr);
     if (kind == JSTRACE_OBJECT)
-        JSObject::writeBarrierPre((JSObject *) ptr);
+        JSObject::writeBarrierPre(reinterpret_cast<RawObject>(ptr));
     else if (kind == JSTRACE_STRING)
-        JSString::writeBarrierPre((JSString *) ptr);
+        JSString::writeBarrierPre(reinterpret_cast<RawString>(ptr));
     else if (kind == JSTRACE_SCRIPT)
-        JSScript::writeBarrierPre((JSScript *) ptr);
+        JSScript::writeBarrierPre(reinterpret_cast<RawScript>(ptr));
     else if (kind == JSTRACE_SHAPE)
         Shape::writeBarrierPre((Shape *) ptr);
     else if (kind == JSTRACE_BASE_SHAPE)
-        BaseShape::writeBarrierPre((BaseShape *) ptr);
+        BaseShape::writeBarrierPre(reinterpret_cast<RawBaseShape>(ptr));
     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/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -303,28 +303,28 @@ struct TypeObject {
 };
 
 struct BaseShape {
     js::Class   *clasp;
     JSObject    *parent;
 };
 
 struct Shape {
-    BaseShape   *base;
-    jsid        _1;
-    uint32_t    slotInfo;
+    shadow::BaseShape *base;
+    jsid              _1;
+    uint32_t          slotInfo;
 
     static const uint32_t FIXED_SLOTS_SHIFT = 27;
 };
 
 struct Object {
-    Shape       *shape;
-    TypeObject  *type;
-    js::Value   *slots;
-    js::Value   *_1;
+    shadow::Shape      *shape;
+    shadow::TypeObject *type;
+    js::Value          *slots;
+    js::Value          *_1;
 
     size_t numFixedSlots() const { return shape->slotInfo >> Shape::FIXED_SLOTS_SHIFT; }
     Value *fixedSlots() const {
         return (Value *)(uintptr_t(this) + sizeof(shadow::Object));
     }
 
     js::Value &slotRef(size_t slot) const {
         size_t nfixed = numFixedSlots();
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -986,17 +986,17 @@ JSFunction::initBoundFunction(JSContext 
      * value and arguments count.
      */
     if (!self->toDictionaryMode(cx))
         return false;
 
     if (!self->setFlag(cx, BaseShape::BOUND_FUNCTION))
         return false;
 
-    if (!self->setSlotSpan(cx, BOUND_FUNCTION_RESERVED_SLOTS + argslen))
+    if (!JSObject::setSlotSpan(cx, self, BOUND_FUNCTION_RESERVED_SLOTS + argslen))
         return false;
 
     self->setSlot(JSSLOT_BOUND_FUNCTION_THIS, thisArg);
     self->setSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT, PrivateUint32Value(argslen));
 
     self->initSlotRange(BOUND_FUNCTION_RESERVED_SLOTS, args, argslen);
 
     return true;
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -553,69 +553,61 @@ TryNewGCThing(JSContext *cx, js::gc::All
 }
 
 } /* namespace gc */
 } /* namespace js */
 
 inline JSObject *
 js_NewGCObject(JSContext *cx, js::gc::AllocKind kind)
 {
-    AssertCanGC();
     JS_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST);
     return js::gc::NewGCThing<JSObject>(cx, kind, js::gc::Arena::thingSize(kind));
 }
 
 inline JSObject *
 js_TryNewGCObject(JSContext *cx, js::gc::AllocKind kind)
 {
-    AssertCanGC();
     JS_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST);
     return js::gc::TryNewGCThing<JSObject>(cx, kind, js::gc::Arena::thingSize(kind));
 }
 
 inline JSString *
 js_NewGCString(JSContext *cx)
 {
-    AssertCanGC();
     return js::gc::NewGCThing<JSString>(cx, js::gc::FINALIZE_STRING, sizeof(JSString));
 }
 
 inline JSShortString *
 js_NewGCShortString(JSContext *cx)
 {
-    AssertCanGC();
     return js::gc::NewGCThing<JSShortString>(cx, js::gc::FINALIZE_SHORT_STRING, sizeof(JSShortString));
 }
 
 inline JSExternalString *
 js_NewGCExternalString(JSContext *cx)
 {
-    AssertCanGC();
     return js::gc::NewGCThing<JSExternalString>(cx, js::gc::FINALIZE_EXTERNAL_STRING,
                                                 sizeof(JSExternalString));
 }
 
 inline JSScript *
 js_NewGCScript(JSContext *cx)
 {
-    AssertCanGC();
     return js::gc::NewGCThing<JSScript>(cx, js::gc::FINALIZE_SCRIPT, sizeof(JSScript));
 }
 
 inline js::Shape *
 js_NewGCShape(JSContext *cx)
 {
-    AssertCanGC();
     return js::gc::NewGCThing<js::Shape>(cx, js::gc::FINALIZE_SHAPE, sizeof(js::Shape));
 }
 
-inline js::BaseShape *
+inline js::Return<js::BaseShape*>
 js_NewGCBaseShape(JSContext *cx)
 {
-    AssertCanGC();
     return js::gc::NewGCThing<js::BaseShape>(cx, js::gc::FINALIZE_BASE_SHAPE, sizeof(js::BaseShape));
 }
 
 #if JS_HAS_XML_SUPPORT
 extern JSXML *
 js_NewGCXML(JSContext *cx);
 #endif
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3251,32 +3251,29 @@ JSObject::setLastProperty(JSContext *cx,
 
     if (!updateSlotsForSpan(cx, obj, oldSpan, newSpan))
         return false;
 
     obj->shape_ = shape;
     return true;
 }
 
-bool
-JSObject::setSlotSpan(JSContext *cx, uint32_t span)
+/* static */ bool
+JSObject::setSlotSpan(JSContext *cx, HandleObject obj, uint32_t span)
 {
-    JS_ASSERT(inDictionaryMode());
-    js::BaseShape *base = lastProperty()->base();
-
-    size_t oldSpan = base->slotSpan();
-
+    JS_ASSERT(obj->inDictionaryMode());
+
+    size_t oldSpan = obj->lastProperty()->base()->slotSpan();
     if (oldSpan == span)
         return true;
 
-    RootedObject self(cx, this);
-    if (!JSObject::updateSlotsForSpan(cx, self, oldSpan, span))
+    if (!JSObject::updateSlotsForSpan(cx, obj, oldSpan, span))
         return false;
 
-    base->setSlotSpan(span);
+    obj->lastProperty()->base()->setSlotSpan(span);
     return true;
 }
 
 /* static */ bool
 JSObject::growSlots(JSContext *cx, HandleObject obj, uint32_t oldCount, uint32_t newCount)
 {
     JS_ASSERT(newCount > oldCount);
     JS_ASSERT(newCount >= SLOT_CAPACITY_MIN);
@@ -3723,17 +3720,18 @@ JSObject::allocSlot(JSContext *cx, uint3
 
     if (slot >= SHAPE_MAXIMUM_SLOT) {
         js_ReportOutOfMemory(cx);
         return false;
     }
 
     *slotp = slot;
 
-    if (inDictionaryMode() && !setSlotSpan(cx, slot + 1))
+    RootedObject self(cx, this);
+    if (inDictionaryMode() && !setSlotSpan(cx, self, slot + 1))
         return false;
 
     return true;
 }
 
 void
 JSObject::freeSlot(uint32_t slot)
 {
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -306,17 +306,17 @@ struct JSObject : public js::ObjectImpl
      */
     inline void removeLastProperty(JSContext *cx);
     inline bool canRemoveLastProperty();
 
     /*
      * Update the slot span directly for a dictionary object, and allocate
      * slots to cover the new span if necessary.
      */
-    bool setSlotSpan(JSContext *cx, uint32_t span);
+    static bool setSlotSpan(JSContext *cx, JS::HandleObject obj, uint32_t span);
 
     /* Upper bound on the number of elements in an object. */
     static const uint32_t NELEMENTS_LIMIT = JS_BIT(28);
 
   public:
     inline bool setDelegate(JSContext *cx);
 
     inline bool isBoundFunction() const;
--- a/js/src/jspropertytree.cpp
+++ b/js/src/jspropertytree.cpp
@@ -125,16 +125,18 @@ Shape::removeChild(Shape *child)
         kidp->setShape(otherChild);
         js_delete(hash);
     }
 }
 
 Shape *
 PropertyTree::getChild(JSContext *cx, Shape *parent_, uint32_t nfixed, const StackShape &child)
 {
+    AssertCanGC();
+
     Shape *shape = NULL;
 
     JS_ASSERT(parent_);
 
     /*
      * The property tree has extremely low fan-out below its root in
      * popular embeddings with real-world workloads. Patterns such as
      * defining closures that capture a constructor's environment as
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -68,77 +68,75 @@ ShapeTable::init(JSRuntime *rt, Shape *l
          * (nearest to lastProp) must win. See bug 600067.
          */
         if (!SHAPE_FETCH(spp))
             SHAPE_STORE_PRESERVING_COLLISION(spp, &shape);
     }
     return true;
 }
 
-bool
-Shape::makeOwnBaseShape(JSContext *cx)
+/* static */ bool
+Shape::makeOwnBaseShape(JSContext *cx, HandleShape shape)
 {
-    JS_ASSERT(!base()->isOwned());
-    assertSameCompartment(cx, compartment());
+    JS_ASSERT(!shape->base()->isOwned());
+    assertSameCompartment(cx, shape->compartment());
 
-    RootedShape self(cx, this);
-
-    BaseShape *nbase = js_NewGCBaseShape(cx);
+    UnrootedBaseShape nbase = js_NewGCBaseShape(cx);
     if (!nbase)
         return false;
 
-    new (nbase) BaseShape(StackBaseShape(self));
-    nbase->setOwned(self->base()->toUnowned());
+    new (nbase) BaseShape(StackBaseShape(shape));
+    nbase->setOwned(shape->base()->toUnowned());
 
-    self->base_ = nbase;
+    shape->base_ = nbase;
 
     return true;
 }
 
 void
 Shape::handoffTableTo(Shape *shape)
 {
+    AutoAssertNoGC nogc;
     JS_ASSERT(inDictionary() && shape->inDictionary());
 
     if (this == shape)
         return;
 
     JS_ASSERT(base()->isOwned() && !shape->base()->isOwned());
 
-    BaseShape *nbase = base();
+    UnrootedBaseShape nbase = base();
 
     JS_ASSERT_IF(shape->hasSlot(), nbase->slotSpan() > shape->slot());
 
     this->base_ = nbase->baseUnowned();
     nbase->adoptUnowned(shape->base()->toUnowned());
 
     shape->base_ = nbase;
 }
 
-bool
-Shape::hashify(JSContext *cx)
+/* static */ bool
+Shape::hashify(JSContext *cx, HandleShape shape)
 {
-    JS_ASSERT(!hasTable());
+    AssertCanGC();
+    JS_ASSERT(!shape->hasTable());
 
-    RootedShape self(cx, this);
-
-    if (!ensureOwnBaseShape(cx))
+    if (!shape->ensureOwnBaseShape(cx))
         return false;
 
     JSRuntime *rt = cx->runtime;
-    ShapeTable *table = rt->new_<ShapeTable>(self->entryCount());
+    ShapeTable *table = rt->new_<ShapeTable>(shape->entryCount());
     if (!table)
         return false;
 
-    if (!table->init(rt, self)) {
+    if (!table->init(rt, shape)) {
         js_free(table);
         return false;
     }
 
-    self->base()->setTable(table);
+    shape->base()->setTable(table);
     return true;
 }
 
 /*
  * Double hashing needs the second hash code to be relatively prime to table
  * size, so we simply make hash2 odd.
  */
 #define HASH1(hash0,shift)      ((hash0) >> (shift))
@@ -273,16 +271,17 @@ ShapeTable::grow(JSContext *cx)
         return false;
     }
     return true;
 }
 
 Shape *
 Shape::getChildBinding(JSContext *cx, const StackShape &child)
 {
+    AssertCanGC();
     JS_ASSERT(!inDictionary());
 
     /* Try to allocate all slots inline. */
     uint32_t slots = child.slotSpan();
     gc::AllocKind kind = gc::GetGCObjectKind(slots);
     uint32_t nfixed = gc::GetGCKindSlots(kind);
 
     return cx->propertyTree().getChild(cx, this, nfixed, child);
@@ -299,22 +298,24 @@ Shape::replaceLastProperty(JSContext *cx
     if (!shape->parent) {
         /* Treat as resetting the initial property of the shape hierarchy. */
         AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
         return EmptyShape::getInitialShape(cx, base.clasp, proto,
                                            base.parent, kind,
                                            base.flags & BaseShape::OBJECT_FLAG_MASK);
     }
 
-    UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
-    if (!nbase)
-        return NULL;
+    StackShape child(shape);
+    {
+        UnrootedUnownedBaseShape nbase = BaseShape::getUnowned(cx, base);
+        if (!nbase)
+            return NULL;
 
-    StackShape child(shape);
-    child.base = nbase;
+        child.base = nbase;
+    }
 
     return cx->propertyTree().getChild(cx, shape->parent, shape->numFixedSlots(), child);
 }
 
 /*
  * Get or create a property-tree or dictionary child property of |parent|,
  * which must be lastProperty() if inDictionaryMode(), else parent must be
  * one of lastProperty() or lastProperty()->parent.
@@ -349,17 +350,17 @@ JSObject::getChildProperty(JSContext *cx
 
     if (inDictionaryMode()) {
         JS_ASSERT(parent == lastProperty());
         StackShape::AutoRooter childRoot(cx, &child);
         shape = js_NewGCShape(cx);
         if (!shape)
             return NULL;
         if (child.hasSlot() && child.slot() >= self->lastProperty()->base()->slotSpan()) {
-            if (!self->setSlotSpan(cx, child.slot() + 1))
+            if (!JSObject::setSlotSpan(cx, self, child.slot() + 1))
                 return NULL;
         }
         shape->initDictionaryShape(child, self->numFixedSlots(), &self->shape_);
     } else {
         shape = cx->propertyTree().getChild(cx, parent, self->numFixedSlots(), child);
         if (!shape)
             return NULL;
         //JS_ASSERT(shape->parent == parent);
@@ -409,17 +410,17 @@ JSObject::toDictionaryMode(JSContext *cx
         StackShape child(shape);
         dprop->initDictionaryShape(child, self->numFixedSlots(), listp);
 
         JS_ASSERT(!dprop->hasTable());
         dictionaryShape = dprop;
         shape = shape->previous();
     }
 
-    if (!root->hashify(cx)) {
+    if (!Shape::hashify(cx, root)) {
         js_ReportOutOfMemory(cx);
         return false;
     }
 
     JS_ASSERT((Shape **) root->listp == root.address());
     root->listp = &self->shape_;
     self->shape_ = root;
 
@@ -478,16 +479,17 @@ JSObject::addProperty(JSContext *cx, jsi
 
 Shape *
 JSObject::addPropertyInternal(JSContext *cx, jsid id_,
                               PropertyOp getter, StrictPropertyOp setter,
                               uint32_t slot, unsigned attrs,
                               unsigned flags, int shortid, Shape **spp,
                               bool allowDictionary)
 {
+    AssertCanGC();
     JS_ASSERT_IF(!allowDictionary, !inDictionaryMode());
 
     RootedId id(cx, id_);
     RootedObject self(cx, this);
 
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
 
     ShapeTable *table = NULL;
@@ -518,30 +520,32 @@ JSObject::addPropertyInternal(JSContext 
 
     /* Find or create a property tree node labeled by our arguments. */
     Shape *shape;
     {
         shape = self->lastProperty();
 
         uint32_t index;
         bool indexed = js_IdIsIndex(id, &index);
-        UnownedBaseShape *nbase;
+
+        UnrootedUnownedBaseShape nbase;
         if (shape->base()->matchesGetterSetter(getter, setter) && !indexed) {
             nbase = shape->base()->unowned();
         } else {
             StackBaseShape base(shape->base());
             base.updateGetterSetter(attrs, getter, setter);
             if (indexed)
                 base.flags |= BaseShape::INDEXED;
             nbase = BaseShape::getUnowned(cx, base);
             if (!nbase)
                 return NULL;
         }
 
         StackShape child(nbase, id, slot, self->numFixedSlots(), attrs, flags, shortid);
+        DropUnrooted(nbase);
         shape = self->getChildProperty(cx, self->lastProperty(), child);
     }
 
     if (shape) {
         JS_ASSERT(shape == self->lastProperty());
 
         if (table) {
             /* Store the tree node pointer in the table entry for id. */
@@ -696,24 +700,26 @@ JSObject::putProperty(JSContext *cx, jsi
         shape->shortid_ = int16_t(shortid);
     } else {
         /*
          * Updating the last property in a non-dictionary-mode object. Find an
          * alternate shared child of the last property's previous shape.
          */
         StackBaseShape base(self->lastProperty()->base());
         base.updateGetterSetter(attrs, getter, setter);
-        UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
+
+        UnrootedUnownedBaseShape nbase = BaseShape::getUnowned(cx, base);
         if (!nbase)
             return NULL;
 
         JS_ASSERT(shape == self->lastProperty());
 
         /* Find or create a property tree node labeled by our arguments. */
         StackShape child(nbase, id, slot, self->numFixedSlots(), attrs, flags, shortid);
+        DropUnrooted(nbase);
         Shape *newShape = self->getChildProperty(cx, shape->parent, child);
 
         if (!newShape) {
             self->checkShapeConsistency();
             return NULL;
         }
 
         shape = newShape;
@@ -816,17 +822,17 @@ JSObject::removeProperty(JSContext *cx, 
              * Get an up to date unowned base shape for the new last property
              * when removing the dictionary's last property. Information in
              * base shapes for non-last properties may be out of sync with the
              * object's state.
              */
             RootedShape previous(cx, self->lastProperty()->parent);
             StackBaseShape base(self->lastProperty()->base());
             base.updateGetterSetter(previous->attrs, previous->getter(), previous->setter());
-            BaseShape *nbase = BaseShape::getUnowned(cx, base);
+            UnrootedBaseShape nbase = BaseShape::getUnowned(cx, base);
             if (!nbase)
                 return false;
             previous->base_ = nbase;
         }
     }
 
     /* If shape has a slot, free its slot number. */
     if (shape->hasSlot()) {
@@ -997,17 +1003,17 @@ JSObject::clearParent(JSContext *cx, Han
 JSObject::setParent(JSContext *cx, HandleObject obj, HandleObject parent)
 {
     if (parent && !parent->setDelegate(cx))
         return false;
 
     if (obj->inDictionaryMode()) {
         StackBaseShape base(obj->lastProperty());
         base.parent = parent;
-        UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
+        UnrootedUnownedBaseShape nbase = BaseShape::getUnowned(cx, base);
         if (!nbase)
             return false;
 
         obj->lastProperty()->base()->adoptUnowned(nbase);
         return true;
     }
 
     Shape *newShape = Shape::setObjectParent(cx, parent, obj->getTaggedProto(), obj->shape_);
@@ -1061,17 +1067,17 @@ JSObject::setFlag(JSContext *cx, /*BaseS
 
     RootedObject self(cx, this);
 
     if (inDictionaryMode()) {
         if (generateShape == GENERATE_SHAPE && !generateOwnShape(cx))
             return false;
         StackBaseShape base(self->lastProperty());
         base.flags |= flag;
-        UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
+        UnrootedUnownedBaseShape nbase = BaseShape::getUnowned(cx, base);
         if (!nbase)
             return false;
 
         self->lastProperty()->base()->adoptUnowned(nbase);
         return true;
     }
 
     Shape *newShape = Shape::setObjectFlag(cx, flag, getTaggedProto(), lastProperty());
@@ -1101,61 +1107,62 @@ StackBaseShape::hash(const StackBaseShap
     hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(base->clasp) >> 3);
     hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(base->parent) >> 3);
     hash = JS_ROTATE_LEFT32(hash, 4) ^ uintptr_t(base->rawGetter);
     hash = JS_ROTATE_LEFT32(hash, 4) ^ uintptr_t(base->rawSetter);
     return hash;
 }
 
 /* static */ inline bool
-StackBaseShape::match(UnownedBaseShape *key, const StackBaseShape *lookup)
+StackBaseShape::match(RawUnownedBaseShape key, const StackBaseShape *lookup)
 {
     return key->flags == lookup->flags
         && key->clasp == lookup->clasp
         && key->parent == lookup->parent
         && key->rawGetter == lookup->rawGetter
         && key->rawSetter == lookup->rawSetter;
 }
 
-/* static */ UnownedBaseShape *
+/* static */ UnownedBaseShape*
 BaseShape::getUnowned(JSContext *cx, const StackBaseShape &base)
 {
     BaseShapeSet &table = cx->compartment->baseShapes;
 
     if (!table.initialized() && !table.init())
         return NULL;
 
     BaseShapeSet::AddPtr p = table.lookupForAdd(&base);
 
     if (p)
         return *p;
 
     StackBaseShape::AutoRooter root(cx, &base);
 
-    BaseShape *nbase_ = js_NewGCBaseShape(cx);
+    UnrootedBaseShape nbase_ = js_NewGCBaseShape(cx);
     if (!nbase_)
         return NULL;
+
     new (nbase_) BaseShape(base);
 
-    UnownedBaseShape *nbase = static_cast<UnownedBaseShape *>(nbase_);
+    UnrootedUnownedBaseShape nbase = static_cast<UnrootedUnownedBaseShape>(nbase_);
 
     if (!table.relookupOrAdd(p, &base, nbase))
         return NULL;
 
     return nbase;
 }
 
 void
 JSCompartment::sweepBaseShapeTable()
 {
     gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP_TABLES_BASE_SHAPE);
 
     if (baseShapes.initialized()) {
         for (BaseShapeSet::Enum e(baseShapes); !e.empty(); e.popFront()) {
-            UnownedBaseShape *base = e.front();
+            RawUnownedBaseShape base = e.front();
             if (IsBaseShapeAboutToBeFinalized(&base))
                 e.removeFront();
         }
     }
 }
 
 void
 BaseShape::finalize(FreeOp *fop)
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -2,30 +2,26 @@
  * vim: set ts=8 sw=4 et tw=99:
  *
  * 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 jsscope_h___
 #define jsscope_h___
-/*
- * JS symbol tables.
- */
-#ifdef DEBUG
-#include <stdio.h>
-#endif
+
+#include "mozilla/Attributes.h"
 
 #include "jsobj.h"
 #include "jspropertytree.h"
 #include "jstypes.h"
 
 #include "js/HashTable.h"
+#include "gc/Heap.h"
 #include "gc/Root.h"
-#include "mozilla/Attributes.h"
 
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4800)
 #pragma warning(push)
 #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
 #endif
 
@@ -225,17 +221,19 @@ class PropertyTree;
  * may change when the object has an established property lineage. On such
  * changes the entire property lineage is not updated, but rather only the
  * last property (and its base shape). This works because only the object's
  * last property is used to query information about the object. Care must be
  * taken to call JSObject::canRemoveLastProperty when unwinding an object to
  * an earlier property, however.
  */
 
-class UnownedBaseShape;
+ForwardDeclare(UnownedBaseShape);
+ForwardDeclare(BaseShape);
+ForwardDeclare(Shape);
 
 class BaseShape : public js::gc::Cell
 {
   public:
     friend struct Shape;
     friend struct StackBaseShape;
     friend struct StackShape;
 
@@ -306,18 +304,18 @@ class BaseShape : public js::gc::Cell
 
     inline BaseShape &operator=(const BaseShape &other);
 
     bool isOwned() const { return !!(flags & OWNED_SHAPE); }
 
     inline bool matchesGetterSetter(PropertyOp rawGetter,
                                     StrictPropertyOp rawSetter) const;
 
-    inline void adoptUnowned(UnownedBaseShape *other);
-    inline void setOwned(UnownedBaseShape *unowned);
+    inline void adoptUnowned(UnrootedUnownedBaseShape other);
+    inline void setOwned(UnrootedUnownedBaseShape unowned);
 
     JSObject *getObjectParent() const { return parent; }
     uint32_t getObjectFlags() const { return flags & OBJECT_FLAG_MASK; }
 
     bool hasGetterObject() const { return !!(flags & HAS_GETTER_OBJECT); }
     JSObject *getterObject() const { JS_ASSERT(hasGetterObject()); return getterObj; }
 
     bool hasSetterObject() const { return !!(flags & HAS_SETTER_OBJECT); }
@@ -326,81 +324,81 @@ class BaseShape : public js::gc::Cell
     bool hasTable() const { JS_ASSERT_IF(table_, isOwned()); return table_ != NULL; }
     ShapeTable &table() const { JS_ASSERT(table_ && isOwned()); return *table_; }
     void setTable(ShapeTable *table) { JS_ASSERT(isOwned()); table_ = table; }
 
     uint32_t slotSpan() const { JS_ASSERT(isOwned()); return slotSpan_; }
     void setSlotSpan(uint32_t slotSpan) { JS_ASSERT(isOwned()); slotSpan_ = slotSpan; }
 
     /* Lookup base shapes from the compartment's baseShapes table. */
-    static UnownedBaseShape *getUnowned(JSContext *cx, const StackBaseShape &base);
+    static UnownedBaseShape* getUnowned(JSContext *cx, const StackBaseShape &base);
 
     /* Get the canonical base shape. */
-    inline UnownedBaseShape *unowned();
+    inline UnownedBaseShape* unowned();
 
     /* Get the canonical base shape for an owned one. */
-    inline UnownedBaseShape *baseUnowned();
+    inline UnownedBaseShape* baseUnowned();
 
     /* Get the canonical base shape for an unowned one (i.e. identity). */
-    inline UnownedBaseShape *toUnowned();
+    inline UnownedBaseShape* toUnowned();
 
     /* Check that an owned base shape is consistent with its unowned base. */
     inline void assertConsistency();
 
     /* For JIT usage */
     static inline size_t offsetOfClass() { return offsetof(BaseShape, clasp); }
     static inline size_t offsetOfParent() { return offsetof(BaseShape, parent); }
     static inline size_t offsetOfFlags() { return offsetof(BaseShape, flags); }
 
-    static inline void writeBarrierPre(BaseShape *shape);
-    static inline void writeBarrierPost(BaseShape *shape, void *addr);
-    static inline void readBarrier(BaseShape *shape);
+    static inline void writeBarrierPre(RawBaseShape shape);
+    static inline void writeBarrierPost(RawBaseShape shape, void *addr);
+    static inline void readBarrier(RawBaseShape shape);
 
     static inline ThingRootKind rootKind() { return THING_ROOT_BASE_SHAPE; }
 
     inline void markChildren(JSTracer *trc);
 
   private:
     static void staticAsserts() {
         JS_STATIC_ASSERT(offsetof(BaseShape, clasp) == offsetof(js::shadow::BaseShape, clasp));
     }
 };
 
 class UnownedBaseShape : public BaseShape {};
 
-UnownedBaseShape *
+UnownedBaseShape*
 BaseShape::unowned()
 {
     return isOwned() ? baseUnowned() : toUnowned();
 }
 
-UnownedBaseShape *
+UnownedBaseShape*
 BaseShape::toUnowned()
 {
-    JS_ASSERT(!isOwned() && !unowned_); return static_cast<UnownedBaseShape *>(this);
+    JS_ASSERT(!isOwned() && !unowned_); return static_cast<RawUnownedBaseShape>(this);
 }
 
-UnownedBaseShape *
+UnownedBaseShape*
 BaseShape::baseUnowned()
 {
     JS_ASSERT(isOwned() && unowned_); return unowned_;
 }
 
 /* Entries for the per-compartment baseShapes set of unowned base shapes. */
 struct StackBaseShape
 {
     typedef const StackBaseShape *Lookup;
 
     uint32_t flags;
     Class *clasp;
     JSObject *parent;
     PropertyOp rawGetter;
     StrictPropertyOp rawSetter;
 
-    StackBaseShape(BaseShape *base)
+    StackBaseShape(UnrootedBaseShape base)
       : flags(base->flags & BaseShape::OBJECT_FLAG_MASK),
         clasp(base->clasp),
         parent(base->parent),
         rawGetter(NULL),
         rawSetter(NULL)
     {}
 
     StackBaseShape(Class *clasp, JSObject *parent, uint32_t objectFlags)
@@ -413,17 +411,17 @@ struct StackBaseShape
 
     inline StackBaseShape(Shape *shape);
 
     inline void updateGetterSetter(uint8_t attrs,
                                    PropertyOp rawGetter,
                                    StrictPropertyOp rawSetter);
 
     static inline HashNumber hash(const StackBaseShape *lookup);
-    static inline bool match(UnownedBaseShape *key, const StackBaseShape *lookup);
+    static inline bool match(RawUnownedBaseShape key, const StackBaseShape *lookup);
 
     class AutoRooter : private AutoGCRooter
     {
       public:
         explicit AutoRooter(JSContext *cx, const StackBaseShape *base_
                             JS_GUARD_OBJECT_NOTIFIER_PARAM)
           : AutoGCRooter(cx, STACKBASESHAPE), base(base_), skip(cx, base_)
         {
@@ -512,28 +510,29 @@ struct Shape : public js::gc::Cell
                                     HeapPtrShape *dictp);
 
     Shape *getChildBinding(JSContext *cx, const StackShape &child);
 
     /* Replace the base shape of the last shape in a non-dictionary lineage with base. */
     static Shape *replaceLastProperty(JSContext *cx, const StackBaseShape &base,
                                       TaggedProto proto, Shape *shape);
 
-    bool hashify(JSContext *cx);
+    static bool hashify(JSContext *cx, HandleShape shape);
     void handoffTableTo(Shape *newShape);
 
     inline void setParent(js::Shape *p);
 
     bool ensureOwnBaseShape(JSContext *cx) {
         if (base()->isOwned())
             return true;
-        return makeOwnBaseShape(cx);
+        RootedShape self(cx, this);
+        return makeOwnBaseShape(cx, self);
     }
 
-    bool makeOwnBaseShape(JSContext *cx);
+    static bool makeOwnBaseShape(JSContext *cx, HandleShape shape);
 
   public:
     bool hasTable() const { return base()->hasTable(); }
     js::ShapeTable &table() const { return base()->table(); }
 
     void sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf,
                              size_t *propTableSize, size_t *kidsSize) const {
         *propTableSize = hasTable() ? table().sizeOfIncludingThis(mallocSizeOf) : 0;
@@ -624,17 +623,17 @@ struct Shape : public js::gc::Cell
 
         UNUSED_BITS     = 0x3C
     };
 
     /* Get a shape identical to this one, without parent/kids information. */
     Shape(const StackShape &other, uint32_t nfixed);
 
     /* Used by EmptyShape (see jsscopeinlines.h). */
-    Shape(UnownedBaseShape *base, uint32_t nfixed);
+    Shape(UnrootedUnownedBaseShape base, uint32_t nfixed);
 
     /* Copy constructor disabled, to avoid misuse of the above form. */
     Shape(const Shape &other) MOZ_DELETE;
 
     /*
      * Whether this shape has a valid slot value. This may be true even if
      * !hasSlot() (see SlotInfo comment above), and may be false even if
      * hasSlot() if the shape is being constructed and has not had a slot
@@ -686,24 +685,24 @@ struct Shape : public js::gc::Cell
                ? ObjectValue(*base()->setterObj)
                : UndefinedValue();
     }
 
     void update(js::PropertyOp getter, js::StrictPropertyOp setter, uint8_t attrs);
 
     inline bool matches(const Shape *other) const;
     inline bool matches(const StackShape &other) const;
-    inline bool matchesParamsAfterId(BaseShape *base,
+    inline bool matchesParamsAfterId(UnrootedBaseShape base,
                                      uint32_t aslot, unsigned aattrs, unsigned aflags,
                                      int ashortid) const;
 
     bool get(JSContext* cx, HandleObject receiver, JSObject *obj, JSObject *pobj, MutableHandleValue vp);
     bool set(JSContext* cx, HandleObject obj, HandleObject receiver, bool strict, MutableHandleValue vp);
 
-    BaseShape *base() const { return base_; }
+    BaseShape* base() const { return base_; }
 
     bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; }
     uint32_t slot() const { JS_ASSERT(hasSlot() && !hasMissingSlot()); return maybeSlot(); }
     uint32_t maybeSlot() const { return slotInfo & SLOT_MASK; }
 
     bool isEmptyShape() const {
         JS_ASSERT_IF(JSID_IS_EMPTY(propid_), hasMissingSlot());
         return JSID_IS_EMPTY(propid_);
@@ -895,17 +894,17 @@ class AutoRooterGetterSetter
 
   private:
     mozilla::Maybe<Inner> inner;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 struct EmptyShape : public js::Shape
 {
-    EmptyShape(UnownedBaseShape *base, uint32_t nfixed);
+    EmptyShape(UnrootedUnownedBaseShape base, uint32_t nfixed);
 
     /*
      * Lookup an initial shape matching the given parameters, creating an empty
      * shape if none was found.
      */
     static Shape *getInitialShape(JSContext *cx, Class *clasp, TaggedProto proto,
                                   JSObject *parent, gc::AllocKind kind, uint32_t objectFlags = 0);
 
@@ -958,24 +957,25 @@ struct InitialShapeEntry
     static inline HashNumber hash(const Lookup &lookup);
     static inline bool match(const InitialShapeEntry &key, const Lookup &lookup);
 };
 
 typedef HashSet<InitialShapeEntry, InitialShapeEntry, SystemAllocPolicy> InitialShapeSet;
 
 struct StackShape
 {
-    UnownedBaseShape *base;
-    jsid             propid;
-    uint32_t         slot_;
-    uint8_t          attrs;
-    uint8_t          flags;
-    int16_t          shortid;
+    /* For performance, StackShape only roots when absolutely necessary. */
+    RawUnownedBaseShape base;
+    RawId               propid;
+    uint32_t            slot_;
+    uint8_t             attrs;
+    uint8_t             flags;
+    int16_t             shortid;
 
-    StackShape(UnownedBaseShape *base, jsid propid, uint32_t slot,
+    StackShape(UnrootedUnownedBaseShape base, jsid propid, uint32_t slot,
                uint32_t nfixed, unsigned attrs, unsigned flags, int shortid)
       : base(base),
         propid(propid),
         slot_(slot),
         attrs(uint8_t(attrs)),
         flags(uint8_t(flags)),
         shortid(int16_t(shortid))
     {
@@ -1076,17 +1076,17 @@ Shape::search(JSContext *cx, Shape *star
         Shape **spp = start->table().search(id, adding);
         return SHAPE_FETCH(spp);
     }
 
     if (start->numLinearSearches() == LINEAR_SEARCHES_MAX) {
         if (start->isBigEnoughForAShapeTable()) {
             RootedShape startRoot(cx, start);
             RootedId idRoot(cx, id);
-            if (startRoot->hashify(cx)) {
+            if (Shape::hashify(cx, startRoot)) {
                 Shape **spp = startRoot->table().search(idRoot, adding);
                 return SHAPE_FETCH(spp);
             }
             start = startRoot;
             id = idRoot;
         }
         /*
          * No table built -- there weren't enough entries, or OOM occurred.
--- a/js/src/jsscopeinlines.h
+++ b/js/src/jsscopeinlines.h
@@ -147,17 +147,17 @@ StackBaseShape::updateGetterSetter(uint8
         flags |= BaseShape::HAS_SETTER_OBJECT;
     }
 
     this->rawGetter = rawGetter;
     this->rawSetter = rawSetter;
 }
 
 inline void
-BaseShape::adoptUnowned(UnownedBaseShape *other)
+BaseShape::adoptUnowned(UnrootedUnownedBaseShape other)
 {
     /*
      * This is a base shape owned by a dictionary object, update it to reflect the
      * unowned base shape of a new last property.
      */
     JS_ASSERT(isOwned());
     mozilla::DebugOnly<uint32_t> flags = getObjectFlags();
     JS_ASSERT((flags & other->getObjectFlags()) == flags);
@@ -169,28 +169,28 @@ BaseShape::adoptUnowned(UnownedBaseShape
     setOwned(other);
     setTable(table);
     setSlotSpan(span);
 
     assertConsistency();
 }
 
 inline void
-BaseShape::setOwned(UnownedBaseShape *unowned)
+BaseShape::setOwned(UnrootedUnownedBaseShape unowned)
 {
     flags |= OWNED_SHAPE;
     this->unowned_ = unowned;
 }
 
 inline void
 BaseShape::assertConsistency()
 {
 #ifdef DEBUG
     if (isOwned()) {
-        UnownedBaseShape *unowned = baseUnowned();
+        UnrootedUnownedBaseShape unowned = baseUnowned();
         JS_ASSERT(hasGetterObject() == unowned->hasGetterObject());
         JS_ASSERT(hasSetterObject() == unowned->hasSetterObject());
         JS_ASSERT_IF(hasGetterObject(), getterObject() == unowned->getterObject());
         JS_ASSERT_IF(hasSetterObject(), setterObject() == unowned->setterObject());
         JS_ASSERT(getObjectParent() == unowned->getObjectParent());
         JS_ASSERT(getObjectFlags() == unowned->getObjectFlags());
     }
 #endif
@@ -205,17 +205,17 @@ Shape::Shape(const StackShape &other, ui
     flags(other.flags),
     shortid_(other.shortid),
     parent(NULL)
 {
     kids.setNull();
 }
 
 inline
-Shape::Shape(UnownedBaseShape *base, uint32_t nfixed)
+Shape::Shape(UnrootedUnownedBaseShape base, uint32_t nfixed)
   : base_(base),
     propid_(JSID_EMPTY),
     slotInfo(SHAPE_INVALID_SLOT | (nfixed << FIXED_SLOTS_SHIFT)),
     attrs(JSPROP_SHARED),
     flags(0),
     shortid_(0),
     parent(NULL)
 {
@@ -248,17 +248,17 @@ Shape::matches(const js::Shape *other) c
 inline bool
 Shape::matches(const StackShape &other) const
 {
     return propid_.get() == other.propid &&
            matchesParamsAfterId(other.base, other.slot_, other.attrs, other.flags, other.shortid);
 }
 
 inline bool
-Shape::matchesParamsAfterId(BaseShape *base, uint32_t aslot,
+Shape::matchesParamsAfterId(UnrootedBaseShape base, uint32_t aslot,
                             unsigned aattrs, unsigned aflags, int ashortid) const
 {
     return base->unowned() == this->base()->unowned() &&
            maybeSlot() == aslot &&
            attrs == aattrs &&
            ((flags ^ aflags) & PUBLIC_FLAGS) == 0 &&
            shortid_ == ashortid;
 }
@@ -386,17 +386,17 @@ Shape::initDictionaryShape(const StackSh
     new (this) Shape(child, nfixed);
     this->flags |= IN_DICTIONARY;
 
     this->listp = NULL;
     insertIntoDictionary(dictp);
 }
 
 inline
-EmptyShape::EmptyShape(UnownedBaseShape *base, uint32_t nfixed)
+EmptyShape::EmptyShape(UnrootedUnownedBaseShape base, uint32_t nfixed)
   : js::Shape(base, nfixed)
 {
     /* Only empty shapes can be NON_NATIVE. */
     if (!getObjectClass()->isNative())
         flags |= NON_NATIVE;
 }
 
 inline void
@@ -438,43 +438,43 @@ Shape::markChildren(JSTracer *trc)
 {
     MarkBaseShape(trc, &base_, "base");
     gc::MarkId(trc, &propidRef(), "propid");
     if (parent)
         MarkShape(trc, &parent, "parent");
 }
 
 inline void
-BaseShape::writeBarrierPre(BaseShape *base)
+BaseShape::writeBarrierPre(RawBaseShape base)
 {
 #ifdef JSGC_INCREMENTAL
     if (!base)
         return;
 
     JSCompartment *comp = base->compartment();
     if (comp->needsBarrier()) {
-        BaseShape *tmp = base;
+        RawBaseShape tmp = base;
         MarkBaseShapeUnbarriered(comp->barrierTracer(), &tmp, "write barrier");
         JS_ASSERT(tmp == base);
     }
 #endif
 }
 
 inline void
-BaseShape::writeBarrierPost(BaseShape *shape, void *addr)
+BaseShape::writeBarrierPost(RawBaseShape shape, void *addr)
 {
 }
 
 inline void
-BaseShape::readBarrier(BaseShape *base)
+BaseShape::readBarrier(RawBaseShape base)
 {
 #ifdef JSGC_INCREMENTAL
     JSCompartment *comp = base->compartment();
     if (comp->needsBarrier()) {
-        BaseShape *tmp = base;
+        RawBaseShape tmp = base;
         MarkBaseShapeUnbarriered(comp->barrierTracer(), &tmp, "read barrier");
         JS_ASSERT(tmp == base);
     }
 #endif
 }
 
 inline void
 BaseShape::markChildren(JSTracer *trc)
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -115,25 +115,27 @@ Bindings::initWithTemporaryStorage(JSCon
 #ifdef DEBUG
         /* The caller ensures no duplicate aliased names. */
         JS_ASSERT(!added.has(bi->name()));
         if (!added.put(bi->name()))
             return false;
 #endif
 
         StackBaseShape base(&CallClass, cx->global(), BaseShape::VAROBJ | BaseShape::DELEGATE);
-        UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
+
+        UnrootedUnownedBaseShape nbase = BaseShape::getUnowned(cx, base);
         if (!nbase)
             return false;
 
         RootedId id(cx, NameToId(bi->name()));
         unsigned attrs = JSPROP_PERMANENT | JSPROP_ENUMERATE |
                          (bi->kind() == CONSTANT ? JSPROP_READONLY : 0);
         unsigned frameIndex = bi.frameIndex();
         StackShape child(nbase, id, slot++, 0, attrs, Shape::HAS_SHORTID, frameIndex);
+        DropUnrooted(nbase);
 
         self->callObjShape_ = self->callObjShape_->getChildBinding(cx, child);
         if (!self->callObjShape_)
             return false;
     }
     JS_ASSERT(!bi);
 
     return true;
--- a/js/src/vm/ObjectImpl.h
+++ b/js/src/vm/ObjectImpl.h
@@ -11,16 +11,17 @@
 #include "mozilla/Assertions.h"
 #include "mozilla/StandardInteger.h"
 
 #include "jsfriendapi.h"
 #include "jsinfer.h"
 #include "jsval.h"
 
 #include "gc/Barrier.h"
+#include "gc/Heap.h"
 #include "vm/NumericConversions.h"
 #include "vm/String.h"
 
 namespace js {
 
 class Debugger;
 class ObjectImpl;