Backed out changeset 4282a285d381 (bug 707049 backout).
authorBrian Hackett <bhackett1024@gmail.com>
Sat, 31 Dec 2011 07:32:04 -0700
changeset 86060 10f831bfaf08329ede947f4cbf19480a4cf62601
parent 86059 4282a285d3813d5eb7a6639e690ce533a2fc1b04
child 86061 b73c54dfb1d0450a856725c3d1ea85bec07cb337
push idunknown
push userunknown
push dateunknown
bugs707049
milestone12.0a1
backs out4282a285d3813d5eb7a6639e690ce533a2fc1b04
Backed out changeset 4282a285d381 (bug 707049 backout).
js/public/HashTable.h
js/src/configure.in
js/src/frontend/BytecodeEmitter-inl.h
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/frontend/FoldConstants.cpp
js/src/frontend/Parser.cpp
js/src/gc/Barrier-inl.h
js/src/gc/Barrier.h
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarray.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsfriendapi.cpp
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsgcinlines.h
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsinterp.cpp
js/src/jsiter.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jspropertytree.cpp
js/src/jspropertytree.h
js/src/jsproxy.cpp
js/src/jsprvtd.h
js/src/jsreflect.cpp
js/src/jsscope.cpp
js/src/jsscope.h
js/src/jsscopeinlines.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsstr.cpp
js/src/jsxml.cpp
js/src/perf/jsperf.cpp
js/src/shell/js.cpp
js/src/vm/CallObject.cpp
js/src/vm/Debugger.cpp
js/src/vm/GlobalObject.cpp
js/src/vm/GlobalObject.h
js/src/vm/String.h
--- a/js/public/HashTable.h
+++ b/js/public/HashTable.h
@@ -1368,16 +1368,22 @@ class HashSet
 
     /* Like put, but assert that the given key is not already present. */
     bool putNew(const T &t) {
         AddPtr p = lookupForAdd(t);
         JS_ASSERT(!p);
         return add(p, t);
     }
 
+    bool putNew(const Lookup &l, const T &t) {
+        AddPtr p = lookupForAdd(l);
+        JS_ASSERT(!p);
+        return add(p, t);
+    }
+
     void remove(const Lookup &l) {
         if (Ptr p = lookup(l))
             remove(p);
     }
 };
 
 }  /* namespace js */
 
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -4356,16 +4356,27 @@ JSGC_INCREMENTAL=1
 MOZ_ARG_DISABLE_BOOL(gcincremental,
 [  --disable-gcincremental Disable incremental GC],
     JSGC_INCREMENTAL= )
 if test -n "$JSGC_INCREMENTAL"; then
     AC_DEFINE(JSGC_INCREMENTAL)
 fi
 
 dnl ========================================================
+dnl = Perform moving GC stack rooting analysis
+dnl ========================================================
+MOZ_ARG_ENABLE_BOOL(gcrootanalysis,
+[  --enable-root-analysis Enable moving GC stack root analysis],
+    JSGC_ROOT_ANALYSIS=1,
+    JSGC_ROOT_ANALYSIS= )
+if test -n "$JSGC_ROOT_ANALYSIS"; then
+    AC_DEFINE(JSGC_ROOT_ANALYSIS)
+fi
+
+dnl ========================================================
 dnl = Use Valgrind
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(valgrind,
 [  --enable-valgrind       Enable Valgrind integration hooks (default=no)],
     MOZ_VALGRIND=1,
     MOZ_VALGRIND= )
 if test -n "$MOZ_VALGRIND"; then
     MOZ_CHECK_HEADER([valgrind/valgrind.h], [],
--- a/js/src/frontend/BytecodeEmitter-inl.h
+++ b/js/src/frontend/BytecodeEmitter-inl.h
@@ -47,17 +47,18 @@
 namespace js {
 
 inline
 TreeContext::TreeContext(Parser *prs)
   : flags(0), bodyid(0), blockidGen(0), parenDepth(0), yieldCount(0), argumentsCount(0),
     topStmt(NULL), topScopeStmt(NULL), blockChain(NULL), blockNode(NULL),
     decls(prs->context), parser(prs), yieldNode(NULL), argumentsNode(NULL), scopeChain_(NULL),
     lexdeps(prs->context), parent(prs->tc), staticLevel(0), funbox(NULL), functionList(NULL),
-    innermostWith(NULL), bindings(prs->context), sharpSlotBase(-1)
+    innermostWith(NULL), bindings(prs->context), bindingsRoot(prs->context, &bindings),
+    sharpSlotBase(-1)
 {
     prs->tc = this;
 }
 
 /*
  * For functions the tree context is constructed and destructed a second
  * time during code generation. To avoid a redundant stats update in such
  * cases, we store UINT16_MAX in maxScopeDepth.
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2008,21 +2008,20 @@ EmitEnterBlock(JSContext *cx, BytecodeEm
 
     /*
      * If clones of this block will have any extensible parents, then the
      * clones must get unique shapes; see the comments for
      * js::Bindings::extensibleParents.
      */
     if ((bce->flags & TCF_FUN_EXTENSIBLE_SCOPE) ||
         bce->bindings.extensibleParents()) {
-        HeapPtrShape shape;
-        shape.init(blockObj->lastProperty());
-        if (!Shape::setExtensibleParents(cx, &shape))
-            return false;
-        blockObj->setLastPropertyInfallible(shape);
+        Shape *newShape = Shape::setExtensibleParents(cx, blockObj->lastProperty());
+        if (!newShape)
+            return false;
+        blockObj->setLastPropertyInfallible(newShape);
     }
 
     return true;
 }
 
 /*
  * Try to convert a *NAME op to a *GNAME op, which optimizes access to
  * undeclared globals. Return true if a conversion was made.
@@ -5567,26 +5566,29 @@ EmitWith(JSContext *cx, BytecodeEmitter 
     if (Emit1(cx, bce, JSOP_LEAVEWITH) < 0)
         return false;
     return PopStatementBCE(cx, bce);
 }
 
 static bool
 SetMethodFunction(JSContext *cx, FunctionBox *funbox, JSAtom *atom)
 {
+    RootedVarObject parent(cx);
+    parent = funbox->function()->getParent();
+
     /*
      * Replace a boxed function with a new one with a method atom. Methods
      * require a function with the extended size finalize kind, which normal
      * functions don't have. We don't eagerly allocate functions with the
      * expanded size for boxed functions, as most functions are not methods.
      */
     JSFunction *fun = js_NewFunction(cx, NULL, NULL,
                                      funbox->function()->nargs,
                                      funbox->function()->flags,
-                                     funbox->function()->getParent(),
+                                     parent,
                                      funbox->function()->atom,
                                      JSFunction::ExtendedFinalizeKind);
     if (!fun)
         return false;
 
     JSScript *script = funbox->function()->script();
     if (script) {
         fun->setScript(script);
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -346,16 +346,17 @@ struct TreeContext {                /* t
                                        if (flags & TCF_IN_FUNCTION) and not in
                                        js::frontend::CompileFunctionBody */
     FunctionBox     *functionList;
 
     ParseNode       *innermostWith; /* innermost WITH parse node */
 
     Bindings        bindings;       /* bindings in this code, including
                                        arguments if we're compiling a function */
+    Bindings::StackRoot bindingsRoot; /* root for stack allocated bindings. */
 
     void trace(JSTracer *trc);
 
     inline TreeContext(Parser *prs);
     inline ~TreeContext();
 
     /*
      * js::BytecodeEmitter derives from js::TreeContext; however, only the
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -311,17 +311,16 @@ FoldXMLConstants(JSContext *cx, ParseNod
             pnp = &pn2->pn_next;
             pn1 = *pnp;
             accum = NULL;
             continue;
         }
 
         if (accum) {
             {
-                AutoStringRooter tvr(cx, accum);
                 str = ((kind == PNK_XMLSTAGO || kind == PNK_XMLPTAGC) && i != 0)
                       ? js_AddAttributePart(cx, i & 1, accum, str)
                       : js_ConcatStrings(cx, accum, str);
             }
             if (!str)
                 return JS_FALSE;
 #ifdef DEBUG_brendanXXX
             printf("2: %d, %d => ", i, j);
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -948,17 +948,19 @@ Parser::newFunction(TreeContext *tc, JSA
     /*
      * Find the global compilation context in order to pre-set the newborn
      * function's parent slot to tc->scopeChain. If the global context is a
      * compile-and-go one, we leave the pre-set parent intact; otherwise we
      * clear parent and proto.
      */
     while (tc->parent)
         tc = tc->parent;
-    JSObject *parent = tc->inFunction() ? NULL : tc->scopeChain();
+
+    RootedVarObject parent(context);
+    parent = tc->inFunction() ? NULL : tc->scopeChain();
 
     JSFunction *fun =
         js_NewFunction(context, NULL, NULL, 0,
                        JSFUN_INTERPRETED | (kind == Expression ? JSFUN_LAMBDA : 0),
                        parent, atom);
     if (fun && !tc->compileAndGo()) {
         if (!fun->clearParent(context))
             return NULL;
--- a/js/src/gc/Barrier-inl.h
+++ b/js/src/gc/Barrier-inl.h
@@ -76,35 +76,38 @@ DestroyValueRange(HeapValue *vec, uintN 
     for (uintN i = 0; i < len; i++)
         vec[i].~HeapValue();
 }
 
 inline
 HeapValue::HeapValue(const Value &v)
     : value(v)
 {
+    JS_ASSERT(!IsPoisonedValue(v));
     post();
 }
 
 inline
 HeapValue::HeapValue(const HeapValue &v)
     : value(v.value)
 {
+    JS_ASSERT(!IsPoisonedValue(v.value));
     post();
 }
 
 inline
 HeapValue::~HeapValue()
 {
     pre();
 }
 
 inline void
 HeapValue::init(const Value &v)
 {
+    JS_ASSERT(!IsPoisonedValue(v));
     value = v;
     post();
 }
 
 inline void
 HeapValue::init(JSCompartment *comp, const Value &v)
 {
     value = v;
@@ -162,25 +165,27 @@ inline void
 HeapValue::post(JSCompartment *comp)
 {
 }
 
 inline HeapValue &
 HeapValue::operator=(const Value &v)
 {
     pre();
+    JS_ASSERT(!IsPoisonedValue(v));
     value = v;
     post();
     return *this;
 }
 
 inline HeapValue &
 HeapValue::operator=(const HeapValue &v)
 {
     pre();
+    JS_ASSERT(!IsPoisonedValue(v.value));
     value = v.value;
     post();
     return *this;
 }
 
 inline void
 HeapValue::set(JSCompartment *comp, const Value &v)
 {
@@ -188,44 +193,48 @@ HeapValue::set(JSCompartment *comp, cons
     if (value.isMarkable()) {
         js::gc::Cell *cell = (js::gc::Cell *)value.toGCThing();
         JS_ASSERT(cell->compartment() == comp ||
                   cell->compartment() == comp->rt->atomsCompartment);
     }
 #endif
 
     pre(comp);
+    JS_ASSERT(!IsPoisonedValue(v));
     value = v;
     post(comp);
 }
 
 inline void
 HeapValue::boxNonDoubleFrom(JSValueType type, uint64_t *out)
 {
     pre();
     value.boxNonDoubleFrom(type, out);
+    JS_ASSERT(!IsPoisonedValue(value));
     post();
 }
 
 inline
 HeapId::HeapId(jsid id)
     : value(id)
 {
+    JS_ASSERT(!IsPoisonedId(id));
     post();
 }
 
 inline
 HeapId::~HeapId()
 {
     pre();
 }
 
 inline void
 HeapId::init(jsid id)
 {
+    JS_ASSERT(!IsPoisonedId(id));
     value = id;
     post();
 }
 
 inline void
 HeapId::pre()
 {
 #ifdef JSGC_INCREMENTAL
@@ -242,25 +251,27 @@ inline void
 HeapId::post()
 {
 }
 
 inline HeapId &
 HeapId::operator=(jsid id)
 {
     pre();
+    JS_ASSERT(!IsPoisonedId(id));
     value = id;
     post();
     return *this;
 }
 
 inline HeapId &
 HeapId::operator=(const HeapId &v)
 {
     pre();
+    JS_ASSERT(!IsPoisonedId(v.value));
     value = v.value;
     post();
     return *this;
 }
 
 } /* namespace js */
 
 #endif /* jsgc_barrier_inl_h___ */
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -181,16 +181,17 @@ class HeapPtr
     HeapPtr() : value(NULL) {}
     explicit HeapPtr(T *v) : value(v) { post(); }
     explicit HeapPtr(const HeapPtr<T> &v) : value(v.value) { post(); }
 
     ~HeapPtr() { pre(); }
 
     /* Use this to install a ptr into a newly allocated object. */
     void init(T *v) {
+        JS_ASSERT(!IsPoisonedPtr<T>(v));
         value = v;
         post();
     }
 
     /* Use to set the pointer to NULL. */
     void clear() {
 	pre();
 	value = NULL;
@@ -205,23 +206,25 @@ class HeapPtr
      */
     T **unsafeGet() { return &value; }
     void unsafeSet(T *v) { value = v; }
 
     Unioned *unsafeGetUnioned() { return &other; }
 
     HeapPtr<T, Unioned> &operator=(T *v) {
         pre();
+        JS_ASSERT(!IsPoisonedPtr<T>(v));
         value = v;
         post();
         return *this;
     }
 
     HeapPtr<T, Unioned> &operator=(const HeapPtr<T> &v) {
         pre();
+        JS_ASSERT(!IsPoisonedPtr<T>(v.value));
         value = v.value;
         post();
         return *this;
     }
 
     T &operator*() const { return *value; }
     T *operator->() const { return value; }
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1848,16 +1848,18 @@ JS_PUBLIC_API(JSBool)
 JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsid id, JSBool *resolved)
 {
     JSString *idstr;
     JSRuntime *rt;
     JSAtom *atom;
     JSStdName *stdnm;
     uintN i;
 
+    RootObject objRoot(cx, &obj);
+
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, id);
     *resolved = JS_FALSE;
 
     rt = cx->runtime;
     JS_ASSERT(rt->state != JSRTS_DOWN);
     if (rt->state == JSRTS_LANDING || !JSID_IS_ATOM(id))
         return JS_TRUE;
@@ -3035,17 +3037,18 @@ JS_FinalizeStub(JSContext *cx, JSObject 
 JS_PUBLIC_API(JSObject *)
 JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto,
              JSClass *clasp, JSNative constructor, uintN nargs,
              JSPropertySpec *ps, JSFunctionSpec *fs,
              JSPropertySpec *static_ps, JSFunctionSpec *static_fs)
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, parent_proto);
-    return js_InitClass(cx, obj, parent_proto, Valueify(clasp), constructor,
+    RootObject objRoot(cx, &obj);
+    return js_InitClass(cx, objRoot, parent_proto, Valueify(clasp), constructor,
                         nargs, ps, fs, static_ps, static_fs);
 }
 
 #ifdef JS_THREADSAFE
 JS_PUBLIC_API(JSClass *)
 JS_GetClass(JSContext *cx, JSObject *obj)
 {
     return obj->getJSClass();
@@ -3583,28 +3586,33 @@ JS_DefineElement(JSContext *cx, JSObject
 static JSBool
 DefineProperty(JSContext *cx, JSObject *obj, const char *name, const Value &value,
                PropertyOp getter, StrictPropertyOp setter, uintN attrs,
                uintN flags, intN tinyid)
 {
     jsid id;
     JSAtom *atom;
 
+    RootObject objRoot(cx, &obj);
+    RootValue valueRoot(cx, &value);
+
     if (attrs & JSPROP_INDEX) {
         id = INT_TO_JSID(intptr_t(name));
         atom = NULL;
         attrs &= ~JSPROP_INDEX;
     } else {
         atom = js_Atomize(cx, name, strlen(name));
         if (!atom)
             return JS_FALSE;
         id = ATOM_TO_JSID(atom);
     }
 
     if (attrs & JSPROP_NATIVE_ACCESSORS) {
+        RootId idRoot(cx, &id);
+
         JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
         attrs &= ~JSPROP_NATIVE_ACCESSORS;
         if (getter) {
             JSObject *getobj = JS_NewFunction(cx, (Native) getter, 0, 0, obj->getGlobal(), NULL);
             if (!getobj)
                 return false;
             getter = JS_DATA_TO_FUNC_PTR(PropertyOp, getobj);
             attrs |= JSPROP_GETTER;
@@ -3612,16 +3620,17 @@ DefineProperty(JSContext *cx, JSObject *
         if (setter) {
             JSObject *setobj = JS_NewFunction(cx, (Native) setter, 1, 0, obj->getGlobal(), NULL);
             if (!setobj)
                 return false;
             setter = JS_DATA_TO_FUNC_PTR(StrictPropertyOp, setobj);
             attrs |= JSPROP_SETTER;
         }
     }
+
     return DefinePropertyById(cx, obj, id, value, getter, setter, attrs, flags, tinyid);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value,
                   PropertyOp getter, JSStrictPropertyOp setter, uintN attrs)
 {
     return DefineProperty(cx, obj, name, value, getter, setter, attrs, 0, 0);
@@ -3674,17 +3683,20 @@ JS_DefineObject(JSContext *cx, JSObject 
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, proto);
 
     Class *clasp = Valueify(jsclasp);
     if (!clasp)
         clasp = &ObjectClass;    /* default class is Object */
 
-    JSObject *nobj = NewObjectWithClassProto(cx, clasp, proto, obj);
+    RootObject root(cx, &obj);
+    RootedVarObject nobj(cx);
+
+    nobj = NewObjectWithClassProto(cx, clasp, proto, obj);
     if (!nobj)
         return NULL;
 
     if (!DefineProperty(cx, obj, name, ObjectValue(*nobj), NULL, NULL, attrs, 0, 0))
         return NULL;
 
     return nobj;
 }
@@ -3707,16 +3719,17 @@ JS_DefineConstDoubles(JSContext *cx, JSO
     }
     return ok;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DefineProperties(JSContext *cx, JSObject *obj, JSPropertySpec *ps)
 {
     JSBool ok;
+    RootObject root(cx, &obj);
 
     for (ok = true; ps->name; ps++) {
         ok = DefineProperty(cx, obj, ps->name, UndefinedValue(), ps->getter, ps->setter,
                             ps->flags, Shape::HAS_SHORTID, ps->tinyid);
         if (!ok)
             break;
     }
     return ok;
@@ -4176,17 +4189,16 @@ JS_NewPropertyIterator(JSContext *cx, JS
         index = -1;
     } else {
         /*
          * Non-native case: enumerate a JSIdArray and keep it via private.
          *
          * Note: we have to make sure that we root obj around the call to
          * JS_Enumerate to protect against multiple allocations under it.
          */
-        AutoObjectRooter tvr(cx, iterobj);
         ida = JS_Enumerate(cx, obj);
         if (!ida)
             return NULL;
         pdata = ida;
         index = ida->length;
     }
 
     /* iterobj cannot escape to other threads here. */
@@ -4362,29 +4374,32 @@ JS_NewFunction(JSContext *cx, JSNative n
 
     if (!name) {
         atom = NULL;
     } else {
         atom = js_Atomize(cx, name, strlen(name));
         if (!atom)
             return NULL;
     }
-    return js_NewFunction(cx, NULL, native, nargs, flags, parent, atom);
+
+    RootObject parentRoot(cx, &parent);
+    return js_NewFunction(cx, NULL, native, nargs, flags, parentRoot, atom);
 }
 
 JS_PUBLIC_API(JSFunction *)
 JS_NewFunctionById(JSContext *cx, JSNative native, uintN nargs, uintN flags, JSObject *parent,
                    jsid id)
 {
     JS_ASSERT(JSID_IS_STRING(id));
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, parent);
 
-    return js_NewFunction(cx, NULL, native, nargs, flags, parent, JSID_TO_ATOM(id));
+    RootObject parentRoot(cx, &parent);
+    return js_NewFunction(cx, NULL, native, nargs, flags, parentRoot, JSID_TO_ATOM(id));
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent)
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, parent);  // XXX no funobj for now
     if (!parent) {
@@ -4528,36 +4543,39 @@ js_generic_native_method_dispatcher(JSCo
     vp[2 + --argc].setUndefined();
 
     return fs->call(cx, argc, vp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs)
 {
+    RootObject objRoot(cx, &obj);
+
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     uintN flags;
-    JSObject *ctor;
+    RootedVarObject ctor(cx);
     JSFunction *fun;
 
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
-    ctor = NULL;
     for (; fs->name; fs++) {
         flags = fs->flags;
 
         JSAtom *atom = js_Atomize(cx, fs->name, strlen(fs->name));
         if (!atom)
             return JS_FALSE;
 
         /*
          * Define a generic arity N+1 static method for the arity N prototype
          * method if flags contains JSFUN_GENERIC_NATIVE.
          */
         if (flags & JSFUN_GENERIC_NATIVE) {
+            RootAtom root(cx, &atom);
+
             if (!ctor) {
                 ctor = JS_GetConstructor(cx, obj);
                 if (!ctor)
                     return JS_FALSE;
             }
 
             flags &= ~JSFUN_GENERIC_NATIVE;
             fun = js_DefineFunction(cx, ctor, ATOM_TO_JSID(atom),
@@ -4570,58 +4588,65 @@ JS_DefineFunctions(JSContext *cx, JSObje
 
             /*
              * As jsapi.h notes, fs must point to storage that lives as long
              * as fun->object lives.
              */
             fun->setExtendedSlot(0, PrivateValue(fs));
         }
 
-        fun = js_DefineFunction(cx, obj, ATOM_TO_JSID(atom), fs->call, fs->nargs, flags);
+        fun = js_DefineFunction(cx, objRoot,
+                                ATOM_TO_JSID(atom), fs->call, fs->nargs, flags);
         if (!fun)
             return JS_FALSE;
     }
     return JS_TRUE;
 }
 
 JS_PUBLIC_API(JSFunction *)
 JS_DefineFunction(JSContext *cx, JSObject *obj, const char *name, JSNative call,
                   uintN nargs, uintN attrs)
 {
+    RootObject objRoot(cx, &obj);
+
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     JSAtom *atom = js_Atomize(cx, name, strlen(name));
     if (!atom)
         return NULL;
-    return js_DefineFunction(cx, obj, ATOM_TO_JSID(atom), call, nargs, attrs);
+    return js_DefineFunction(cx, objRoot, ATOM_TO_JSID(atom), call, nargs, attrs);
 }
 
 JS_PUBLIC_API(JSFunction *)
 JS_DefineUCFunction(JSContext *cx, JSObject *obj,
                     const jschar *name, size_t namelen, JSNative call,
                     uintN nargs, uintN attrs)
 {
+    RootObject objRoot(cx, &obj);
+
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
     if (!atom)
         return NULL;
-    return js_DefineFunction(cx, obj, ATOM_TO_JSID(atom), call, nargs, attrs);
+    return js_DefineFunction(cx, objRoot, ATOM_TO_JSID(atom), call, nargs, attrs);
 }
 
 extern JS_PUBLIC_API(JSFunction *)
 JS_DefineFunctionById(JSContext *cx, JSObject *obj, jsid id, JSNative call,
                     uintN nargs, uintN attrs)
 {
+    RootObject objRoot(cx, &obj);
+
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
-    return js_DefineFunction(cx, obj, id, call, nargs, attrs);
+    return js_DefineFunction(cx, objRoot, id, call, nargs, attrs);
 }
 
 struct AutoLastFrameCheck {
     AutoLastFrameCheck(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM)
       : cx(cx) {
         JS_ASSERT(cx);
         JS_GUARD_OBJECT_NOTIFIER_INIT;
     }
@@ -4912,16 +4937,18 @@ JS_GetGlobalFromScript(JSScript *script)
 
 static JSFunction *
 CompileUCFunctionForPrincipalsCommon(JSContext *cx, JSObject *obj,
                                      JSPrincipals *principals, const char *name,
                                      uintN nargs, const char **argnames,
                                      const jschar *chars, size_t length,
                                      const char *filename, uintN lineno, JSVersion version)
 {
+    RootObject objRoot(cx, &obj);
+
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, principals);
     AutoLastFrameCheck lfc(cx);
 
     JSAtom *funAtom;
     if (!name) {
         funAtom = NULL;
@@ -4934,17 +4961,17 @@ CompileUCFunctionForPrincipalsCommon(JSC
     Bindings bindings(cx);
     for (uintN i = 0; i < nargs; i++) {
         uint16_t dummy;
         JSAtom *argAtom = js_Atomize(cx, argnames[i], strlen(argnames[i]));
         if (!argAtom || !bindings.addArgument(cx, argAtom, &dummy))
             return NULL;
     }
 
-    JSFunction *fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, obj, funAtom);
+    JSFunction *fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, objRoot, funAtom);
     if (!fun)
         return NULL;
 
     if (!frontend::CompileFunctionBody(cx, fun, principals, NULL, &bindings,
                                        chars, length, filename, lineno, version))
     {
         return NULL;
     }
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -200,16 +200,46 @@ inline Anchor<T>::~Anchor()
      * NB: there is a Anchor<Value>::~Anchor() specialization below.
      */
     volatile T sink;
     sink = hold;
 }
 #endif  /* defined(__GNUC__) */
 
 /*
+ * Methods for poisoning GC heap pointer words and checking for poisoned words.
+ * These are in this file for use in Value methods and so forth.
+ *
+ * If the moving GC hazard analysis is in use and detects a non-rooted stack
+ * pointer to a GC thing, one byte of that pointer is poisoned to refer to an
+ * invalid location. For both 32 bit and 64 bit systems, the fourth byte of the
+ * pointer is overwritten, to reduce the likelihood of accidentally changing
+ * a live integer value.
+ */
+
+inline void PoisonPtr(jsuword *v)
+{
+#if defined(JSGC_ROOT_ANALYSIS) && defined(DEBUG)
+    uint8_t *ptr = (uint8_t *) v + 3;
+    *ptr = JS_FREE_PATTERN;
+#endif
+}
+
+template <typename T>
+inline bool IsPoisonedPtr(T *v)
+{
+#if defined(JSGC_ROOT_ANALYSIS) && defined(DEBUG)
+    uint32_t mask = jsuword(v) & 0xff000000;
+    return mask == uint32_t(JS_FREE_PATTERN << 24);
+#else
+    return false;
+#endif
+}
+
+/*
  * JS::Value is the C++ interface for a single JavaScript Engine value.
  * A few general notes on JS::Value:
  *
  * - JS::Value has setX() and isX() members for X in
  *
  *     { Int32, Double, String, Boolean, Undefined, Null, Object, Magic }
  *
  *   JS::Value also contains toX() for each of the non-singleton types.
@@ -276,26 +306,28 @@ class Value
     JS_ALWAYS_INLINE
     double &getDoubleRef() {
         JS_ASSERT(isDouble());
         return data.asDouble;
     }
 
     JS_ALWAYS_INLINE
     void setString(JSString *str) {
+        JS_ASSERT(!IsPoisonedPtr(str));
         data = STRING_TO_JSVAL_IMPL(str);
     }
 
     JS_ALWAYS_INLINE
     void setString(const JS::Anchor<JSString *> &str) {
         setString(str.get());
     }
 
     JS_ALWAYS_INLINE
     void setObject(JSObject &obj) {
+        JS_ASSERT(!IsPoisonedPtr(&obj));
         data = OBJECT_TO_JSVAL_IMPL(&obj);
     }
 
     JS_ALWAYS_INLINE
     void setObject(const JS::Anchor<JSObject *> &obj) {
         setObject(*obj.get());
     }
 
@@ -676,16 +708,26 @@ class Value
         JS_STATIC_ASSERT(sizeof(JSWhyMagic) <= 4);
         JS_STATIC_ASSERT(sizeof(Value) == 8);
     }
 
     friend jsval_layout (::JSVAL_TO_IMPL)(Value);
     friend Value (::IMPL_TO_JSVAL)(jsval_layout l);
 } JSVAL_ALIGNMENT;
 
+inline bool
+IsPoisonedValue(const Value &v)
+{
+    if (v.isString())
+        return IsPoisonedPtr(v.toString());
+    if (v.isObject())
+        return IsPoisonedPtr(&v.toObject());
+    return false;
+}
+
 /************************************************************************/
 
 static JS_ALWAYS_INLINE Value
 NullValue()
 {
     Value v;
     v.setNull();
     return v;
@@ -1977,16 +2019,26 @@ extern JS_PUBLIC_API(void)
 JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth);
 
 extern JS_PUBLIC_API(JSBool)
 JS_IsInRequest(JSContext *cx);
 
 #ifdef __cplusplus
 JS_END_EXTERN_C
 
+inline bool
+IsPoisonedId(jsid iden)
+{
+    if (JSID_IS_STRING(iden))
+        return JS::IsPoisonedPtr(JSID_TO_STRING(iden));
+    if (JSID_IS_OBJECT(iden))
+        return JS::IsPoisonedPtr(JSID_TO_OBJECT(iden));
+    return false;
+}
+
 class JSAutoRequest {
   public:
     JSAutoRequest(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM)
         : mContext(cx), mSaveDepth(0) {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
         JS_BeginRequest(mContext);
     }
     ~JSAutoRequest() {
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -655,19 +655,16 @@ array_length_setter(JSContext *cx, JSObj
          * oldlen, we iterate through all properties and remove those that
          * correspond to indexes in the half-open range [newlen, oldlen).  See
          * bug 322135.
          */
         JSObject *iter = JS_NewPropertyIterator(cx, obj);
         if (!iter)
             return false;
 
-        /* Protect iter against GC under JSObject::deleteProperty. */
-        AutoObjectRooter tvr(cx, iter);
-
         jsuint gap = oldlen - newlen;
         for (;;) {
             if (!JS_CHECK_OPERATION_LIMIT(cx) || !JS_NextProperty(cx, iter, &id))
                 return false;
             if (JSID_IS_VOID(id))
                 break;
             jsuint index;
             Value junk;
@@ -3583,25 +3580,28 @@ js_Array(JSContext *cx, uintN argc, Valu
     return true;
 }
 
 JSObject *
 js_InitArrayClass(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isNative());
 
-    GlobalObject *global = obj->asGlobal();
-
-    JSObject *arrayProto = global->createBlankPrototype(cx, &SlowArrayClass);
+    RootedVar<GlobalObject*> global(cx);
+    global = obj->asGlobal();
+
+    RootedVarObject arrayProto(cx);
+    arrayProto = global->createBlankPrototype(cx, &SlowArrayClass);
     if (!arrayProto || !AddLengthProperty(cx, arrayProto))
         return NULL;
     arrayProto->setArrayLength(cx, 0);
 
-    JSFunction *ctor = global->createConstructor(cx, js_Array, &ArrayClass,
-                                                 CLASS_ATOM(cx, Array), 1);
+    RootedVarFunction ctor(cx);
+    ctor = global->createConstructor(cx, js_Array, &ArrayClass,
+                                     CLASS_ATOM(cx, Array), 1);
     if (!ctor)
         return NULL;
 
     /*
      * The default 'new' type of Array.prototype is required by type inference
      * to have unknown properties, to simplify handling of e.g. heterogenous
      * arrays in JSON and script literals and allows setDenseArrayElement to
      * be used without updating the indexed type set for such default arrays.
@@ -3669,29 +3669,35 @@ NewArray(JSContext *cx, uint32_t length,
         /* Fixup the elements pointer and length, which may be incorrect. */
         obj->setFixedElements();
         obj->setArrayLength(cx, length);
         if (allocateCapacity && !EnsureNewArrayElements(cx, obj, length))
             return NULL;
         return obj;
     }
 
-    if (!proto && !FindProto(cx, &ArrayClass, parent, &proto))
+    Root<GlobalObject*> parentRoot(cx, &parent);
+
+    if (!proto && !FindProto(cx, &ArrayClass, parentRoot, &proto))
         return NULL;
 
-    types::TypeObject *type = proto->getNewType(cx);
+    RootObject protoRoot(cx, &proto);
+    RootedVarTypeObject type(cx);
+
+    type = proto->getNewType(cx);
     if (!type)
         return NULL;
 
     /*
      * Get a shape with zero fixed slots, regardless of the size class.
      * See JSObject::createDenseArray.
      */
-    Shape *shape = EmptyShape::getInitialShape(cx, &ArrayClass, proto,
-                                               proto->getParent(), gc::FINALIZE_OBJECT0);
+    RootedVarShape shape(cx);
+    shape = EmptyShape::getInitialShape(cx, &ArrayClass, proto,
+                                        parent, gc::FINALIZE_OBJECT0);
     if (!shape)
         return NULL;
 
     JSObject* obj = JSObject::createDenseArray(cx, kind, shape, type, length);
     if (!obj)
         return NULL;
 
     if (entry != -1)
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1468,16 +1468,22 @@ JSContext::JSContext(JSRuntime *rt)
     , stackIterAssertionEnabled(true)
 #endif
 {
     PodZero(&sharpObjectMap);
     PodZero(&link);
 #ifdef JS_THREADSAFE
     PodZero(&threadLinks);
 #endif
+#ifdef JSGC_ROOT_ANALYSIS
+    PodArrayZero(thingGCRooters);
+#ifdef DEBUG
+    checkGCRooters = NULL;
+#endif
+#endif
 }
 
 JSContext::~JSContext()
 {
 #ifdef JS_THREADSAFE
     JS_ASSERT(!thread_);
 #endif
 
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -791,16 +791,19 @@ struct JSArgumentFormatMap {
 };
 #endif
 
 extern const JSDebugHooks js_NullDebugHooks;  /* defined in jsdbgapi.cpp */
 
 namespace js {
 
 class AutoGCRooter;
+template <typename T> class Root;
+class CheckRoot;
+
 struct AutoResolving;
 
 static inline bool
 OptionsHasXML(uint32_t options)
 {
     return !!(options & JSOPTION_XML);
 }
 
@@ -1097,16 +1100,38 @@ struct JSContext
                                                without the corresponding
                                                JS_EndRequest. */
     JSCList             threadLinks;        /* JSThread contextList linkage */
 #endif
 
     /* Stack of thread-stack-allocated GC roots. */
     js::AutoGCRooter   *autoGCRooters;
 
+#ifdef JSGC_ROOT_ANALYSIS
+
+    /*
+     * Stack allocated GC roots for stack GC heap pointers, which may be
+     * overwritten if moved during a GC.
+     */
+    js::Root<js::gc::Cell*> *thingGCRooters[js::THING_ROOT_COUNT];
+
+#ifdef DEBUG
+    /*
+     * Stack allocated list of stack locations which hold non-relocatable
+     * GC heap pointers (where the target is rooted somewhere else) or integer
+     * values which may be confused for GC heap pointers. These are used to
+     * suppress false positives which occur when a rooting analysis treats the
+     * location as holding a relocatable pointer, but have no other effect on
+     * GC behavior.
+     */
+    js::CheckRoot *checkGCRooters;
+#endif
+
+#endif /* JSGC_ROOT_ANALYSIS */
+
     /* Debug hooks associated with the current context. */
     const JSDebugHooks  *debugHooks;
 
     /* Security callbacks that override any defined on the runtime. */
     JSSecurityCallbacks *securityCallbacks;
 
     /* Stored here to avoid passing it around as a parameter. */
     uintN               resolveFlags;
@@ -1402,16 +1427,270 @@ class AutoGCRooter {
         OBJVECTOR =   -16  /* js::AutoObjectVector */
     };
 
   private:
     AutoGCRooter(AutoGCRooter &ida) MOZ_DELETE;
     void operator=(AutoGCRooter &ida) MOZ_DELETE;
 };
 
+/*
+ * 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:
+ *
+ * Foo();
+ * ... = 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 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.
+ *
+ * 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.
+ *
+ * - Root<T> roots an existing stack allocated variable or other location of
+ *   type T. This is typically used either when a variable only needs to be
+ *   rooted on certain rare paths, or when a function takes a bare GC thing
+ *   pointer as an argument and needs to root it. In the latter case a
+ *   Handle<T> is generally preferred, see below.
+ *
+ * - RootedVar<T> declares a variable of type T, whose value is always rooted.
+ *
+ * - Handle<T> is a const reference to a Root<T> or RootedVar<T>. Handles are
+ *   coerced automatically from such a Root<T> or RootedVar<T>. Functions which
+ *   take GC things or values as arguments and need to root those arguments
+ *   should generally replace those arguments with handles 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.
+ */
+
+template <typename T>
+struct RootMethods { };
+
+template <> struct RootMethods<const jsid>
+{
+    static jsid initial() { return JSID_VOID; }
+    static ThingRootKind kind() { return THING_ROOT_ID; }
+    static bool poisoned(jsid id) { return IsPoisonedId(id); }
+};
+
+template <> struct RootMethods<jsid>
+{
+    static jsid initial() { return JSID_VOID; }
+    static ThingRootKind kind() { return THING_ROOT_ID; }
+    static bool poisoned(jsid id) { return IsPoisonedId(id); }
+};
+
+template <> struct RootMethods<const Value>
+{
+    static Value initial() { return UndefinedValue(); }
+    static ThingRootKind kind() { return THING_ROOT_VALUE; }
+    static bool poisoned(const Value &v) { return IsPoisonedValue(v); }
+};
+
+template <> struct RootMethods<Value>
+{
+    static Value initial() { return UndefinedValue(); }
+    static ThingRootKind kind() { return THING_ROOT_VALUE; }
+    static bool poisoned(const Value &v) { return IsPoisonedValue(v); }
+};
+
+template <typename T>
+struct RootMethods<T *>
+{
+    static T *initial() { return NULL; }
+    static ThingRootKind kind() { return T::rootKind(); }
+    static bool poisoned(T *v) { return IsPoisonedPtr(v); }
+};
+
+/*
+ * Root a stack location holding a GC thing. This takes a stack pointer
+ * and ensures that throughout its lifetime the referenced variable
+ * will remain pinned against a moving GC.
+ *
+ * It is important to ensure that the location referenced by a Root is
+ * initialized, as otherwise the GC may try to use the the uninitialized value.
+ * It is generally preferable to use either RootedVar for local variables, or
+ * Handle for arguments.
+ */
+template <typename T>
+class Root
+{
+  public:
+    Root(JSContext *cx, const T *ptr
+         JS_GUARD_OBJECT_NOTIFIER_PARAM)
+    {
+#ifdef JSGC_ROOT_ANALYSIS
+        ThingRootKind kind = RootMethods<T>::kind();
+        this->stack = reinterpret_cast<Root<T>**>(&cx->thingGCRooters[kind]);
+        this->prev = *stack;
+        *stack = this;
+#endif
+
+        JS_ASSERT(!RootMethods<T>::poisoned(*ptr));
+
+        this->ptr = ptr;
+
+        JS_GUARD_OBJECT_NOTIFIER_INIT;
+    }
+
+    ~Root()
+    {
+#ifdef JSGC_ROOT_ANALYSIS
+        JS_ASSERT(*stack == this);
+        *stack = prev;
+#endif
+    }
+
+#ifdef JSGC_ROOT_ANALYSIS
+    Root<T> *previous() { return prev; }
+#endif
+
+    const T *address() const { return ptr; }
+
+  private:
+
+#ifdef JSGC_ROOT_ANALYSIS
+    Root<T> **stack, *prev;
+#endif
+    const T *ptr;
+
+    JS_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
+template<typename T> template <typename S>
+inline
+Handle<T>::Handle(const Root<S> &root)
+{
+    testAssign<S>();
+    ptr = reinterpret_cast<const T *>(root.address());
+}
+
+typedef Root<JSObject*>          RootObject;
+typedef Root<JSFunction*>        RootFunction;
+typedef Root<Shape*>             RootShape;
+typedef Root<BaseShape*>         RootBaseShape;
+typedef Root<types::TypeObject*> RootTypeObject;
+typedef Root<JSString*>          RootString;
+typedef Root<JSAtom*>            RootAtom;
+typedef Root<jsid>               RootId;
+typedef Root<Value>              RootValue;
+
+/* Mark a stack location as a root for a rooting analysis. */
+class CheckRoot
+{
+#if defined(DEBUG) && defined(JSGC_ROOT_ANALYSIS)
+
+    CheckRoot **stack, *prev;
+    const uint8 *ptr;
+
+  public:
+    template <typename T>
+    CheckRoot(JSContext *cx, const T *ptr
+              JS_GUARD_OBJECT_NOTIFIER_PARAM)
+    {
+        this->stack = &cx->checkGCRooters;
+        this->prev = *stack;
+        *stack = this;
+        this->ptr = (const uint8 *) ptr;
+        JS_GUARD_OBJECT_NOTIFIER_INIT;
+    }
+
+    ~CheckRoot()
+    {
+        JS_ASSERT(*stack == this);
+        *stack = prev;
+    }
+
+    CheckRoot *previous() { return prev; }
+
+    bool contains(const uint8 *v, size_t len) {
+        return ptr >= v && ptr < v + len;
+    }
+
+#else /* DEBUG && JSGC_ROOT_ANALYSIS */
+
+  public:
+    template <typename T>
+    CheckRoot(JSContext *cx, const T *ptr
+              JS_GUARD_OBJECT_NOTIFIER_PARAM)
+    {
+        JS_GUARD_OBJECT_NOTIFIER_INIT;
+    }
+
+#endif /* DEBUG && JSGC_ROOT_ANALYSIS */
+
+    JS_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
+/* Make a local variable which stays rooted throughout its lifetime. */
+template <typename T>
+class RootedVar
+{
+  public:
+    RootedVar(JSContext *cx)
+        : ptr(RootMethods<T>::initial()), root(cx, &ptr)
+    {}
+
+    RootedVar(JSContext *cx, T initial)
+        : ptr(initial), root(cx, &ptr)
+    {}
+
+    operator T () { return ptr; }
+    T operator ->() { return ptr; }
+    T * address() { return &ptr; }
+    const T * address() const { return &ptr; }
+    T raw() { return ptr; }
+
+    T & operator =(T value)
+    {
+        JS_ASSERT(!RootMethods<T>::poisoned(value));
+        ptr = value;
+        return ptr;
+    }
+
+  private:
+    T ptr;
+    Root<T> root;
+};
+
+template <typename T> template <typename S>
+inline
+Handle<T>::Handle(const RootedVar<S> &root)
+{
+    ptr = reinterpret_cast<const T *>(root.address());
+}
+
+typedef RootedVar<JSObject*>          RootedVarObject;
+typedef RootedVar<JSFunction*>        RootedVarFunction;
+typedef RootedVar<Shape*>             RootedVarShape;
+typedef RootedVar<BaseShape*>         RootedVarBaseShape;
+typedef RootedVar<types::TypeObject*> RootedVarTypeObject;
+typedef RootedVar<JSString*>          RootedVarString;
+typedef RootedVar<JSAtom*>            RootedVarAtom;
+typedef RootedVar<jsid>               RootedVarId;
+typedef RootedVar<Value>              RootedVarValue;
+
 /* FIXME(bug 332648): Move this into a public header. */
 class AutoValueRooter : private AutoGCRooter
 {
   public:
     explicit AutoValueRooter(JSContext *cx
                              JS_GUARD_OBJECT_NOTIFIER_PARAM)
       : AutoGCRooter(cx, JSVAL), val(js::NullValue())
     {
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -231,70 +231,77 @@ js::IsOriginalScriptFunction(JSFunction 
 {
     return fun->script()->function() == fun;
 }
 
 JS_FRIEND_API(JSFunction *)
 js::DefineFunctionWithReserved(JSContext *cx, JSObject *obj, const char *name, JSNative call,
                                uintN nargs, uintN attrs)
 {
+    RootObject objRoot(cx, &obj);
+
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     JSAtom *atom = js_Atomize(cx, name, strlen(name));
     if (!atom)
         return NULL;
-    return js_DefineFunction(cx, obj, ATOM_TO_JSID(atom), call, nargs, attrs,
+    return js_DefineFunction(cx, objRoot, ATOM_TO_JSID(atom), call, nargs, attrs,
                              JSFunction::ExtendedFinalizeKind);
 }
 
 JS_FRIEND_API(JSFunction *)
 js::NewFunctionWithReserved(JSContext *cx, JSNative native, uintN nargs, uintN flags,
                             JSObject *parent, const char *name)
 {
+    RootObject parentRoot(cx, &parent);
+
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     JSAtom *atom;
 
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, parent);
 
     if (!name) {
         atom = NULL;
     } else {
         atom = js_Atomize(cx, name, strlen(name));
         if (!atom)
             return NULL;
     }
 
-    return js_NewFunction(cx, NULL, native, nargs, flags, parent, atom,
+    return js_NewFunction(cx, NULL, native, nargs, flags, parentRoot, atom,
                           JSFunction::ExtendedFinalizeKind);
 }
 
 JS_FRIEND_API(JSFunction *)
 js::NewFunctionByIdWithReserved(JSContext *cx, JSNative native, uintN nargs, uintN flags, JSObject *parent,
                                 jsid id)
 {
+    RootObject parentRoot(cx, &parent);
+
     JS_ASSERT(JSID_IS_STRING(id));
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, parent);
 
-    return js_NewFunction(cx, NULL, native, nargs, flags, parent, JSID_TO_ATOM(id),
+    return js_NewFunction(cx, NULL, native, nargs, flags, parentRoot, JSID_TO_ATOM(id),
                           JSFunction::ExtendedFinalizeKind);
 }
 
 JS_FRIEND_API(JSObject *)
 js::InitClassWithReserved(JSContext *cx, JSObject *obj, JSObject *parent_proto,
                           JSClass *clasp, JSNative constructor, uintN nargs,
                           JSPropertySpec *ps, JSFunctionSpec *fs,
                           JSPropertySpec *static_ps, JSFunctionSpec *static_fs)
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, parent_proto);
-    return js_InitClass(cx, obj, parent_proto, Valueify(clasp), constructor,
+    RootObject objRoot(cx, &obj);
+    return js_InitClass(cx, objRoot, parent_proto, Valueify(clasp), constructor,
                         nargs, ps, fs, static_ps, static_fs, NULL,
                         JSFunction::ExtendedFinalizeKind);
 }
 
 JS_FRIEND_API(const Value &)
 js::GetFunctionNativeReserved(JSObject *fun, size_t which)
 {
     JS_ASSERT(fun->toFunction()->isNative());
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -126,23 +126,27 @@ js::ArgumentsObject *
 ArgumentsObject::create(JSContext *cx, uint32_t argc, JSObject &callee, StackFrame *fp)
 {
     JS_ASSERT(argc <= StackSpace::ARGS_LENGTH_MAX);
 
     JSObject *proto = callee.getGlobal()->getOrCreateObjectPrototype(cx);
     if (!proto)
         return NULL;
 
-    TypeObject *type = proto->getNewType(cx);
+    RootedVarTypeObject type(cx);
+
+    type = proto->getNewType(cx);
     if (!type)
         return NULL;
 
     bool strict = callee.toFunction()->inStrictMode();
     Class *clasp = strict ? &StrictArgumentsObjectClass : &NormalArgumentsObjectClass;
-    Shape *emptyArgumentsShape =
+
+    RootedVarShape emptyArgumentsShape(cx);
+    emptyArgumentsShape =
         EmptyShape::getInitialShape(cx, clasp, proto,
                                     proto->getParent(), FINALIZE_KIND,
                                     BaseShape::INDEXED);
     if (!emptyArgumentsShape)
         return NULL;
 
     ArgumentsData *data = (ArgumentsData *)
         cx->malloc_(offsetof(ArgumentsData, slots) + argc * sizeof(Value));
@@ -618,22 +622,25 @@ Class js::DeclEnvClass = {
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub
 };
 
 static inline JSObject *
 NewDeclEnvObject(JSContext *cx, StackFrame *fp)
 {
-    types::TypeObject *type = cx->compartment->getEmptyType(cx);
+    RootedVarTypeObject type(cx);
+    type = cx->compartment->getEmptyType(cx);
     if (!type)
         return NULL;
 
     JSObject *parent = fp->scopeChain().getGlobal();
-    Shape *emptyDeclEnvShape =
+
+    RootedVarShape emptyDeclEnvShape(cx);
+    emptyDeclEnvShape =
         EmptyShape::getInitialShape(cx, &DeclEnvClass, NULL,
                                     parent, CallObject::DECL_ENV_FINALIZE_KIND);
     if (!emptyDeclEnvShape)
         return NULL;
 
     JSObject *envobj = JSObject::create(cx, CallObject::DECL_ENV_FINALIZE_KIND,
                                         emptyDeclEnvShape, type, NULL);
     if (!envobj)
@@ -1199,16 +1206,18 @@ static const uint16_t poisonPillProps[] 
     ATOM_OFFSET(caller),
 };
 
 static JSBool
 fun_enumerate(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isFunction());
 
+    RootObject root(cx, &obj);
+
     jsid id;
     bool found;
 
     if (!obj->isBoundFunction()) {
         id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
         if (!obj->hasProperty(cx, id, &found, JSRESOLVE_QUALIFIED))
             return false;
     }
@@ -1279,17 +1288,18 @@ ResolveInterpretedFunctionPrototype(JSCo
 
 static JSBool
 fun_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
             JSObject **objp)
 {
     if (!JSID_IS_ATOM(id))
         return true;
 
-    JSFunction *fun = obj->toFunction();
+    RootedVarFunction fun(cx);
+    fun = obj->toFunction();
 
     if (JSID_IS_ATOM(id, cx->runtime->atomState.classPrototypeAtom)) {
         /*
          * Native or "built-in" functions do not have a .prototype property per
          * ECMA-262, or (Object.prototype, Function.prototype, etc.) have that
          * property created eagerly.
          *
          * ES5 15.3.4: the non-native function object named Function.prototype
@@ -1297,65 +1307,65 @@ fun_resolve(JSContext *cx, JSObject *obj
          *
          * ES5 15.3.4.5: bound functions don't have a prototype property. The
          * isNative() test covers this case because bound functions are native
          * functions by definition/construction.
          */
         if (fun->isNative() || fun->isFunctionPrototype())
             return true;
 
-        if (!ResolveInterpretedFunctionPrototype(cx, obj))
+        if (!ResolveInterpretedFunctionPrototype(cx, fun))
             return false;
-        *objp = obj;
+        *objp = fun;
         return true;
     }
 
     if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom) ||
         JSID_IS_ATOM(id, cx->runtime->atomState.nameAtom)) {
         JS_ASSERT(!IsInternalFunctionObject(obj));
 
         Value v;
         if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom))
             v.setInt32(fun->nargs);
         else
             v.setString(fun->atom ? fun->atom : cx->runtime->emptyString);
         
-        if (!DefineNativeProperty(cx, obj, id, v, JS_PropertyStub, JS_StrictPropertyStub,
+        if (!DefineNativeProperty(cx, fun, id, v, JS_PropertyStub, JS_StrictPropertyStub,
                                   JSPROP_PERMANENT | JSPROP_READONLY, 0, 0)) {
             return false;
         }
-        *objp = obj;
+        *objp = fun;
         return true;
     }
 
     for (uintN i = 0; i < ArrayLength(poisonPillProps); i++) {
         const uint16_t offset = poisonPillProps[i];
 
         if (JSID_IS_ATOM(id, OFFSET_TO_ATOM(cx->runtime, offset))) {
-            JS_ASSERT(!IsInternalFunctionObject(obj));
+            JS_ASSERT(!IsInternalFunctionObject(fun));
 
             PropertyOp getter;
             StrictPropertyOp setter;
             uintN attrs = JSPROP_PERMANENT;
-            if (fun->isInterpreted() ? fun->inStrictMode() : obj->isBoundFunction()) {
-                JSObject *throwTypeError = obj->getThrowTypeError();
+            if (fun->isInterpreted() ? fun->inStrictMode() : fun->isBoundFunction()) {
+                JSObject *throwTypeError = fun->getThrowTypeError();
 
                 getter = CastAsPropertyOp(throwTypeError);
                 setter = CastAsStrictPropertyOp(throwTypeError);
                 attrs |= JSPROP_GETTER | JSPROP_SETTER;
             } else {
                 getter = fun_getProperty;
                 setter = JS_StrictPropertyStub;
             }
 
-            if (!DefineNativeProperty(cx, obj, id, UndefinedValue(), getter, setter,
+            if (!DefineNativeProperty(cx, fun, id, UndefinedValue(), getter, setter,
                                       attrs, 0, 0)) {
                 return false;
             }
-            *objp = obj;
+            *objp = fun;
             return true;
         }
     }
 
     return true;
 }
 
 #if JS_HAS_XDR
@@ -1382,28 +1392,27 @@ js_XDRFunctionObject(JSXDRState *xdr, JS
                                      name);
             }
             return false;
         }
         firstword = !!fun->atom;
         flagsword = (fun->nargs << 16) | fun->flags;
         script = fun->script();
     } else {
-        fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, NULL, NULL);
+        RootedVarObject parent(cx, NULL);
+        fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, parent, NULL);
         if (!fun)
             return false;
         if (!fun->clearParent(cx))
             return false;
         if (!fun->clearType(cx))
             return false;
         script = NULL;
     }
 
-    AutoObjectRooter tvr(cx, fun);
-
     if (!JS_XDRUint32(xdr, &firstword))
         return false;
     if ((firstword & 1U) && !js_XDRAtom(xdr, &fun->atom))
         return false;
     if (!JS_XDRUint32(xdr, &flagsword))
         return false;
 
     if (!js_XDRScript(xdr, &script))
@@ -1859,17 +1868,18 @@ fun_bind(JSContext *cx, uintN argc, Valu
     Value &thisv = args.thisv();
 
     /* Step 2. */
     if (!js_IsCallable(thisv)) {
         ReportIncompatibleMethod(cx, args, &FunctionClass);
         return false;
     }
 
-    JSObject *target = &thisv.toObject();
+    RootedVarObject target(cx);
+    target = &thisv.toObject();
 
     /* Step 3. */
     Value *boundArgs = NULL;
     uintN argslen = 0;
     if (args.length() > 1) {
         boundArgs = args.array() + 1;
         argslen = args.length() - 1;
     }
@@ -1939,17 +1949,18 @@ JSFunctionSpec function_methods[] = {
 };
 
 JSBool
 Function(JSContext *cx, uintN argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* Block this call if security callbacks forbid it. */
-    GlobalObject *global = args.callee().getGlobal();
+    RootedVar<GlobalObject*> global(cx);
+    global = args.callee().getGlobal();
     if (!global->isRuntimeCodeGenEnabled(cx)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CSP_BLOCKED_FUNCTION);
         return false;
     }
 
     Bindings bindings(cx);
 
     const char *filename;
@@ -2139,17 +2150,17 @@ LookupInterpretedFunctionPrototype(JSCon
     JS_ASSERT(!shape->isMethod());
     return shape;
 }
 
 } /* namespace js */
 
 JSFunction *
 js_NewFunction(JSContext *cx, JSObject *funobj, Native native, uintN nargs,
-               uintN flags, JSObject *parent, JSAtom *atom, js::gc::AllocKind kind)
+               uintN flags, HandleObject parent, JSAtom *atom, js::gc::AllocKind kind)
 {
     JS_ASSERT(kind == JSFunction::FinalizeKind || kind == JSFunction::ExtendedFinalizeKind);
     JS_ASSERT(sizeof(JSFunction) <= gc::Arena::thingSize(JSFunction::FinalizeKind));
     JS_ASSERT(sizeof(FunctionExtended) <= gc::Arena::thingSize(JSFunction::ExtendedFinalizeKind));
 
     JSFunction *fun;
 
     if (funobj) {
@@ -2300,22 +2311,25 @@ js_NewFlatClosure(JSContext *cx, JSFunct
 
     for (uint32_t i = 0, n = uva->length; i < n; i++)
         closure->initFlatClosureUpvar(i, GetUpvar(cx, level, uva->vector[i]));
 
     return closure;
 }
 
 JSFunction *
-js_DefineFunction(JSContext *cx, JSObject *obj, jsid id, Native native,
+js_DefineFunction(JSContext *cx, HandleObject obj, jsid id, Native native,
                   uintN nargs, uintN attrs, AllocKind kind)
 {
+    RootId idRoot(cx, &id);
+
     PropertyOp gop;
     StrictPropertyOp sop;
-    JSFunction *fun;
+
+    RootedVarFunction fun(cx);
 
     if (attrs & JSFUN_STUB_GSOPS) {
         /*
          * JSFUN_STUB_GSOPS is a request flag only, not stored in fun->flags or
          * the defined property's attributes. This allows us to encode another,
          * internal flag using the same bit, JSFUN_EXPR_CLOSURE -- see jsfun.h
          * for more on this.
          */
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -305,31 +305,31 @@ JSObject::toFunction() const
     return static_cast<const JSFunction *>(this);
 }
 
 extern JSString *
 fun_toStringHelper(JSContext *cx, JSObject *obj, uintN indent);
 
 extern JSFunction *
 js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs,
-               uintN flags, JSObject *parent, JSAtom *atom,
+               uintN flags, js::HandleObject parent, JSAtom *atom,
                js::gc::AllocKind kind = JSFunction::FinalizeKind);
 
 extern JSFunction * JS_FASTCALL
 js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent, JSObject *proto,
                        js::gc::AllocKind kind = JSFunction::FinalizeKind);
 
 extern JSFunction * JS_FASTCALL
 js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain);
 
 extern JSFunction *
 js_NewFlatClosure(JSContext *cx, JSFunction *fun);
 
 extern JSFunction *
-js_DefineFunction(JSContext *cx, JSObject *obj, jsid id, JSNative native,
+js_DefineFunction(JSContext *cx, js::HandleObject obj, jsid id, JSNative native,
                   uintN nargs, uintN flags,
                   js::gc::AllocKind kind = JSFunction::FinalizeKind);
 
 /*
  * Flags for js_ValueToFunction and js_ReportIsNotFunction.
  */
 #define JSV2F_CONSTRUCT         INITIAL_CONSTRUCT
 #define JSV2F_SEARCH_STACK      0x10000
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -3379,16 +3379,99 @@ RunDebugGC(JSContext *cx)
     rt->gcTriggerCompartment = rt->gcDebugCompartmentGC ? cx->compartment : NULL;
     if (rt->gcTriggerCompartment == rt->atomsCompartment)
         rt->gcTriggerCompartment = NULL;
 
     RunLastDitchGC(cx);
 #endif
 }
 
+#if defined(DEBUG) && defined(JSGC_ROOT_ANALYSIS) && !defined(JS_THREADSAFE)
+
+static void
+CheckStackRoot(JSTracer *trc, jsuword *w)
+{
+    /* Mark memory as defined for valgrind, as in MarkWordConservatively. */
+#ifdef JS_VALGRIND
+    VALGRIND_MAKE_MEM_DEFINED(&w, sizeof(w));
+#endif
+
+    ConservativeGCTest test = MarkIfGCThingWord(trc, *w, DONT_MARK_THING);
+
+    if (test == CGCT_VALID) {
+        JSContext *iter = NULL;
+        bool matched = false;
+        JSRuntime *rt = trc->context->runtime;
+        while (JSContext *acx = js_ContextIterator(rt, JS_TRUE, &iter)) {
+            for (unsigned i = 0; i < THING_ROOT_COUNT; i++) {
+                Root<Cell*> *rooter = acx->thingGCRooters[i];
+                while (rooter) {
+                    if (rooter->address() == (Cell **) w)
+                        matched = true;
+                    rooter = rooter->previous();
+                }
+            }
+            CheckRoot *check = acx->checkGCRooters;
+            while (check) {
+                if (check->contains((uint8 *) w, sizeof(w)))
+                    matched = true;
+                check = check->previous();
+            }
+        }
+        if (!matched) {
+            /*
+             * Only poison the last byte in the word. It is easy to get
+             * accidental collisions when a value that does not occupy a full
+             * word is used to overwrite a now-dead GC thing pointer. In this
+             * case we want to avoid damaging the smaller value.
+             */
+            PoisonPtr(w);
+        }
+    }
+}
+
+static void
+CheckStackRootsRange(JSTracer *trc, jsuword *begin, jsuword *end)
+{
+    JS_ASSERT(begin <= end);
+    for (jsuword *i = begin; i != end; ++i)
+        CheckStackRoot(trc, i);
+}
+
+void
+CheckStackRoots(JSContext *cx)
+{
+    AutoCopyFreeListToArenas copy(cx->runtime);
+
+    JSTracer checker;
+    JS_TRACER_INIT(&checker, cx, EmptyMarkCallback);
+
+    ThreadData *td = JS_THREAD_DATA(cx);
+
+    ConservativeGCThreadData *ctd = &td->conservativeGC;
+    ctd->recordStackTop();
+
+    JS_ASSERT(ctd->hasStackToScan());
+    jsuword *stackMin, *stackEnd;
+#if JS_STACK_GROWTH_DIRECTION > 0
+    stackMin = td->nativeStackBase;
+    stackEnd = ctd->nativeStackTop;
+#else
+    stackMin = ctd->nativeStackTop + 1;
+    stackEnd = td->nativeStackBase;
+#endif
+
+    JS_ASSERT(stackMin <= stackEnd);
+    CheckStackRootsRange(&checker, stackMin, stackEnd);
+    CheckStackRootsRange(&checker, ctd->registerSnapshot.words,
+                         ArrayEnd(ctd->registerSnapshot.words));
+}
+
+#endif /* DEBUG && JSGC_ROOT_ANALYSIS && !JS_THREADSAFE */
+
 #ifdef JS_GC_ZEAL
 
 /*
  * Write barrier verification
  *
  * The next few functions are for incremental write barrier verification. When
  * StartVerifyBarriers is called, a snapshot is taken of all objects in the GC
  * heap and saved in an explicit graph data structure. Later, EndVerifyBarriers
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1832,16 +1832,25 @@ namespace gc {
 
 JSCompartment *
 NewCompartment(JSContext *cx, JSPrincipals *principals);
 
 /* Tries to run a GC no matter what (used for GC zeal). */
 void
 RunDebugGC(JSContext *cx);
 
+#if defined(JSGC_ROOT_ANALYSIS) && defined(DEBUG) && !defined(JS_THREADSAFE)
+/* Overwrites stack references to GC things which have not been rooted. */
+void CheckStackRoots(JSContext *cx);
+
+inline void MaybeCheckStackRoots(JSContext *cx) { CheckStackRoots(cx); }
+#else
+inline void MaybeCheckStackRoots(JSContext *cx) {}
+#endif
+
 const int ZealPokeThreshold = 1;
 const int ZealAllocThreshold = 2;
 const int ZealVerifierThreshold = 4;
 
 #ifdef JS_GC_ZEAL
 
 /* Check that write barriers have been used correctly. See jsgc.cpp. */
 void
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -386,16 +386,18 @@ NewGCThing(JSContext *cx, js::gc::AllocK
     JS_ASSERT(!cx->runtime->gcRunning);
     JS_ASSERT(!JS_THREAD_DATA(cx)->noGCOrAllocationCheck);
 
 #ifdef JS_GC_ZEAL
     if (cx->runtime->needZealousGC())
         js::gc::RunDebugGC(cx);
 #endif
 
+    js::gc::MaybeCheckStackRoots(cx);
+
     JSCompartment *comp = cx->compartment;
     void *t = comp->arenas.allocateFromFreeList(kind, thingSize);
     if (!t)
         t = js::gc::ArenaLists::refillFreeList(cx, kind);
     return static_cast<T *>(t);
 }
 
 /* Alternate form which allocates a GC thing if doing so cannot trigger a GC. */
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -1964,16 +1964,18 @@ TypeCompartment::init(JSContext *cx)
     if (cx && cx->getRunOptions() & JSOPTION_TYPE_INFERENCE)
         inferenceEnabled = true;
 }
 
 TypeObject *
 TypeCompartment::newTypeObject(JSContext *cx, JSScript *script,
                                JSProtoKey key, JSObject *proto, bool unknown)
 {
+    RootObject root(cx, &proto);
+
     TypeObject *object = gc::NewGCThing<TypeObject>(cx, gc::FINALIZE_TYPE_OBJECT, sizeof(TypeObject));
     if (!object)
         return NULL;
     new(object) TypeObject(proto, key == JSProto_Function, unknown);
 
     if (!cx->typeInferenceEnabled())
         object->flags |= OBJECT_FLAG_UNKNOWN_MASK;
     else
@@ -5171,17 +5173,21 @@ TypeMonitorResult(JSContext *cx, JSScrip
     types->addType(cx, type);
 }
 
 bool
 TypeScript::SetScope(JSContext *cx, JSScript *script, JSObject *scope)
 {
     JS_ASSERT(script->types && !script->types->hasScope());
 
+    Root<JSScript*> scriptRoot(cx, &script);
+    RootObject scopeRoot(cx, &scope);
+
     JSFunction *fun = script->function();
+    bool nullClosure = fun && fun->isNullClosure();
 
     JS_ASSERT_IF(!fun, !script->isOuterFunction && !script->isInnerFunction);
     JS_ASSERT_IF(!scope, fun && !script->isInnerFunction);
 
     /*
      * The scope object must be the initial one for the script, before any call
      * object has been created in the heavyweight case.
      */
@@ -5202,17 +5208,17 @@ TypeScript::SetScope(JSContext *cx, JSSc
      * every time a call object is created from the bindings.
      */
     if (!script->bindings.setParent(cx, script->types->global))
         return false;
 
     if (!cx->typeInferenceEnabled())
         return true;
 
-    if (!script->isInnerFunction || fun->isNullClosure()) {
+    if (!script->isInnerFunction || nullClosure) {
         /*
          * Outermost functions need nesting information if there are inner
          * functions directly nested in them.
          */
         if (script->isOuterFunction) {
             script->types->nesting = cx->new_<TypeScriptNesting>();
             if (!script->types->nesting)
                 return false;
@@ -5806,19 +5812,16 @@ JSObject::setNewTypeUnknown(JSContext *c
     }
 
     return true;
 }
 
 TypeObject *
 JSObject::getNewType(JSContext *cx, JSFunction *fun)
 {
-    if (!setDelegate(cx))
-        return NULL;
-
     TypeObjectSet &table = cx->compartment->newTypeObjects;
 
     if (!table.initialized() && !table.init())
         return NULL;
 
     TypeObjectSet::AddPtr p = table.lookupForAdd(this);
     if (p) {
         TypeObject *type = *p;
@@ -5835,49 +5838,54 @@ JSObject::getNewType(JSContext *cx, JSFu
          * 'prototype' property of some scripted function.
          */
         if (type->newScript && type->newScript->fun != fun)
             type->clearNewScript(cx);
 
         return type;
     }
 
-    bool markUnknown = lastProperty()->hasObjectFlag(BaseShape::NEW_TYPE_UNKNOWN);
+    RootedVarObject self(cx, this);
+
+    if (!setDelegate(cx))
+        return NULL;
+
+    bool markUnknown = self->lastProperty()->hasObjectFlag(BaseShape::NEW_TYPE_UNKNOWN);
 
     TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL,
-                                                            JSProto_Object, this, markUnknown);
+                                                            JSProto_Object, self, markUnknown);
     if (!type)
         return NULL;
 
-    if (!table.relookupOrAdd(p, this, type))
+    if (!table.relookupOrAdd(p, self, type))
         return NULL;
 
     if (!cx->typeInferenceEnabled())
         return type;
 
     AutoEnterTypeInference enter(cx);
 
     /*
      * Set the special equality flag for types whose prototype also has the
      * flag set. This is a hack, :XXX: need a real correspondence between
      * types and the possible js::Class of objects with that type.
      */
-    if (hasSpecialEquality())
+    if (self->hasSpecialEquality())
         type->flags |= OBJECT_FLAG_SPECIAL_EQUALITY;
 
     if (fun)
         CheckNewScriptProperties(cx, type, fun);
 
 #if JS_HAS_XML_SUPPORT
     /* Special case for XML object equality, see makeLazyType(). */
-    if (isXML() && !type->unknownProperties())
+    if (self->isXML() && !type->unknownProperties())
         type->flags |= OBJECT_FLAG_UNKNOWN_MASK;
 #endif
 
-    if (getClass()->ext.equality)
+    if (self->getClass()->ext.equality)
         type->flags |= OBJECT_FLAG_SPECIAL_EQUALITY;
 
     /*
      * The new type is not present in any type sets, so mark the object as
      * unknown in all type sets it appears in. This allows the prototype of
      * such objects to mutate freely without triggering an expensive walk of
      * the compartment's type sets. (While scripts normally don't mutate
      * __proto__, the browser will for proxies and such, and we need to
@@ -5887,16 +5895,18 @@ JSObject::getNewType(JSContext *cx, JSFu
         type->flags |= OBJECT_FLAG_SETS_MARKED_UNKNOWN;
 
     return type;
 }
 
 TypeObject *
 JSCompartment::getLazyType(JSContext *cx, JSObject *proto)
 {
+    gc::MaybeCheckStackRoots(cx);
+
     TypeObjectSet &table = cx->compartment->lazyTypeObjects;
 
     if (!table.initialized() && !table.init())
         return NULL;
 
     TypeObjectSet::AddPtr p = table.lookupForAdd(proto);
     if (p) {
         TypeObject *type = *p;
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -873,16 +873,18 @@ struct TypeObject : gc::Cell
      * from all the compartment's type objects.
      */
     void finalize(JSContext *cx, bool background) {}
 
     static inline void writeBarrierPre(TypeObject *type);
     static inline void writeBarrierPost(TypeObject *type, void *addr);
     static inline void readBarrier(TypeObject *type);
 
+    static inline ThingRootKind rootKind() { return THING_ROOT_TYPE_OBJECT; }
+
   private:
     inline uint32_t basePropertyCount() const;
     inline void setBasePropertyCount(uint32_t count);
 
     static void staticAsserts() {
         JS_STATIC_ASSERT(offsetof(TypeObject, proto) == offsetof(js::shadow::TypeObject, proto));
     }
 };
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -1322,23 +1322,29 @@ inline bool
 JSScript::ensureHasTypes(JSContext *cx)
 {
     return types || makeTypes(cx);
 }
 
 inline bool
 JSScript::ensureRanAnalysis(JSContext *cx, JSObject *scope)
 {
-    if (!ensureHasTypes(cx))
-        return false;
-    if (!types->hasScope() && !js::types::TypeScript::SetScope(cx, this, scope))
+    JSScript *self = this;
+
+    if (!self->ensureHasTypes(cx))
         return false;
-    if (!hasAnalysis() && !makeAnalysis(cx))
+    if (!self->types->hasScope()) {
+        js::CheckRoot root(cx, &self);
+        js::RootObject objRoot(cx, &scope);
+        if (!js::types::TypeScript::SetScope(cx, self, scope))
+            return false;
+    }
+    if (!self->hasAnalysis() && !self->makeAnalysis(cx))
         return false;
-    JS_ASSERT(analysis()->ranBytecode());
+    JS_ASSERT(self->analysis()->ranBytecode());
     return true;
 }
 
 inline bool
 JSScript::ensureRanInference(JSContext *cx)
 {
     if (!ensureRanAnalysis(cx, NULL))
         return false;
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -208,17 +208,16 @@ js::GetScopeChain(JSContext *cx, StackFr
      * common with subsequent steps to include in the loop.
      *
      * js_CloneBlockObject leaves the clone's parent slot uninitialized. We
      * populate it below.
      */
     JSObject *innermostNewChild = js_CloneBlockObject(cx, sharedBlock, fp);
     if (!innermostNewChild)
         return NULL;
-    AutoObjectRooter tvr(cx, innermostNewChild);
 
     /*
      * Clone our way towards outer scopes until we reach the innermost
      * enclosing function, or the innermost block we've already cloned.
      */
     JSObject *newChild = innermostNewChild;
     for (;;) {
         JS_ASSERT(newChild->getProto() == sharedBlock);
@@ -639,41 +638,43 @@ InitSharpSlots(JSContext *cx, StackFrame
 #endif
 
 bool
 js::ExecuteKernel(JSContext *cx, JSScript *script, JSObject &scopeChain, const Value &thisv,
                   ExecuteType type, StackFrame *evalInFrame, Value *result)
 {
     JS_ASSERT_IF(evalInFrame, type == EXECUTE_DEBUG);
 
+    Root<JSScript*> scriptRoot(cx, &script);
+
     if (script->isEmpty()) {
         if (result)
             result->setUndefined();
         return true;
     }
 
     ExecuteFrameGuard efg;
     if (!cx->stack.pushExecuteFrame(cx, script, thisv, scopeChain, type, evalInFrame, &efg))
         return false;
 
+    if (!script->ensureRanAnalysis(cx, &scopeChain))
+        return false;
+
     /* Give strict mode eval its own fresh lexical environment. */
     StackFrame *fp = efg.fp();
     if (fp->isStrictEvalFrame() && !CreateEvalCallObject(cx, fp))
         return false;
 
 #if JS_HAS_SHARP_VARS
     if (script->hasSharps && !InitSharpSlots(cx, fp))
         return false;
 #endif
 
     Probes::startExecution(cx, script);
 
-    if (!script->ensureRanAnalysis(cx, &scopeChain))
-        return false;
-
     TypeScript::SetThis(cx, script, fp->thisValue());
 
     AutoPreserveEnumerators preserve(cx);
     JSBool ok = RunScript(cx, script, fp);
     if (result && ok)
         *result = fp->returnValue();
 
     if (fp->isStrictEvalFrame())
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -211,20 +211,25 @@ Enumerate(JSContext *cx, JSObject *obj, 
 
     return true;
 }
 
 static bool
 EnumerateNativeProperties(JSContext *cx, JSObject *obj, JSObject *pobj, uintN flags, IdSet &ht,
                           AutoIdVector *props)
 {
+    RootObject objRoot(cx, &obj);
+    RootObject pobjRoot(cx, &pobj);
+
     size_t initialLength = props->length();
 
     /* Collect all unique properties from this object's scope. */
-    for (Shape::Range r = pobj->lastProperty()->all(); !r.empty(); r.popFront()) {
+    Shape::Range r = pobj->lastProperty()->all();
+    Shape::Range::Root root(cx, &r);
+    for (; !r.empty(); r.popFront()) {
         const Shape &shape = r.front();
 
         if (!JSID_IS_DEFAULT_XML_NAMESPACE(shape.propid()) &&
             !Enumerate(cx, obj, pobj, shape.propid(), shape.enumerable(), flags, ht, props))
         {
             return false;
         }
     }
@@ -289,17 +294,20 @@ struct SortComparatorIds
 
 static bool
 Snapshot(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector *props)
 {
     IdSet ht(cx);
     if (!ht.init(32))
         return NULL;
 
-    JSObject *pobj = obj;
+    RootObject objRoot(cx, &obj);
+    RootedVarObject pobj(cx);
+    pobj = obj;
+
     do {
         Class *clasp = pobj->getClass();
         if (pobj->isNative() &&
             !pobj->getOps()->enumerate &&
             !(clasp->flags & JSCLASS_NEW_ENUMERATE)) {
             if (!clasp->enumerate(cx, pobj))
                 return false;
             if (!EnumerateNativeProperties(cx, obj, pobj, flags, ht, props))
@@ -468,22 +476,24 @@ Compare(T *a, T *b, size_t c)
     }
     return true;
 }
 
 static inline JSObject *
 NewIteratorObject(JSContext *cx, uintN flags)
 {
     if (flags & JSITER_ENUMERATE) {
-        types::TypeObject *type = cx->compartment->getEmptyType(cx);
+        RootedVarTypeObject type(cx);
+        type = cx->compartment->getEmptyType(cx);
         if (!type)
             return NULL;
 
-        Shape *emptyEnumeratorShape = EmptyShape::getInitialShape(cx, &IteratorClass, NULL, NULL,
-                                                                  ITERATOR_FINALIZE_KIND);
+        RootedVarShape emptyEnumeratorShape(cx);
+        emptyEnumeratorShape = EmptyShape::getInitialShape(cx, &IteratorClass, NULL, NULL,
+                                                           ITERATOR_FINALIZE_KIND);
         if (!emptyEnumeratorShape)
             return NULL;
 
         JSObject *obj = JSObject::create(cx, ITERATOR_FINALIZE_KIND,
                                          emptyEnumeratorShape, type, NULL);
         if (!obj)
             return NULL;
 
@@ -923,26 +933,26 @@ SuppressDeletedPropertyHelper(JSContext 
             HeapId *props_end = ni->end();
             for (HeapId *idp = props_cursor; idp < props_end; ++idp) {
                 if (predicate(*idp)) {
                     /*
                      * Check whether another property along the prototype chain
                      * became visible as a result of this deletion.
                      */
                     if (obj->getProto()) {
-                        AutoObjectRooter proto(cx, obj->getProto());
-                        AutoObjectRooter obj2(cx);
+                        JSObject *proto = obj->getProto();
+                        JSObject *obj2;
                         JSProperty *prop;
-                        if (!proto.object()->lookupGeneric(cx, *idp, obj2.addr(), &prop))
+                        if (!proto->lookupGeneric(cx, *idp, &obj2, &prop))
                             return false;
                         if (prop) {
                             uintN attrs;
-                            if (obj2.object()->isNative())
+                            if (obj2->isNative())
                                 attrs = ((Shape *) prop)->attributes();
-                            else if (!obj2.object()->getGenericAttributes(cx, *idp, &attrs))
+                            else if (!obj2->getGenericAttributes(cx, *idp, &attrs))
                                 return false;
 
                             if (attrs & JSPROP_ENUMERATE)
                                 continue;
                         }
                     }
 
                     /*
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1928,17 +1928,17 @@ obj_keys(JSContext *cx, uintN argc, Valu
     if (!aobj)
         return false;
     vp->setObject(*aobj);
 
     return true;
 }
 
 static bool
-HasProperty(JSContext* cx, JSObject* obj, jsid id, Value* vp, bool *foundp)
+HasProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, bool *foundp)
 {
     if (!obj->hasProperty(cx, id, foundp, JSRESOLVE_QUALIFIED | JSRESOLVE_DETECTING))
         return false;
     if (!*foundp) {
         vp->setUndefined();
         return true;
     }
 
@@ -1962,26 +1962,26 @@ PropDesc::PropDesc()
     hasValue(false),
     hasWritable(false),
     hasEnumerable(false),
     hasConfigurable(false)
 {
 }
 
 bool
-PropDesc::initialize(JSContext* cx, const Value &origval, bool checkAccessors)
+PropDesc::initialize(JSContext *cx, const Value &origval, bool checkAccessors)
 {
     Value v = origval;
 
     /* 8.10.5 step 1 */
     if (v.isPrimitive()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
         return false;
     }
-    JSObject* desc = &v.toObject();
+    JSObject *desc = &v.toObject();
 
     /* Make a copy of the descriptor. We might need it later. */
     pd = v;
 
     /* Start with the proper defaults. */
     attrs = JSPROP_PERMANENT | JSPROP_READONLY;
 
     bool found;
@@ -2488,17 +2488,17 @@ js_DefineOwnProperty(JSContext *cx, JSOb
     if (!DefineProperty(cx, obj, id, *desc, true, &rval))
         return false;
     *bp = !!rval;
     return true;
 }
 
 /* ES5 15.2.3.6: Object.defineProperty(O, P, Attributes) */
 static JSBool
-obj_defineProperty(JSContext* cx, uintN argc, Value* vp)
+obj_defineProperty(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *obj;
     if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.defineProperty", &obj))
         return false;
 
     jsid id;
     if (!ValueToId(cx, argc >= 2 ? vp[3] : UndefinedValue(), &id))
         return JS_FALSE;
@@ -2554,31 +2554,31 @@ DefineProperties(JSContext *cx, JSObject
 extern JSBool
 js_PopulateObject(JSContext *cx, JSObject *newborn, JSObject *props)
 {
     return DefineProperties(cx, newborn, props);
 }
 
 /* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
 static JSBool
-obj_defineProperties(JSContext* cx, uintN argc, Value* vp)
+obj_defineProperties(JSContext *cx, uintN argc, Value *vp)
 {
     /* Steps 1 and 7. */
     JSObject *obj;
     if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.defineProperties", &obj))
         return false;
     vp->setObject(*obj);
 
     /* Step 2. */
     if (argc < 2) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
                              "Object.defineProperties", "0", "s");
         return false;
     }
-    JSObject* props = ToObject(cx, &vp[3]);
+    JSObject *props = ToObject(cx, &vp[3]);
     if (!props)
         return false;
 
     /* Steps 3-6. */
     return DefineProperties(cx, obj, props);
 }
 
 /* ES5 15.2.3.5: Object.create(O [, Properties]) */
@@ -2699,48 +2699,50 @@ obj_preventExtensions(JSContext *cx, uin
 }
 
 bool
 JSObject::sealOrFreeze(JSContext *cx, ImmutabilityType it)
 {
     assertSameCompartment(cx, this);
     JS_ASSERT(it == SEAL || it == FREEZE);
 
+    RootedVarObject self(cx, this);
+
     AutoIdVector props(cx);
     if (isExtensible()) {
         if (!preventExtensions(cx, &props))
             return false;
     } else {
         if (!GetPropertyNames(cx, this, JSITER_HIDDEN | JSITER_OWNONLY, &props))
             return false;
     }
 
     /* preventExtensions must slowify dense arrays, so we can assign to holes without checks. */
-    JS_ASSERT(!isDenseArray());
+    JS_ASSERT(!self->isDenseArray());
 
     for (size_t i = 0, len = props.length(); i < len; i++) {
         jsid id = props[i];
 
         uintN attrs;
-        if (!getGenericAttributes(cx, id, &attrs))
+        if (!self->getGenericAttributes(cx, id, &attrs))
             return false;
 
         /* Make all attributes permanent; if freezing, make data attributes read-only. */
         uintN new_attrs;
         if (it == FREEZE && !(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
             new_attrs = JSPROP_PERMANENT | JSPROP_READONLY;
         else
             new_attrs = JSPROP_PERMANENT;
 
         /* If we already have the attributes we need, skip the setAttributes call. */
         if ((attrs | new_attrs) == attrs)
             continue;
 
         attrs |= new_attrs;
-        if (!setGenericAttributes(cx, id, &attrs))
+        if (!self->setGenericAttributes(cx, id, &attrs))
             return false;
     }
 
     return true;
 }
 
 bool
 JSObject::isSealedOrFrozen(JSContext *cx, ImmutabilityType it, bool *resultp)
@@ -2910,25 +2912,28 @@ js_Object(JSContext *cx, uintN argc, Val
 static inline JSObject *
 NewObject(JSContext *cx, Class *clasp, types::TypeObject *type, JSObject *parent,
           gc::AllocKind kind)
 {
     JS_ASSERT(clasp != &ArrayClass);
     JS_ASSERT_IF(clasp == &FunctionClass,
                  kind == JSFunction::FinalizeKind || kind == JSFunction::ExtendedFinalizeKind);
 
-    Shape *shape = EmptyShape::getInitialShape(cx, clasp, type->proto, parent, kind);
+    RootTypeObject typeRoot(cx, &type);
+
+    RootedVarShape shape(cx);
+    shape = EmptyShape::getInitialShape(cx, clasp, type->proto, parent, kind);
     if (!shape)
         return NULL;
 
     HeapValue *slots;
     if (!PreallocateObjectDynamicSlots(cx, shape, &slots))
         return NULL;
 
-    JSObject* obj = JSObject::create(cx, kind, shape, type, slots);
+    JSObject *obj = JSObject::create(cx, kind, shape, typeRoot, slots);
     if (!obj)
         return NULL;
 
     Probes::createObject(cx, obj);
     return obj;
 }
 
 JSObject *
@@ -2941,16 +2946,19 @@ js::NewObjectWithGivenProto(JSContext *c
     NewObjectCache &cache = cx->compartment->newObjectCache;
 
     NewObjectCache::EntryIndex entry = -1;
     if (proto && (!parent || parent == proto->getParent()) && !proto->isGlobal()) {
         if (cache.lookupProto(clasp, proto, kind, &entry))
             return cache.newObjectFromHit(cx, entry);
     }
 
+    RootObject protoRoot(cx, &proto);
+    RootObject parentRoot(cx, &parent);
+
     types::TypeObject *type = proto ? proto->getNewType(cx) : cx->compartment->getEmptyType(cx);
     if (!type)
         return NULL;
 
     /*
      * Default parent to the parent of the prototype, which was set from
      * the parent of the prototype's constructor.
      */
@@ -2994,17 +3002,19 @@ js::NewObjectWithClassProto(JSContext *c
     NewObjectCache &cache = cx->compartment->newObjectCache;
 
     NewObjectCache::EntryIndex entry = -1;
     if (parent->isGlobal() && protoKey != JSProto_Null) {
         if (cache.lookupGlobal(clasp, parent->asGlobal(), kind, &entry))
             return cache.newObjectFromHit(cx, entry);
     }
 
-    if (!FindProto(cx, clasp, parent, &proto))
+    RootObject parentRoot(cx, &parent);
+
+    if (!FindProto(cx, clasp, parentRoot, &proto))
         return NULL;
 
     types::TypeObject *type = proto->getNewType(cx);
     if (!type)
         return NULL;
 
     JSObject *obj = NewObject(cx, clasp, type, parent, kind);
     if (!obj)
@@ -3504,80 +3514,88 @@ Class js::WithClass = {
 
 static const gc::AllocKind WITH_FINALIZE_KIND = gc::FINALIZE_OBJECT4;
 
 JS_REQUIRES_STACK JSObject *
 js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth)
 {
     JSObject *obj;
 
-    TypeObject *type = proto->getNewType(cx);
+    RootedVarTypeObject type(cx);
+    type = proto->getNewType(cx);
     if (!type)
         return NULL;
 
     StackFrame *priv = js_FloatingFrameIfGenerator(cx, cx->fp());
 
-    Shape *emptyWithShape = EmptyShape::getInitialShape(cx, &WithClass, proto,
-                                                        parent->getGlobal(),
-                                                        WITH_FINALIZE_KIND);
+    RootedVarShape emptyWithShape(cx);
+    emptyWithShape = EmptyShape::getInitialShape(cx, &WithClass, proto,
+                                                 parent->getGlobal(),
+                                                 WITH_FINALIZE_KIND);
     if (!emptyWithShape)
         return NULL;
 
     obj = JSObject::create(cx, WITH_FINALIZE_KIND, emptyWithShape, type, NULL);
     if (!obj)
         return NULL;
     OBJ_SET_BLOCK_DEPTH(cx, obj, depth);
 
     if (!obj->setInternalScopeChain(cx, parent))
         return NULL;
     obj->setPrivate(priv);
 
-    AutoObjectRooter tvr(cx, obj);
     JSObject *thisp = proto->thisObject(cx);
     if (!thisp)
         return NULL;
 
     assertSameCompartment(cx, obj, thisp);
 
     obj->setWithThis(thisp);
     return obj;
 }
 
 static const uint32_t BLOCK_RESERVED_SLOTS = 2;
 static const gc::AllocKind BLOCK_FINALIZE_KIND = gc::FINALIZE_OBJECT4;
 
 JSObject *
 js_NewBlockObject(JSContext *cx)
 {
-    types::TypeObject *type = cx->compartment->getEmptyType(cx);
+    RootedVarTypeObject type(cx);
+    type = cx->compartment->getEmptyType(cx);
     if (!type)
         return NULL;
 
-    Shape *emptyBlockShape = EmptyShape::getInitialShape(cx, &BlockClass, NULL, NULL,
-                                                         BLOCK_FINALIZE_KIND);
+    RootedVarShape emptyBlockShape(cx);
+    emptyBlockShape = EmptyShape::getInitialShape(cx, &BlockClass, NULL, NULL,
+                                                  BLOCK_FINALIZE_KIND);
     if (!emptyBlockShape)
         return NULL;
 
     return JSObject::create(cx, FINALIZE_OBJECT4, emptyBlockShape, type, NULL);
 }
 
 JSObject *
 js_CloneBlockObject(JSContext *cx, JSObject *proto, StackFrame *fp)
 {
     JS_ASSERT(proto->isStaticBlock());
 
-    TypeObject *type = proto->getNewType(cx);
+    RootedVarTypeObject type(cx);
+    type = proto->getNewType(cx);
     if (!type)
         return NULL;
 
     HeapValue *slots;
     if (!PreallocateObjectDynamicSlots(cx, proto->lastProperty(), &slots))
         return NULL;
 
-    JSObject *clone = JSObject::create(cx, BLOCK_FINALIZE_KIND, proto->lastProperty(), type, slots);
+    RootedVarShape shape(cx);
+    shape = proto->lastProperty();
+
+    JSObject *clone = JSObject::create(cx, BLOCK_FINALIZE_KIND,
+                                       shape, type, slots);
     if (!clone)
         return NULL;
 
     /* Set the parent if necessary, as for call objects. */
     JSObject *global = fp->scopeChain().getGlobal();
     if (global != clone->getParent()) {
         JS_ASSERT(clone->getParent() == NULL);
         if (!clone->setParent(cx, global))
@@ -3680,32 +3698,35 @@ block_setProperty(JSContext *cx, JSObjec
 
 const Shape *
 JSObject::defineBlockVariable(JSContext *cx, jsid id, intN index, bool *redeclared)
 {
     JS_ASSERT(isStaticBlock());
 
     *redeclared = false;
 
+    RootedVarObject self(cx, this);
+
     /* Inline JSObject::addProperty in order to trap the redefinition case. */
-    Shape **spp = nativeSearch(cx, id, true);
-    if (SHAPE_FETCH(spp)) {
+    Shape **spp;
+    Shape *shape = Shape::search(cx, lastProperty(), id, &spp, true);
+    if (shape) {
         *redeclared = true;
         return NULL;
     }
 
     /*
      * Don't convert this object to dictionary mode so that we can clone the
      * block's shape later.
      */
     uint32_t slot = JSSLOT_FREE(&BlockClass) + index;
-    const Shape *shape = addPropertyInternal(cx, id, block_getProperty, block_setProperty,
-                                             slot, JSPROP_ENUMERATE | JSPROP_PERMANENT,
-                                             Shape::HAS_SHORTID, index, spp,
-                                             /* allowDictionary = */ false);
+    shape = self->addPropertyInternal(cx, id, block_getProperty, block_setProperty,
+                                      slot, JSPROP_ENUMERATE | JSPROP_PERMANENT,
+                                      Shape::HAS_SHORTID, index, spp,
+                                      /* allowDictionary = */ false);
     if (!shape)
         return NULL;
     return shape;
 }
 
 JSBool
 JSObject::nonNativeSetProperty(JSContext *cx, jsid id, js::Value *vp, JSBool strict)
 {
@@ -4174,18 +4195,16 @@ js_XDRBlockObject(JSXDRState *xdr, JSObj
          */
         if (parentId == NO_PARENT_INDEX)
             parent = NULL;
         else
             parent = xdr->script->getObject(parentId);
         obj->setStaticBlockScopeChain(parent);
     }
 
-    AutoObjectRooter tvr(cx, obj);
-
     if (!JS_XDRUint32(xdr, &depthAndCount))
         return false;
 
     if (xdr->mode == JSXDR_DECODE) {
         depth = uint16_t(depthAndCount >> 16);
         count = uint16_t(depthAndCount);
         obj->setSlot(JSSLOT_BLOCK_DEPTH, Value(Int32Value(depth)));
 
@@ -4309,17 +4328,17 @@ ClearClassObject(JSContext *cx, JSObject
     if (!obj->isGlobal())
         return;
 
     obj->setSlot(key, UndefinedValue());
     obj->setSlot(JSProto_LIMIT + key, UndefinedValue());
 }
 
 JSObject *
-DefineConstructorAndPrototype(JSContext *cx, JSObject *obj, JSProtoKey key, JSAtom *atom,
+DefineConstructorAndPrototype(JSContext *cx, HandleObject obj, JSProtoKey key, HandleAtom atom,
                               JSObject *protoProto, Class *clasp,
                               Native constructor, uintN nargs,
                               JSPropertySpec *ps, JSFunctionSpec *fs,
                               JSPropertySpec *static_ps, JSFunctionSpec *static_fs,
                               JSObject **ctorp, AllocKind ctorKind)
 {
     /*
      * Create a prototype object for this class.
@@ -4345,28 +4364,29 @@ DefineConstructorAndPrototype(JSContext 
 
     /*
      * Create the prototype object.  (GlobalObject::createBlankPrototype isn't
      * used because it parents the prototype object to the global and because
      * it uses WithProto::Given.  FIXME: Undo dependencies on this parentage
      * [which already needs to happen for bug 638316], figure out nicer
      * semantics for null-protoProto, and use createBlankPrototype.)
      */
-    JSObject *proto = NewObjectWithClassProto(cx, clasp, protoProto, obj);
+    RootedVarObject proto(cx);
+    proto = NewObjectWithClassProto(cx, clasp, protoProto, obj);
     if (!proto)
         return NULL;
 
     if (!proto->setSingletonType(cx))
         return NULL;
 
     if (clasp == &ArrayClass && !proto->makeDenseArraySlow(cx))
         return NULL;
 
     /* After this point, control must exit via label bad or out. */
-    JSObject *ctor;
+    RootedVarObject ctor(cx);
     bool named = false;
     bool cached = false;
     if (!constructor) {
         /*
          * Lacking a constructor, name the prototype (e.g., Math) unless this
          * class (a) is anonymous, i.e. for internal use only; (b) the class
          * of obj (the global object) is has a reserved slot indexed by key;
          * and (c) key is not the null key.
@@ -4382,19 +4402,19 @@ DefineConstructorAndPrototype(JSContext 
         ctor = proto;
     } else {
         /*
          * Create the constructor, not using GlobalObject::createConstructor
          * because the constructor currently must have |obj| as its parent.
          * (FIXME: remove this dependency on the exact identity of the parent,
          * perhaps as part of bug 638316.)
          */
-        JSFunction *fun =
-            js_NewFunction(cx, NULL, constructor, nargs, JSFUN_CONSTRUCTOR, obj, atom,
-                           ctorKind);
+        RootedVarFunction fun(cx);
+        fun = js_NewFunction(cx, NULL, constructor, nargs, JSFUN_CONSTRUCTOR, obj, atom,
+                             ctorKind);
         if (!fun)
             goto bad;
         fun->setConstructorClass(clasp);
 
         /*
          * Set the class object early for standard class constructors. Type
          * inference may need to access these, and js_GetClassPrototype will
          * fail if it tries to do a reentrant reconstruction of the class.
@@ -4470,38 +4490,41 @@ IsStandardClassResolved(JSObject *obj, j
 {
     JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
 
     /* If the constructor is undefined, then it hasn't been initialized. */
     return (obj->getReservedSlot(key) != UndefinedValue());
 }
 
 void
-MarkStandardClassInitializedNoProto(JSObject* obj, js::Class *clasp)
+MarkStandardClassInitializedNoProto(JSObject *obj, js::Class *clasp)
 {
     JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
 
     /*
      * We use True so that it's obvious what we're doing (instead of, say,
      * Null, which might be miscontrued as an error in setting Undefined).
      */
     if (obj->getReservedSlot(key) == UndefinedValue())
         obj->setSlot(key, BooleanValue(true));
 }
 
 }
 
 JSObject *
-js_InitClass(JSContext *cx, JSObject *obj, JSObject *protoProto,
+js_InitClass(JSContext *cx, HandleObject obj, JSObject *protoProto,
              Class *clasp, Native constructor, uintN nargs,
              JSPropertySpec *ps, JSFunctionSpec *fs,
              JSPropertySpec *static_ps, JSFunctionSpec *static_fs,
              JSObject **ctorp, AllocKind ctorKind)
 {
-    JSAtom *atom = js_Atomize(cx, clasp->name, strlen(clasp->name));
+    RootObject rootProto(cx, &protoProto);
+
+    RootedVarAtom atom(cx);
+    atom = js_Atomize(cx, clasp->name, strlen(clasp->name));
     if (!atom)
         return NULL;
 
     /*
      * All instances of the class will inherit properties from the prototype
      * object we are about to create (in DefineConstructorAndPrototype), which
      * in turn will inherit from protoProto.
      *
@@ -5000,16 +5023,18 @@ SetProto(JSContext *cx, JSObject *obj, J
 }
 
 }
 
 JSBool
 js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
                   JSObject **objp)
 {
+    RootObject objRoot(cx, &obj);
+
     obj = obj->getGlobal();
     if (!obj->isGlobal()) {
         *objp = NULL;
         return true;
     }
 
     Value v = obj->getReservedSlot(key);
     if (v.isObject()) {
@@ -5036,24 +5061,26 @@ js_GetClassObject(JSContext *cx, JSObjec
     *objp = cobj;
     return true;
 }
 
 JSBool
 js_FindClassObject(JSContext *cx, JSObject *start, JSProtoKey protoKey,
                    Value *vp, Class *clasp)
 {
-    JSObject *obj, *cobj, *pobj;
+    JSObject *cobj, *pobj;
     jsid id;
     JSProperty *prop;
     const Shape *shape;
 
+    RootedVarObject obj(cx);
+
     if (start) {
         obj = start->getGlobal();
-        OBJ_TO_INNER_OBJECT(cx, obj);
+        OBJ_TO_INNER_OBJECT(cx, *obj.address());
     } else {
         obj = GetGlobalForScopeChain(cx);
     }
     if (!obj)
         return false;
 
     if (protoKey != JSProto_Null) {
         JS_ASSERT(JSProto_Null < protoKey);
@@ -5215,16 +5242,19 @@ JSObject::freeSlot(JSContext *cx, uint32
     setSlot(slot, UndefinedValue());
 }
 
 static bool
 PurgeProtoChain(JSContext *cx, JSObject *obj, jsid id)
 {
     const Shape *shape;
 
+    RootObject objRoot(cx, &obj);
+    RootId idRoot(cx, &id);
+
     while (obj) {
         if (!obj->isNative()) {
             obj = obj->getProto();
             continue;
         }
         shape = obj->nativeLookup(cx, id);
         if (shape) {
             PCMETER(JS_PROPERTY_CACHE(cx).pcpurges++);
@@ -5238,16 +5268,19 @@ PurgeProtoChain(JSContext *cx, JSObject 
     }
 
     return true;
 }
 
 bool
 js_PurgeScopeChainHelper(JSContext *cx, JSObject *obj, jsid id)
 {
+    RootObject objRoot(cx, &obj);
+    RootId idRoot(cx, &id);
+
     JS_ASSERT(obj->isDelegate());
     PurgeProtoChain(cx, obj->getProto(), id);
 
     /*
      * We must purge the scope chain only for Call objects as they are the only
      * kind of cacheable non-global object that can gain properties after outer
      * properties with the same names have been cached or traced. Call objects
      * may gain such properties via eval introducing new vars; see bug 490364.
@@ -5340,23 +5373,33 @@ CallAddPropertyHook(JSContext *cx, Class
         }
     }
     return true;
 }
 
 namespace js {
 
 const Shape *
-DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &value,
+DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &value_,
                      PropertyOp getter, StrictPropertyOp setter, uintN attrs,
                      uintN flags, intN shortid, uintN defineHow /* = 0 */)
 {
     JS_ASSERT((defineHow & ~(DNP_CACHE_RESULT | DNP_DONT_PURGE |
                              DNP_SET_METHOD | DNP_SKIP_TYPE)) == 0);
 
+    RootObject objRoot(cx, &obj);
+    RootId idRoot(cx, &id);
+
+    /*
+     * Make a local copy of value, in case a method barrier needs to update the
+     * value to define, and just so addProperty can mutate its inout parameter.
+     */
+    RootedVarValue value(cx);
+    value = value_;
+
     /* Convert string indices to integers if appropriate. */
     id = js_CheckForStringIndex(id);
 
     /*
      * If defining a getter or setter, we must check for its counterpart and
      * update the attributes and property ops.  A getter or setter is really
      * only half of a property.
      */
@@ -5420,61 +5463,55 @@ DefineNativeProperty(JSContext *cx, JSOb
          * Type information for normal native properties should reflect the
          * initial value of the property.
          */
         AddTypePropertyId(cx, obj, id, value);
         if (attrs & JSPROP_READONLY)
             MarkTypePropertyConfigured(cx, obj, id);
     }
 
-    /*
-     * Make a local copy of value, in case a method barrier needs to update the
-     * value to define, and just so addProperty can mutate its inout parameter.
-     */
-    Value valueCopy = value;
-
     if (!shape) {
         /* Add a new property, or replace an existing one of the same id. */
         if (defineHow & DNP_SET_METHOD) {
             JS_ASSERT(clasp == &ObjectClass);
             JS_ASSERT(IsFunctionObject(value));
             JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
             JS_ASSERT(!getter && !setter);
 
-            JSObject *funobj = &value.toObject();
+            JSObject *funobj = &value.raw().toObject();
             if (!funobj->toFunction()->isClonedMethod())
                 flags |= Shape::METHOD;
         }
 
         if (const Shape *existingShape = obj->nativeLookup(cx, id)) {
             if (existingShape->isMethod() &&
-                ObjectValue(*obj->nativeGetMethod(existingShape)) == valueCopy)
+                ObjectValue(*obj->nativeGetMethod(existingShape)) == value)
             {
                 /*
                  * Redefining an existing shape-memoized method object without
                  * changing the property's value, perhaps to change attributes.
                  * Clone now via the method read barrier.
                  */
-                if (!obj->methodReadBarrier(cx, *existingShape, &valueCopy))
+                if (!obj->methodReadBarrier(cx, *existingShape, value.address()))
                     return NULL;
             }
         }
 
         shape = obj->putProperty(cx, id, getter, setter, SHAPE_INVALID_SLOT,
                                  attrs, flags, shortid);
         if (!shape)
             return NULL;
     }
 
     /* Store valueCopy before calling addProperty, in case the latter GC's. */
     if (shape->hasSlot())
-        obj->nativeSetSlot(shape->slot(), valueCopy);
+        obj->nativeSetSlot(shape->slot(), value);
 
     /* XXXbe called with lock held */
-    if (!CallAddPropertyHook(cx, clasp, obj, shape, &valueCopy)) {
+    if (!CallAddPropertyHook(cx, clasp, obj, shape, value.address())) {
         obj->removeProperty(cx, id);
         return NULL;
     }
 
     return shape;
 }
 
 } /* namespace js */
@@ -5495,17 +5532,17 @@ DefineNativeProperty(JSContext *cx, JSOb
  *
  *   - If the resolve hook finds or defines the sought property, set *objp and
  *     *propp appropriately, set *recursedp = false, and return true.
  *
  *   - Otherwise no property was resolved. Set *propp = NULL and *recursedp = false
  *     and return true.
  */
 static JSBool
-CallResolveOp(JSContext *cx, JSObject *start, JSObject *obj, jsid id, uintN flags,
+CallResolveOp(JSContext *cx, JSObject *start, HandleObject obj, HandleId id, uintN flags,
               JSObject **objp, JSProperty **propp, bool *recursedp)
 {
     Class *clasp = obj->getClass();
     JSResolveOp resolve = clasp->resolve;
 
     /*
      * Avoid recursion on (obj, id) already being resolved on cx.
      *
@@ -5523,18 +5560,20 @@ CallResolveOp(JSContext *cx, JSObject *s
     *recursedp = false;
 
     *propp = NULL;
 
     if (clasp->flags & JSCLASS_NEW_RESOLVE) {
         JSNewResolveOp newresolve = reinterpret_cast<JSNewResolveOp>(resolve);
         if (flags == RESOLVE_INFER)
             flags = js_InferFlags(cx, 0);
-        JSObject *obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START) ? start : NULL;
-        if (!newresolve(cx, obj, id, flags, &obj2))
+
+        RootedVarObject obj2(cx);
+        obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START) ? start : NULL;
+        if (!newresolve(cx, obj, id, flags, obj2.address()))
             return false;
 
         /*
          * We trust the new style resolve hook to set obj2 to NULL when
          * the id cannot be resolved. But, when obj2 is not null, we do
          * not assume that id must exist and do full nativeLookup for
          * compatibility.
          */
@@ -5564,30 +5603,33 @@ CallResolveOp(JSContext *cx, JSObject *s
 
 static JS_ALWAYS_INLINE bool
 LookupPropertyWithFlagsInline(JSContext *cx, JSObject *obj, jsid id, uintN flags,
                               JSObject **objp, JSProperty **propp)
 {
     /* We should not get string indices which aren't already integers here. */
     JS_ASSERT(id == js_CheckForStringIndex(id));
 
+    RootObject objRoot(cx, &obj);
+    RootId idRoot(cx, &id);
+
     /* Search scopes starting with obj and following the prototype link. */
     JSObject *start = obj;
     while (true) {
         const Shape *shape = obj->nativeLookup(cx, id);
         if (shape) {
             *objp = obj;
             *propp = (JSProperty *) shape;
             return true;
         }
 
         /* Try obj's class resolve hook if id was not found in obj's scope. */
         if (obj->getClass()->resolve != JS_ResolveStub) {
             bool recursed;
-            if (!CallResolveOp(cx, start, obj, id, flags, objp, propp, &recursed))
+            if (!CallResolveOp(cx, start, objRoot, idRoot, flags, objp, propp, &recursed))
                 return false;
             if (recursed)
                 break;
             if (*propp) {
                 /*
                  * For stats we do not recalculate protoIndex even if it was
                  * resolved on some other object.
                  */
@@ -6114,25 +6156,25 @@ js::CheckUndeclaredVarAssignment(JSConte
            JS_ReportErrorFlagsAndNumber(cx,
                                         (JSREPORT_WARNING | JSREPORT_STRICT
                                          | JSREPORT_STRICT_MODE_ERROR),
                                         js_GetErrorMessage, NULL,
                                         JSMSG_UNDECLARED_VAR, bytes.ptr());
 }
 
 bool
-JSObject::reportReadOnly(JSContext* cx, jsid id, uintN report)
+JSObject::reportReadOnly(JSContext *cx, jsid id, uintN report)
 {
     return js_ReportValueErrorFlags(cx, report, JSMSG_READ_ONLY,
                                     JSDVG_IGNORE_STACK, IdToValue(id), NULL,
                                     NULL, NULL);
 }
 
 bool
-JSObject::reportNotConfigurable(JSContext* cx, jsid id, uintN report)
+JSObject::reportNotConfigurable(JSContext *cx, jsid id, uintN report)
 {
     return js_ReportValueErrorFlags(cx, report, JSMSG_CANT_DELETE,
                                     JSDVG_IGNORE_STACK, IdToValue(id), NULL,
                                     NULL, NULL);
 }
 
 bool
 JSObject::reportNotExtensible(JSContext *cx, uintN report)
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -493,25 +493,25 @@ struct JSObject : js::gc::Cell
     bool setLastProperty(JSContext *cx, const js::Shape *shape);
 
     /* As above, but does not change the slot span. */
     inline void setLastPropertyInfallible(const js::Shape *shape);
 
     /* Make a non-array object with the specified initial state. */
     static inline JSObject *create(JSContext *cx,
                                    js::gc::AllocKind kind,
-                                   js::Shape *shape,
-                                   js::types::TypeObject *type,
+                                   js::HandleShape shape,
+                                   js::HandleTypeObject type,
                                    js::HeapValue *slots);
 
     /* Make a dense array object with the specified initial state. */
     static inline JSObject *createDenseArray(JSContext *cx,
                                              js::gc::AllocKind kind,
-                                             js::Shape *shape,
-                                             js::types::TypeObject *type,
+                                             js::HandleShape shape,
+                                             js::HandleTypeObject type,
                                              uint32_t length);
 
     /*
      * Remove the last property of an object, provided that it is safe to do so
      * (the shape and previous shape do not carry conflicting information about
      * the object itself).
      */
     inline void removeLastProperty(JSContext *cx);
@@ -521,17 +521,16 @@ struct JSObject : js::gc::Cell
      * 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 inline size_t offsetOfShape() { return offsetof(JSObject, shape_); }
     inline js::HeapPtrShape *addressOfShape() { return &shape_; }
 
-    inline js::Shape **nativeSearch(JSContext *cx, jsid id, bool adding = false);
     const js::Shape *nativeLookup(JSContext *cx, jsid id);
 
     inline bool nativeContains(JSContext *cx, jsid id);
     inline bool nativeContains(JSContext *cx, const js::Shape &shape);
 
     /* Upper bound on the number of elements in an object. */
     static const uint32_t NELEMENTS_LIMIT = JS_BIT(28);
 
@@ -1214,30 +1213,30 @@ struct JSObject : js::gc::Cell
      * Get the property with the given id, then call it as a function with the
      * given arguments, providing this object as |this|. If the property isn't
      * callable a TypeError will be thrown. On success the value returned by
      * the call is stored in *vp.
      */
     bool callMethod(JSContext *cx, jsid id, uintN argc, js::Value *argv, js::Value *vp);
 
   private:
-    js::Shape *getChildProperty(JSContext *cx, js::Shape *parent, js::Shape &child);
+    js::Shape *getChildProperty(JSContext *cx, js::Shape *parent, js::StackShape &child);
 
     /*
      * Internal helper that adds a shape not yet mapped by this object.
      *
      * Notes:
      * 1. getter and setter must be normalized based on flags (see jsscope.cpp).
      * 2. !isExtensible() checking must be done by callers.
      */
     js::Shape *addPropertyInternal(JSContext *cx, jsid id,
                                    JSPropertyOp getter, JSStrictPropertyOp setter,
                                    uint32_t slot, uintN attrs,
-                                   uintN flags, intN shortid,
-                                   js::Shape **spp, bool allowDictionary);
+                                   uintN flags, intN shortid, js::Shape **spp,
+                                   bool allowDictionary);
 
     bool toDictionaryMode(JSContext *cx);
 
     struct TradeGutsReserved;
     static bool ReserveForTradeGuts(JSContext *cx, JSObject *a, JSObject *b,
                                     TradeGutsReserved &reserved);
 
     static void TradeGuts(JSContext *cx, JSObject *a, JSObject *b,
@@ -1388,16 +1387,18 @@ struct JSObject : js::gc::Cell
 
     inline void initArrayClass();
 
     static inline void writeBarrierPre(JSObject *obj);
     static inline void writeBarrierPost(JSObject *obj, void *addr);
     inline void privateWriteBarrierPre(void **oldval);
     inline void privateWriteBarrierPost(void **oldval);
 
+    static inline js::ThingRootKind rootKind() { return js::THING_ROOT_OBJECT; }
+
   private:
     static void staticAsserts() {
         /* Check alignment for any fixed slots allocated after the object. */
         JS_STATIC_ASSERT(sizeof(JSObject) % sizeof(js::Value) == 0);
 
         JS_STATIC_ASSERT(offsetof(JSObject, shape_) == offsetof(js::shadow::Object, shape));
         JS_STATIC_ASSERT(offsetof(JSObject, slots) == offsetof(js::shadow::Object, slots));
         JS_STATIC_ASSERT(offsetof(JSObject, type_) == offsetof(js::shadow::Object, type));
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -1027,17 +1027,17 @@ JSObject::initializeSlotRange(size_t sta
         }
     } else {
         js::InitValueRange(slots + start - fixed, length, false);
     }
 }
 
 /* static */ inline JSObject *
 JSObject::create(JSContext *cx, js::gc::AllocKind kind,
-                 js::Shape *shape, js::types::TypeObject *type, js::HeapValue *slots)
+                 js::HandleShape shape, js::HandleTypeObject type, js::HeapValue *slots)
 {
     /*
      * Callers must use dynamicSlotsCount to size the initial slot array of the
      * object. We can't check the allocated capacity of the dynamic slots, but
      * make sure their presence is consistent with the shape.
      */
     JS_ASSERT(shape && type);
     JS_ASSERT(!!dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan()) == !!slots);
@@ -1058,17 +1058,18 @@ JSObject::create(JSContext *cx, js::gc::
     if (size_t span = shape->slotSpan())
         obj->initializeSlotRange(0, span);
 
     return obj;
 }
 
 /* static */ inline JSObject *
 JSObject::createDenseArray(JSContext *cx, js::gc::AllocKind kind,
-                           js::Shape *shape, js::types::TypeObject *type, uint32_t length)
+                           js::HandleShape shape, js::HandleTypeObject type,
+                           uint32_t length)
 {
     JS_ASSERT(shape && type);
     JS_ASSERT(shape->getObjectClass() == &js::ArrayClass);
 
     /*
      * Dense arrays are non-native, and never have properties to store.
      * The number of fixed slots in the shape of such objects is zero.
      */
@@ -1187,27 +1188,22 @@ JSObject::nativeSetSlotWithType(JSContex
 }
 
 inline bool
 JSObject::isNative() const
 {
     return lastProperty()->isNative();
 }
 
-inline js::Shape **
-JSObject::nativeSearch(JSContext *cx, jsid id, bool adding)
-{
-    return js::Shape::search(cx, &shape_, id, adding);
-}
-
 inline const js::Shape *
 JSObject::nativeLookup(JSContext *cx, jsid id)
 {
     JS_ASSERT(isNative());
-    return SHAPE_FETCH(nativeSearch(cx, id));
+    js::Shape **spp;
+    return js::Shape::search(cx, lastProperty(), id, &spp);
 }
 
 inline bool
 JSObject::nativeContains(JSContext *cx, jsid id)
 {
     return nativeLookup(cx, id) != NULL;
 }
 
@@ -1650,22 +1646,27 @@ NewObjectCache::newObjectFromHit(JSConte
     if (obj) {
         memcpy(obj, &entry->templateObject, entry->nbytes);
         Probes::createObject(cx, obj);
         return obj;
     }
 
     /* Copy the entry to the stack first in case it is purged by a GC. */
     size_t nbytes = entry->nbytes;
-    JSObject_Slots16 stackObject;
+    char stackObject[sizeof(JSObject_Slots16)];
+    JS_ASSERT(nbytes <= sizeof(stackObject));
     memcpy(&stackObject, &entry->templateObject, nbytes);
 
+    JSObject *baseobj = (JSObject *) stackObject;
+    RootShape shapeRoot(cx, (Shape **) baseobj->addressOfShape());
+    RootTypeObject typeRoot(cx, (types::TypeObject **) baseobj->addressOfType());
+
     obj = js_NewGCObject(cx, entry->kind);
     if (obj) {
-        memcpy(obj, &stackObject, nbytes);
+        memcpy(obj, baseobj, nbytes);
         Probes::createObject(cx, obj);
         return obj;
     }
 
     return NULL;
 }
 
 static inline bool
@@ -1708,17 +1709,17 @@ GetClassProtoKey(js::Class *clasp)
     if (key != JSProto_Null)
         return key;
     if (clasp->flags & JSCLASS_IS_ANONYMOUS)
         return JSProto_Object;
     return JSProto_Null;
 }
 
 inline bool
-FindProto(JSContext *cx, js::Class *clasp, JSObject *parent, JSObject ** proto)
+FindProto(JSContext *cx, js::Class *clasp, HandleObject parent, JSObject **proto)
 {
     JSProtoKey protoKey = GetClassProtoKey(clasp);
     if (!js_GetClassPrototype(cx, parent, protoKey, proto, clasp))
         return false;
     if (!(*proto) && !js_GetClassPrototype(cx, parent, JSProto_Object, proto))
         return false;
     return true;
 }
@@ -1877,25 +1878,26 @@ DefineConstructorAndPrototype(JSContext 
     JS_ASSERT(proto);
 
     jsid id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]);
     JS_ASSERT(!global->nativeLookup(cx, id));
 
     /* Set these first in case AddTypePropertyId looks for this class. */
     global->setSlot(key, ObjectValue(*ctor));
     global->setSlot(key + JSProto_LIMIT, ObjectValue(*proto));
+    global->setSlot(key + JSProto_LIMIT * 2, ObjectValue(*ctor));
 
     types::AddTypePropertyId(cx, global, id, ObjectValue(*ctor));
     if (!global->addDataProperty(cx, id, key + JSProto_LIMIT * 2, 0)) {
         global->setSlot(key, UndefinedValue());
         global->setSlot(key + JSProto_LIMIT, UndefinedValue());
+        global->setSlot(key + JSProto_LIMIT * 2, UndefinedValue());
         return false;
     }
 
-    global->setSlot(key + JSProto_LIMIT * 2, ObjectValue(*ctor));
     return true;
 }
 
 bool
 PropDesc::checkGetter(JSContext *cx)
 {
     if (hasGet && !js_IsCallable(get) && !get.isUndefined()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_GET_SET_FIELD,
@@ -2016,28 +2018,28 @@ ValueIsSpecial(JSObject *obj, Value *pro
         return false;
     }
 #endif
 
     return false;
 }
 
 JSObject *
-DefineConstructorAndPrototype(JSContext *cx, JSObject *obj, JSProtoKey key, JSAtom *atom,
+DefineConstructorAndPrototype(JSContext *cx, HandleObject obj, JSProtoKey key, HandleAtom atom,
                               JSObject *protoProto, Class *clasp,
                               Native constructor, uintN nargs,
                               JSPropertySpec *ps, JSFunctionSpec *fs,
                               JSPropertySpec *static_ps, JSFunctionSpec *static_fs,
                               JSObject **ctorp = NULL,
                               gc::AllocKind ctorKind = JSFunction::FinalizeKind);
 
 } /* namespace js */
 
 extern JSObject *
-js_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto,
+js_InitClass(JSContext *cx, js::HandleObject obj, JSObject *parent_proto,
              js::Class *clasp, JSNative constructor, uintN nargs,
              JSPropertySpec *ps, JSFunctionSpec *fs,
              JSPropertySpec *static_ps, JSFunctionSpec *static_fs,
              JSObject **ctorp = NULL,
              js::gc::AllocKind ctorKind = JSFunction::FinalizeKind);
 
 inline JSObject *
 js_GetProtoIfDenseArray(JSObject *obj)
--- a/js/src/jspropertytree.cpp
+++ b/js/src/jspropertytree.cpp
@@ -48,23 +48,23 @@
 #include "jsscope.h"
 
 #include "jsobjinlines.h"
 #include "jsscopeinlines.h"
 
 using namespace js;
 
 inline HashNumber
-ShapeHasher::hash(const Lookup l)
+ShapeHasher::hash(const Lookup &l)
 {
-    return l->hash();
+    return l.hash();
 }
 
 inline bool
-ShapeHasher::match(const Key k, const Lookup l)
+ShapeHasher::match(const Key k, const Lookup &l)
 {
     return k->matches(l);
 }
 
 Shape *
 PropertyTree::newShape(JSContext *cx)
 {
     Shape *shape = js_NewGCShape(cx);
@@ -79,18 +79,18 @@ static KidsHash *
 HashChildren(Shape *kid1, Shape *kid2)
 {
     KidsHash *hash = OffTheBooks::new_<KidsHash>();
     if (!hash || !hash->init(2)) {
         Foreground::delete_(hash);
         return NULL;
     }
 
-    JS_ALWAYS_TRUE(hash->putNew(kid1));
-    JS_ALWAYS_TRUE(hash->putNew(kid2));
+    JS_ALWAYS_TRUE(hash->putNew(kid1, kid1));
+    JS_ALWAYS_TRUE(hash->putNew(kid2, kid2));
     return hash;
 }
 
 bool
 PropertyTree::insertChild(JSContext *cx, Shape *parent, Shape *child)
 {
     JS_ASSERT(!parent->inDictionary());
     JS_ASSERT(!child->parent);
@@ -116,17 +116,17 @@ PropertyTree::insertChild(JSContext *cx,
             JS_ReportOutOfMemory(cx);
             return false;
         }
         kidp->setHash(hash);
         child->setParent(parent);
         return true;
     }
 
-    if (!kidp->toHash()->putNew(child)) {
+    if (!kidp->toHash()->putNew(child, child)) {
         JS_ReportOutOfMemory(cx);
         return false;
     }
 
     child->setParent(parent);
     return true;
 }
 
@@ -140,17 +140,19 @@ Shape::removeChild(Shape *child)
     if (kidp->isShape()) {
         JS_ASSERT(kidp->toShape() == child);
         kidp->setNull();
         return;
     }
 
     KidsHash *hash = kidp->toHash();
     JS_ASSERT(hash->count() >= 2);      /* otherwise kidp->isShape() should be true */
+
     hash->remove(child);
+
     if (hash->count() == 1) {
         /* Convert from HASH form back to SHAPE form. */
         KidsHash::Range r = hash->all(); 
         Shape *otherChild = r.front();
         JS_ASSERT((r.popFront(), r.empty()));    /* No more elements! */
         kidp->setShape(otherChild);
         js::UnwantedForeground::delete_(hash);
     }
@@ -166,51 +168,51 @@ ReadBarrier(Shape *shape)
     JSCompartment *comp = shape->compartment();
     if (comp->needsBarrier())
         MarkShapeUnbarriered(comp->barrierTracer(), shape, "read barrier");
 #endif
     return shape;
 }
 
 Shape *
-PropertyTree::getChild(JSContext *cx, Shape *parent, const Shape &child)
+PropertyTree::getChild(JSContext *cx, Shape *parent, uint32_t nfixed, const StackShape &child)
 {
     Shape *shape;
 
     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
      * getters or setters on the new object that is passed in as
      * |this| can significantly increase fan-out below the property
      * tree root -- see bug 335700 for details.
      */
     KidsPointer *kidp = &parent->kids;
     if (kidp->isShape()) {
         shape = kidp->toShape();
-        if (shape->matches(&child))
+        if (shape->matches(child))
             return ReadBarrier(shape);
     } else if (kidp->isHash()) {
-        shape = *kidp->toHash()->lookup(&child);
+        shape = *kidp->toHash()->lookup(child);
         if (shape)
             return ReadBarrier(shape);
     } else {
         /* If kidp->isNull(), we always insert. */
     }
 
+    RootStackShape childRoot(cx, &child);
+    RootShape parentRoot(cx, &parent);
+
     shape = newShape(cx);
     if (!shape)
         return NULL;
 
-    UnownedBaseShape *base = child.base()->unowned();
-
-    new (shape) Shape(&child);
-    shape->base_.init(base);
+    new (shape) Shape(child, nfixed);
 
     if (!insertChild(cx, parent, shape))
         return NULL;
 
     return shape;
 }
 
 void
--- a/js/src/jspropertytree.h
+++ b/js/src/jspropertytree.h
@@ -43,20 +43,20 @@
 #include "jsprvtd.h"
 
 #include "js/HashTable.h"
 
 namespace js {
 
 struct ShapeHasher {
     typedef js::Shape *Key;
-    typedef const js::Shape *Lookup;
+    typedef js::StackShape Lookup;
 
-    static inline HashNumber hash(const Lookup l);
-    static inline bool match(Key k, Lookup l);
+    static inline HashNumber hash(const Lookup &l);
+    static inline bool match(Key k, const Lookup &l);
 };
 
 typedef HashSet<js::Shape *, ShapeHasher, SystemAllocPolicy> KidsHash;
 
 class KidsPointer {
   private:
     enum {
         SHAPE = 0,
@@ -111,17 +111,17 @@ class PropertyTree
     enum { MAX_HEIGHT = 128 };
 
     PropertyTree(JSCompartment *comp)
         : compartment(comp)
     {
     }
     
     js::Shape *newShape(JSContext *cx);
-    js::Shape *getChild(JSContext *cx, js::Shape *parent, const js::Shape &child);
+    js::Shape *getChild(JSContext *cx, Shape *parent, uint32_t nfixed, const StackShape &child);
 
 #ifdef DEBUG
     static void dumpShapes(JSContext *cx);
     static void meter(JSBasicStats *bs, js::Shape *node);
 #endif
 };
 
 } /* namespace js */
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -1723,17 +1723,16 @@ js::FixProxy(JSContext *cx, JSObject *pr
     /*
      * Make a blank object from the recipe fix provided to us.  This must have
      * number of fixed slots as the proxy so that we can swap their contents.
      */
     gc::AllocKind kind = proxy->getAllocKind();
     JSObject *newborn = NewObjectWithGivenProto(cx, clasp, proto, parent, kind);
     if (!newborn)
         return false;
-    AutoObjectRooter tvr2(cx, newborn);
 
     if (clasp == &CallableObjectClass) {
         newborn->setSlot(JSSLOT_CALLABLE_CALL, GetCall(proxy));
         newborn->setSlot(JSSLOT_CALLABLE_CONSTRUCT, GetConstruct(proxy));
     }
 
     {
         AutoPendingProxyOperation pending(cx, proxy);
--- a/js/src/jsprvtd.h
+++ b/js/src/jsprvtd.h
@@ -146,17 +146,16 @@ enum RegExpFlag
 };
 
 enum RegExpExecType
 {
     RegExpExec,
     RegExpTest
 };
 
-class AutoStringRooter;
 class ExecuteArgsGuard;
 class InvokeFrameGuard;
 class InvokeArgsGuard;
 class StringBuffer;
 
 class FrameRegs;
 class StackFrame;
 class StackSegment;
@@ -220,16 +219,19 @@ struct PropertyCacheEntry;
 
 class BaseShape;
 class UnownedBaseShape;
 struct Shape;
 struct EmptyShape;
 class ShapeKindArray;
 class Bindings;
 
+struct StackBaseShape;
+struct StackShape;
+
 class MultiDeclRange;
 class ParseMapPool;
 class DefnOrHeader;
 typedef InlineMap<JSAtom *, Definition *, 24> AtomDefnMap;
 typedef InlineMap<JSAtom *, jsatomid, 24> AtomIndexMap;
 typedef InlineMap<JSAtom *, DefnOrHeader, 24> AtomDOHMap;
 typedef Vector<UpvarCookie, 8> UpvarCookies;
 
@@ -272,16 +274,79 @@ namespace types {
 
 class TypeSet;
 struct TypeCallsite;
 struct TypeObject;
 struct TypeCompartment;
 
 } /* namespace types */
 
+enum ThingRootKind
+{
+    THING_ROOT_OBJECT,
+    THING_ROOT_SHAPE,
+    THING_ROOT_BASE_SHAPE,
+    THING_ROOT_TYPE_OBJECT,
+    THING_ROOT_STRING,
+    THING_ROOT_SCRIPT,
+    THING_ROOT_ID,
+    THING_ROOT_VALUE,
+    THING_ROOT_LIMIT
+};
+
+template <typename T> class Root;
+template <typename T> class RootedVar;
+
+/*
+ * Reference to a stack location rooted for GC. See "Moving GC Stack Rooting"
+ * comment in jscntxt.h.
+ */
+template <typename T>
+class Handle
+{
+  public:
+    // Copy handles of different types, with implicit coercion.
+    template <typename S> Handle(Handle<S> handle) {
+        testAssign<S>();
+        ptr = reinterpret_cast<const T *>(handle.address());
+    }
+
+    // Get a handle from a rooted stack location, with implicit coercion.
+    template <typename S> inline Handle(const Root<S> &root);
+    template <typename S> inline Handle(const RootedVar<S> &root);
+
+    const T *address() { return ptr; }
+
+    operator T () { return value(); }
+    T operator ->() { return value(); }
+
+  private:
+    const T *ptr;
+    T value() { return *ptr; }
+
+    template <typename S>
+    void testAssign() {
+#ifdef DEBUG
+        T a;
+        S b;
+        a = b;
+#endif
+    }
+};
+
+typedef Handle<JSObject*>          HandleObject;
+typedef Handle<JSFunction*>        HandleFunction;
+typedef Handle<Shape*>             HandleShape;
+typedef Handle<BaseShape*>         HandleBaseShape;
+typedef Handle<types::TypeObject*> HandleTypeObject;
+typedef Handle<JSString*>          HandleString;
+typedef Handle<JSAtom*>            HandleAtom;
+typedef Handle<jsid>               HandleId;
+typedef Handle<Value>              HandleValue;
+
 } /* namespace js */
 
 namespace JSC {
 
 class ExecutableAllocator;
 
 } /* namespace JSC */
 
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -3256,17 +3256,20 @@ static JSFunctionSpec static_methods[] =
 };
 
 
 JS_BEGIN_EXTERN_C
 
 JS_PUBLIC_API(JSObject *)
 JS_InitReflect(JSContext *cx, JSObject *obj)
 {
-    JSObject *Reflect = NewObjectWithClassProto(cx, &ObjectClass, NULL, obj);
+    RootObject root(cx, &obj);
+    RootedVarObject Reflect(cx);
+
+    Reflect = NewObjectWithClassProto(cx, &ObjectClass, NULL, obj);
     if (!Reflect || !Reflect->setSingletonType(cx))
         return NULL;
 
     if (!JS_DefineProperty(cx, obj, "Reflect", OBJECT_TO_JSVAL(Reflect),
                            JS_PropertyStub, JS_StrictPropertyStub, 0)) {
         return NULL;
     }
 
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -105,24 +105,26 @@ PropertyTable::init(JSRuntime *rt, Shape
     return true;
 }
 
 bool
 Shape::makeOwnBaseShape(JSContext *cx)
 {
     JS_ASSERT(!base()->isOwned());
 
+    RootedVarShape self(cx, this);
+
     BaseShape *nbase = js_NewGCBaseShape(cx);
     if (!nbase)
         return false;
 
-    new (nbase) BaseShape(*base());
-    nbase->setOwned(base()->toUnowned());
+    new (nbase) BaseShape(*self->base());
+    nbase->setOwned(self->base()->toUnowned());
 
-    this->base_ = nbase;
+    self->base_ = nbase;
 
     return true;
 }
 
 void
 Shape::handoffTableTo(Shape *shape)
 {
     JS_ASSERT(inDictionary() && shape->inDictionary());
@@ -142,30 +144,32 @@ Shape::handoffTableTo(Shape *shape)
     shape->base_ = nbase;
 }
 
 bool
 Shape::hashify(JSContext *cx)
 {
     JS_ASSERT(!hasTable());
 
+    RootedVarShape self(cx, this);
+
     if (!ensureOwnBaseShape(cx))
         return false;
 
     JSRuntime *rt = cx->runtime;
-    PropertyTable *table = rt->new_<PropertyTable>(entryCount());
+    PropertyTable *table = rt->new_<PropertyTable>(self->entryCount());
     if (!table)
         return false;
 
-    if (!table->init(rt, this)) {
+    if (!table->init(rt, self)) {
         rt->free_(table);
         return false;
     }
 
-    base()->setTable(table);
+    self->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))
@@ -298,26 +302,23 @@ PropertyTable::grow(JSContext *cx)
     if (!change(delta, cx) && entryCount + removedCount == size - 1) {
         JS_ReportOutOfMemory(cx);
         return false;
     }
     return true;
 }
 
 Shape *
-Shape::getChildBinding(JSContext *cx, const js::Shape &child, HeapPtrShape *lastBinding)
+Shape::getChildBinding(JSContext *cx, const StackShape &child)
 {
     JS_ASSERT(!inDictionary());
-    JS_ASSERT(!child.inDictionary());
 
-    Shape *shape = JS_PROPERTY_TREE(cx).getChild(cx, this, child);
+    Shape *shape = JS_PROPERTY_TREE(cx).getChild(cx, this, numFixedSlots(), child);
     if (shape) {
         JS_ASSERT(shape->parent == this);
-        JS_ASSERT(this == *lastBinding);
-        *lastBinding = shape;
 
         /*
          * Update the number of fixed slots which bindings of this shape will
          * have. Bindings are constructed as new properties come in, so the
          * call object allocation class is not known ahead of time. Compute
          * the fixed slot count here, which will feed into call objects created
          * off of the bindings.
          */
@@ -336,58 +337,48 @@ Shape::getChildBinding(JSContext *cx, co
             JS_ASSERT(gc::GetGCKindSlots(gc::GetGCObjectKind(nfixed)) == CallObject::RESERVED_SLOTS + 1);
         }
 
         shape->setNumFixedSlots(nfixed - 1);
     }
     return shape;
 }
 
-/* static */ bool
-Shape::replaceLastProperty(JSContext *cx, const BaseShape &base, JSObject *proto, HeapPtrShape *lastp)
+/* static */ Shape *
+Shape::replaceLastProperty(JSContext *cx, const StackBaseShape &base, JSObject *proto, Shape *shape)
 {
-    Shape *shape = *lastp;
     JS_ASSERT(!shape->inDictionary());
 
     if (!shape->parent) {
         /* Treat as resetting the initial property of the shape hierarchy. */
         AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
-        Shape *newShape =
-            EmptyShape::getInitialShape(cx, base.clasp, proto,
-                                        base.parent, kind,
-                                        base.flags & BaseShape::OBJECT_FLAG_MASK);
-        if (!newShape)
-            return false;
-        JS_ASSERT(newShape->numFixedSlots() == shape->numFixedSlots());
-        *lastp = newShape;
-        return true;
+        return EmptyShape::getInitialShape(cx, base.clasp, proto,
+                                           base.parent, kind,
+                                           base.flags & BaseShape::OBJECT_FLAG_MASK);
     }
 
-    BaseShape *nbase = BaseShape::getUnowned(cx, base);
+    RootShape root(cx, &shape);
+
+    UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
     if (!nbase)
         return false;
 
-    Shape child(shape);
-    child.base_ = nbase;
+    StackShape child(shape);
+    child.base = nbase;
 
-    Shape *newShape = JS_PROPERTY_TREE(cx).getChild(cx, shape->parent, child);
-    if (!newShape)
-        return false;
-
-    *lastp = newShape;
-    return true;
+    return JS_PROPERTY_TREE(cx).getChild(cx, shape->parent, shape->numFixedSlots(), child);
 }
 
 /*
  * Get or create a property-tree or dictionary child property of parent, which
  * must be lastProp if inDictionaryMode(), else parent must be one of lastProp
  * or lastProp->parent.
  */
 Shape *
-JSObject::getChildProperty(JSContext *cx, Shape *parent, Shape &child)
+JSObject::getChildProperty(JSContext *cx, Shape *parent, StackShape &child)
 {
     /*
      * Shared properties have no slot, but slot_ will reflect that of parent.
      * Unshared properties allocate a slot here but may lose it due to a
      * JS_ClearScope call.
      */
     if (!child.hasSlot()) {
         child.setSlot(parent->maybeSlot());
@@ -402,107 +393,94 @@ JSObject::getChildProperty(JSContext *cx
             JS_ASSERT(inDictionaryMode() ||
                       parent->hasMissingSlot() ||
                       child.slot() == parent->maybeSlot() + 1);
         }
     }
 
     Shape *shape;
 
+    RootedVarObject self(cx, this);
+
     if (inDictionaryMode()) {
         JS_ASSERT(parent == lastProperty());
+        RootStackShape childRoot(cx, &child);
         shape = js_NewGCShape(cx);
         if (!shape)
             return NULL;
-        if (child.hasSlot() && child.slot() >= lastProperty()->base()->slotSpan()) {
-            if (!setSlotSpan(cx, child.slot() + 1))
+        if (child.hasSlot() && child.slot() >= self->lastProperty()->base()->slotSpan()) {
+            if (!self->setSlotSpan(cx, child.slot() + 1))
                 return NULL;
         }
-        shape->initDictionaryShape(child, &shape_);
+        shape->initDictionaryShape(child, self->numFixedSlots(), &self->shape_);
     } else {
-        shape = JS_PROPERTY_TREE(cx).getChild(cx, parent, child);
+        shape = JS_PROPERTY_TREE(cx).getChild(cx, parent, self->numFixedSlots(), child);
         if (!shape)
             return NULL;
-        JS_ASSERT(shape->parent == parent);
-        JS_ASSERT_IF(parent != lastProperty(), parent == lastProperty()->parent);
-        if (!setLastProperty(cx, shape))
+        //JS_ASSERT(shape->parent == parent);
+        //JS_ASSERT_IF(parent != lastProperty(), parent == lastProperty()->parent);
+        if (!self->setLastProperty(cx, shape))
             return NULL;
     }
 
     return shape;
 }
 
-Shape *
-Shape::newDictionaryList(JSContext *cx, HeapPtrShape *listp)
-{
-    Shape *shape = *listp;
-    Shape *list = shape;
-
-    /*
-     * We temporarily create the dictionary shapes using a root located on the
-     * stack. This way, the GC doesn't see any intermediate state until we
-     * switch listp at the end.
-     */
-    HeapPtrShape root(NULL);
-    HeapPtrShape *childp = &root;
-
-    while (shape) {
-        JS_ASSERT(!shape->inDictionary());
-
-        Shape *dprop = js_NewGCShape(cx);
-        if (!dprop) {
-            *listp = list;
-            return NULL;
-        }
-        dprop->initDictionaryShape(*shape, childp);
-
-        JS_ASSERT(!dprop->hasTable());
-        childp = &dprop->parent;
-        shape = shape->parent;
-    }
-
-    *listp = root;
-    root->listp = listp;
-
-    JS_ASSERT(root->inDictionary());
-    if (!root->hashify(cx)) {
-        *listp = list;
-        return NULL;
-    }
-
-    return root;
-}
-
 bool
 JSObject::toDictionaryMode(JSContext *cx)
 {
     JS_ASSERT(!inDictionaryMode());
 
     /* We allocate the shapes from cx->compartment, so make sure it's right. */
     JS_ASSERT(compartment() == cx->compartment);
 
     uint32_t span = slotSpan();
 
+    RootedVarObject self(cx, this);
+
     /*
      * Clone the shapes into a new dictionary list. Don't update the
      * last property of this object until done, otherwise a GC
      * triggered while creating the dictionary will get the wrong
      * slot span for this object.
      */
-    HeapPtrShape last;
-    last.init(lastProperty());
-    if (!Shape::newDictionaryList(cx, &last))
+    RootedVarShape root(cx);
+    RootedVarShape dictionaryShape(cx);
+
+    RootedVarShape shape(cx);
+    shape = lastProperty();
+
+    while (shape) {
+        JS_ASSERT(!shape->inDictionary());
+
+        Shape *dprop = js_NewGCShape(cx);
+        if (!dprop)
+            return false;
+
+        HeapPtrShape *listp = dictionaryShape
+                              ? &dictionaryShape->parent
+                              : (HeapPtrShape *) root.address();
+
+        StackShape child(shape);
+        dprop->initDictionaryShape(child, self->numFixedSlots(), listp);
+
+        JS_ASSERT(!dprop->hasTable());
+        dictionaryShape = dprop;
+        shape = shape->previous();
+    }
+
+    if (!root->hashify(cx))
         return false;
 
-    JS_ASSERT(last->listp == &last);
-    last->listp = &shape_;
-    shape_ = last;
+    JS_ASSERT((Shape **) root->listp == root.address());
+    root->listp = &self->shape_;
+    self->shape_ = root;
 
-    JS_ASSERT(lastProperty()->hasTable());
-    lastProperty()->base()->setSlotSpan(span);
+    JS_ASSERT(self->inDictionaryMode());
+    root->base()->setSlotSpan(span);
 
     return true;
 }
 
 /*
  * Normalize stub getter and setter values for faster is-stub testing in the
  * SHAPE_CALL_[GS]ETTER macros.
  */
@@ -611,95 +589,104 @@ JSObject::addProperty(JSContext *cx, jsi
 
     if (!isExtensible()) {
         reportNotExtensible(cx);
         return NULL;
     }
 
     NormalizeGetterAndSetter(cx, this, id, attrs, flags, getter, setter);
 
-    /* Search for id with adding = true in order to claim its entry. */
-    Shape **spp = nativeSearch(cx, id, true);
-    JS_ASSERT(!SHAPE_FETCH(spp));
-    return addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid, spp, allowDictionary);
+    Shape **spp = NULL;
+    if (inDictionaryMode())
+        spp = lastProperty()->table().search(id, true);
+
+    return addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid,
+                               spp, allowDictionary);
 }
 
 Shape *
 JSObject::addPropertyInternal(JSContext *cx, jsid id,
                               PropertyOp getter, StrictPropertyOp setter,
                               uint32_t slot, uintN attrs,
-                              uintN flags, intN shortid,
-                              Shape **spp, bool allowDictionary)
+                              uintN flags, intN shortid, Shape **spp,
+                              bool allowDictionary)
 {
     JS_ASSERT_IF(!allowDictionary, !inDictionaryMode());
 
+    RootId idRoot(cx, &id);
+    RootedVarObject self(cx, this);
+
     PropertyTable *table = NULL;
     if (!inDictionaryMode()) {
         bool stableSlot =
             (slot == SHAPE_INVALID_SLOT) ||
             lastProperty()->hasMissingSlot() ||
             (slot == lastProperty()->maybeSlot() + 1);
         JS_ASSERT_IF(!allowDictionary, stableSlot);
         if (allowDictionary &&
             (!stableSlot || lastProperty()->entryCount() >= PropertyTree::MAX_HEIGHT)) {
             if (!toDictionaryMode(cx))
                 return NULL;
-            spp = nativeSearch(cx, id, true);
             table = &lastProperty()->table();
+            spp = table->search(id, true);
         }
-    } else if (lastProperty()->hasTable()) {
+    } else {
         table = &lastProperty()->table();
         if (table->needsToGrow()) {
             if (!table->grow(cx))
                 return NULL;
-
             spp = table->search(id, true);
             JS_ASSERT(!SHAPE_FETCH(spp));
         }
     }
 
+    JS_ASSERT(!!table == !!spp);
+
     /* Find or create a property tree node labeled by our arguments. */
     Shape *shape;
     {
+        shape = self->lastProperty();
+
         jsuint index;
         bool indexed = js_IdIsIndex(id, &index);
         UnownedBaseShape *nbase;
-        if (lastProperty()->base()->matchesGetterSetter(getter, setter) && !indexed) {
-            nbase = lastProperty()->base()->unowned();
+        if (shape->base()->matchesGetterSetter(getter, setter) && !indexed) {
+            nbase = shape->base()->unowned();
         } else {
-            uint32_t flags = lastProperty()->getObjectFlags()
-                             | (indexed ? BaseShape::INDEXED : 0);
-            BaseShape base(getClass(), getParent(), flags, attrs, getter, setter);
+            StackBaseShape base(shape->base());
+            base.updateGetterSetter(attrs, getter, setter);
+            if (indexed)
+                base.flags |= BaseShape::INDEXED;
             nbase = BaseShape::getUnowned(cx, base);
             if (!nbase)
                 return NULL;
         }
 
-        Shape child(nbase, id, slot, numFixedSlots(), attrs, flags, shortid);
-        shape = getChildProperty(cx, lastProperty(), child);
+        StackShape child(nbase, id, slot, self->numFixedSlots(), attrs, flags, shortid);
+        shape = self->getChildProperty(cx, self->lastProperty(), child);
     }
 
     if (shape) {
-        JS_ASSERT(shape == lastProperty());
+        JS_ASSERT(shape == self->lastProperty());
 
         if (table) {
             /* Store the tree node pointer in the table entry for id. */
             SHAPE_STORE_PRESERVING_COLLISION(spp, shape);
             ++table->entryCount;
 
             /* Pass the table along to the new last property, namely shape. */
             JS_ASSERT(&shape->parent->table() == table);
             shape->parent->handoffTableTo(shape);
         }
 
-        CHECK_SHAPE_CONSISTENCY(this);
+        CHECK_SHAPE_CONSISTENCY(self);
         return shape;
     }
 
-    CHECK_SHAPE_CONSISTENCY(this);
+    CHECK_SHAPE_CONSISTENCY(self);
     return NULL;
 }
 
 /*
  * Check and adjust the new attributes for the shape to make sure that our
  * slot access optimizations are sound. It is responsibility of the callers to
  * enforce all restrictions from ECMA-262 v5 8.12.9 [[DefineOwnProperty]].
  */
@@ -725,57 +712,64 @@ CheckCanChangeAttrs(JSContext *cx, JSObj
 Shape *
 JSObject::putProperty(JSContext *cx, jsid id,
                       PropertyOp getter, StrictPropertyOp setter,
                       uint32_t slot, uintN attrs,
                       uintN flags, intN shortid)
 {
     JS_ASSERT(!JSID_IS_VOID(id));
 
+    RootId idRoot(cx, &id);
+
     NormalizeGetterAndSetter(cx, this, id, attrs, flags, getter, setter);
 
+    RootedVarObject self(cx, this);
+
     /* Search for id in order to claim its entry if table has been allocated. */
-    Shape **spp = nativeSearch(cx, id, true);
-    Shape *shape = SHAPE_FETCH(spp);
+    Shape **spp;
+    Shape *shape = Shape::search(cx, lastProperty(), id, &spp, true);
     if (!shape) {
         /*
          * You can't add properties to a non-extensible object, but you can change
          * attributes of properties in such objects.
          */
-        if (!isExtensible()) {
-            reportNotExtensible(cx);
+        if (!self->isExtensible()) {
+            self->reportNotExtensible(cx);
             return NULL;
         }
 
-        return addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid, spp, true);
+        return self->addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid, spp, true);
     }
 
     /* Property exists: search must have returned a valid *spp. */
-    JS_ASSERT(!SHAPE_IS_REMOVED(*spp));
+    JS_ASSERT_IF(spp, !SHAPE_IS_REMOVED(*spp));
 
-    if (!CheckCanChangeAttrs(cx, this, shape, &attrs))
+    RootShape shapeRoot(cx, &shape);
+
+    if (!CheckCanChangeAttrs(cx, self, shape, &attrs))
         return NULL;
     
     /*
      * If the caller wants to allocate a slot, but doesn't care which slot,
      * copy the existing shape's slot into slot so we can match shape, if all
      * other members match.
      */
     bool hadSlot = shape->hasSlot();
     uint32_t oldSlot = shape->maybeSlot();
     if (!(attrs & JSPROP_SHARED) && slot == SHAPE_INVALID_SLOT && hadSlot)
         slot = oldSlot;
 
-    UnownedBaseShape *nbase;
+    RootedVar<UnownedBaseShape*> nbase(cx);
     {
         jsuint index;
         bool indexed = js_IdIsIndex(id, &index);
-        uint32_t flags = lastProperty()->getObjectFlags()
-                         | (indexed ? BaseShape::INDEXED : 0);
-        BaseShape base(getClass(), getParent(), flags, attrs, getter, setter);
+        StackBaseShape base(self->lastProperty()->base());
+        base.updateGetterSetter(attrs, getter, setter);
+        if (indexed)
+            base.flags |= BaseShape::INDEXED;
         nbase = BaseShape::getUnowned(cx, base);
         if (!nbase)
             return NULL;
     }
 
     /*
      * Now that we've possibly preserved slot, check whether all members match.
      * If so, this is a redundant "put" and we can return without more work.
@@ -783,49 +777,49 @@ JSObject::putProperty(JSContext *cx, jsi
     if (shape->matchesParamsAfterId(nbase, slot, attrs, flags, shortid))
         return shape;
 
     /*
      * Overwriting a non-last property requires switching to dictionary mode.
      * The shape tree is shared immutable, and we can't removeProperty and then
      * addPropertyInternal because a failure under add would lose data.
      */
-    if (shape != lastProperty() && !inDictionaryMode()) {
-        if (!toDictionaryMode(cx))
+    if (shape != self->lastProperty() && !self->inDictionaryMode()) {
+        if (!self->toDictionaryMode(cx))
             return NULL;
-        spp = nativeSearch(cx, shape->propid());
+        spp = self->lastProperty()->table().search(shape->propid(), false);
         shape = SHAPE_FETCH(spp);
     }
 
     JS_ASSERT_IF(shape->hasSlot() && !(attrs & JSPROP_SHARED), shape->slot() == slot);
 
     /*
      * Optimize the case of a dictionary-mode object based on the property that
      * dictionaries exclusively own their mutable shape structs, each of which
      * has a unique shape (not shared via a shape tree). We can update the
      * shape in place, though after each modification we need to generate a new
      * last property to invalidate shape guards.
      *
      * This is more than an optimization: it is required to preserve for-in
      * enumeration order (see bug 601399).
      */
-    if (inDictionaryMode()) {
-        bool updateLast = (shape == lastProperty());
-        if (!generateOwnShape(cx))
+    if (self->inDictionaryMode()) {
+        bool updateLast = (shape == self->lastProperty());
+        if (!self->generateOwnShape(cx))
             return NULL;
         if (updateLast)
-            shape = lastProperty();
+            shape = self->lastProperty();
 
         /* FIXME bug 593129 -- slot allocation and JSObject *this must move out of here! */
         if (slot == SHAPE_INVALID_SLOT && !(attrs & JSPROP_SHARED)) {
-            if (!allocSlot(cx, &slot))
+            if (!self->allocSlot(cx, &slot))
                 return NULL;
         }
 
-        if (shape == lastProperty())
+        if (shape == self->lastProperty())
             shape->base()->adoptUnowned(nbase);
         else
             shape->base_ = nbase;
 
         shape->setSlot(slot);
         shape->attrs = uint8_t(attrs);
         shape->flags = flags | Shape::IN_DICTIONARY;
         shape->shortid_ = int16_t(shortid);
@@ -834,49 +828,49 @@ JSObject::putProperty(JSContext *cx, jsi
          * Updating the last property in a non-dictionary-mode object. Such
          * objects share their shapes via a tree rooted at a prototype
          * emptyShape, or perhaps a well-known compartment-wide singleton
          * emptyShape.
          *
          * If any shape in the tree has a property hashtable, it is shared and
          * immutable too, therefore we must not update *spp.
          */
-        BaseShape base(getClass(), getParent(), lastProperty()->getObjectFlags(),
-                       attrs, getter, setter);
+        StackBaseShape base(self->lastProperty()->base());
+        base.updateGetterSetter(attrs, getter, setter);
         UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
         if (!nbase)
             return NULL;
 
-        JS_ASSERT(shape == lastProperty());
+        JS_ASSERT(shape == self->lastProperty());
 
         /* Find or create a property tree node labeled by our arguments. */
-        Shape child(nbase, id, slot, numFixedSlots(), attrs, flags, shortid);
-        Shape *newShape = getChildProperty(cx, shape->parent, child);
+        StackShape child(nbase, id, slot, self->numFixedSlots(), attrs, flags, shortid);
+        Shape *newShape = self->getChildProperty(cx, shape->parent, child);
 
         if (!newShape) {
-            CHECK_SHAPE_CONSISTENCY(this);
+            CHECK_SHAPE_CONSISTENCY(self);
             return NULL;
         }
 
         shape = newShape;
     }
 
     /*
      * Can't fail now, so free the previous incarnation's slot if the new shape
      * has no slot. But we do not need to free oldSlot (and must not, as trying
      * to will botch an assertion in JSObject::freeSlot) if the new last
      * property (shape here) has a slotSpan that does not cover it.
      */
     if (hadSlot && !shape->hasSlot()) {
-        if (oldSlot < slotSpan())
-            freeSlot(cx, oldSlot);
+        if (oldSlot < self->slotSpan())
+            self->freeSlot(cx, oldSlot);
         JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
     }
 
-    CHECK_SHAPE_CONSISTENCY(this);
+    CHECK_SHAPE_CONSISTENCY(self);
 
     return shape;
 }
 
 Shape *
 JSObject::changeProperty(JSContext *cx, Shape *shape, uintN attrs, uintN mask,
                          PropertyOp getter, StrictPropertyOp setter)
 {
@@ -917,122 +911,127 @@ JSObject::changeProperty(JSContext *cx, 
 
     CHECK_SHAPE_CONSISTENCY(this);
     return newShape;
 }
 
 bool
 JSObject::removeProperty(JSContext *cx, jsid id)
 {
-    Shape **spp = nativeSearch(cx, id);
-    Shape *shape = SHAPE_FETCH(spp);
+    RootedVarObject self(cx, this);
+
+    RootId idRoot(cx, &id);
+    RootedVarShape shape(cx);
+
+    Shape **spp;
+    shape = Shape::search(cx, lastProperty(), id, &spp);
     if (!shape)
         return true;
 
     /*
      * If shape is not the last property added, or the last property cannot
      * be removed, switch to dictionary mode.
      */
-    if (!inDictionaryMode() && (shape != lastProperty() || !canRemoveLastProperty())) {
-        if (!toDictionaryMode(cx))
+    if (!self->inDictionaryMode() && (shape != self->lastProperty() || !self->canRemoveLastProperty())) {
+        if (!self->toDictionaryMode(cx))
             return false;
-        spp = nativeSearch(cx, shape->propid());
+        spp = self->lastProperty()->table().search(shape->propid(), false);
         shape = SHAPE_FETCH(spp);
     }
 
     /*
      * If in dictionary mode, get a new shape for the last property after the
      * removal. We need a fresh shape for all dictionary deletions, even of
      * the last property. Otherwise, a shape could replay and caches might
      * return deleted DictionaryShapes! See bug 595365. Do this before changing
      * the object or table, so the remaining removal is infallible.
      */
     Shape *spare = NULL;
-    if (inDictionaryMode()) {
+    if (self->inDictionaryMode()) {
         spare = js_NewGCShape(cx);
         if (!spare)
             return false;
         new (spare) Shape(shape->base()->unowned(), 0);
         if (shape == lastProperty()) {
             /*
              * 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.
              */
             Shape *previous = lastProperty()->parent;
-            BaseShape base(getClass(), getParent(), lastProperty()->getObjectFlags(),
-                           previous->attrs, previous->getter(), previous->setter());
+            StackBaseShape base(lastProperty()->base());
+            base.updateGetterSetter(previous->attrs, previous->getter(), previous->setter());
             BaseShape *nbase = BaseShape::getUnowned(cx, base);
             if (!nbase)
                 return false;
             previous->base_ = nbase;
         }
     }
 
     /* If shape has a slot, free its slot number. */
     if (shape->hasSlot()) {
-        freeSlot(cx, shape->slot());
+        self->freeSlot(cx, shape->slot());
         JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
     }
 
     /*
      * A dictionary-mode object owns mutable, unique shapes on a non-circular
      * doubly linked list, hashed by lastProp->table. So we can edit the list
      * and hash in place.
      */
-    if (inDictionaryMode()) {
-        PropertyTable &table = lastProperty()->table();
+    if (self->inDictionaryMode()) {
+        PropertyTable &table = self->lastProperty()->table();
 
         if (SHAPE_HAD_COLLISION(*spp)) {
             *spp = SHAPE_REMOVED;
             ++table.removedCount;
             --table.entryCount;
         } else {
             *spp = NULL;
             --table.entryCount;
 
 #ifdef DEBUG
             /*
              * Check the consistency of the table but limit the number of
              * checks not to alter significantly the complexity of the
              * delete in debug builds, see bug 534493.
              */
-            const Shape *aprop = lastProperty();
+            const Shape *aprop = self->lastProperty();
             for (int n = 50; --n >= 0 && aprop->parent; aprop = aprop->parent)
-                JS_ASSERT_IF(aprop != shape, nativeContains(cx, *aprop));
+                JS_ASSERT_IF(aprop != shape, self->nativeContains(cx, *aprop));
 #endif
         }
 
         /* Remove shape from its non-circular doubly linked list. */
-        Shape *oldLastProp = lastProperty();
-        shape->removeFromDictionary(this);
+        Shape *oldLastProp = self->lastProperty();
+        shape->removeFromDictionary(self);
 
         /* Hand off table from the old to new last property. */
-        oldLastProp->handoffTableTo(lastProperty());
+        oldLastProp->handoffTableTo(self->lastProperty());
 
         /* Generate a new shape for the object, infallibly. */
-        JS_ALWAYS_TRUE(generateOwnShape(cx, spare));
+        JS_ALWAYS_TRUE(self->generateOwnShape(cx, spare));
 
         /* Consider shrinking table if its load factor is <= .25. */
         uint32_t size = table.capacity();
         if (size > PropertyTable::MIN_SIZE && table.entryCount <= size >> 2)
             (void) table.change(-1, cx);
     } else {
         /*
          * Non-dictionary-mode property tables are shared immutables, so all we
          * need do is retract the last property and we'll either get or else
          * lazily make via a later hashify the exact table for the new property
          * lineage.
          */
-        JS_ASSERT(shape == lastProperty());
-        removeLastProperty(cx);
+        JS_ASSERT(shape == self->lastProperty());
+        self->removeLastProperty(cx);
     }
 
-    CHECK_SHAPE_CONSISTENCY(this);
+    CHECK_SHAPE_CONSISTENCY(self);
     return true;
 }
 
 void
 JSObject::clear(JSContext *cx)
 {
     Shape *shape = lastProperty();
     JS_ASSERT(inDictionaryMode() == shape->inDictionary());
@@ -1065,34 +1064,40 @@ JSObject::rollbackProperties(JSContext *
         JS_ASSERT(lastProperty()->hasSlot() && getSlot(lastProperty()->slot()).isUndefined());
         removeLastProperty(cx);
     }
 }
 
 bool
 JSObject::generateOwnShape(JSContext *cx, Shape *newShape)
 {
+    RootedVarObject self(cx, this);
+
     if (!inDictionaryMode() && !toDictionaryMode(cx))
         return false;
 
     if (!newShape) {
         newShape = js_NewGCShape(cx);
         if (!newShape)
             return false;
-        new (newShape) Shape(lastProperty()->base()->unowned(), 0);
+        new (newShape) Shape(self->lastProperty()->base()->unowned(), 0);
     }
 
-    PropertyTable &table = lastProperty()->table();
-    Shape **spp = lastProperty()->isEmptyShape() ? NULL : table.search(lastProperty()->maybePropid(), false);
+    PropertyTable &table = self->lastProperty()->table();
+    Shape **spp = self->lastProperty()->isEmptyShape()
+                  ? NULL
+                  : table.search(self->lastProperty()->maybePropid(), false);
 
-    Shape *oldShape = lastProperty();
-    newShape->initDictionaryShape(*oldShape, &shape_);
+    Shape *oldShape = self->lastProperty();
+
+    StackShape nshape(self->lastProperty());
+    newShape->initDictionaryShape(nshape, self->numFixedSlots(), &self->shape_);
 
     JS_ASSERT(newShape->parent == oldShape);
-    oldShape->removeFromDictionary(this);
+    oldShape->removeFromDictionary(self);
 
     oldShape->handoffTableTo(newShape);
 
     if (spp)
         SHAPE_STORE_PRESERVING_COLLISION(spp, newShape);
     return true;
 }
 
@@ -1148,141 +1153,168 @@ JSObject::clearParent(JSContext *cx)
 
 bool
 JSObject::setParent(JSContext *cx, JSObject *parent)
 {
     if (parent && !parent->setDelegate(cx))
         return false;
 
     if (inDictionaryMode()) {
-        BaseShape base(*lastProperty()->base()->unowned());
-        base.setObjectParent(parent);
+        StackBaseShape base(lastProperty());
+        base.parent = parent;
         UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
         if (!nbase)
             return false;
 
         lastProperty()->base()->adoptUnowned(nbase);
         return true;
     }
 
-    return Shape::setObjectParent(cx, parent, getProto(), &shape_);
+    Shape *newShape = Shape::setObjectParent(cx, parent, getProto(), shape_);
+    if (!newShape)
+        return false;
+
+    shape_ = newShape;
+    return true;
 }
 
-/* static */ bool
-Shape::setObjectParent(JSContext *cx, JSObject *parent, JSObject *proto, HeapPtrShape *listp)
+/* static */ Shape *
+Shape::setObjectParent(JSContext *cx, JSObject *parent, JSObject *proto, Shape *last)
 {
-    if ((*listp)->getObjectParent() == parent)
-        return true;
+    if (last->getObjectParent() == parent)
+        return last;
 
-    BaseShape base(*(*listp)->base()->unowned());
-    base.setObjectParent(parent);
+    StackBaseShape base(last);
+    base.parent = parent;
 
-    return replaceLastProperty(cx, base, proto, listp);
+    return replaceLastProperty(cx, base, proto, last);
 }
 
 bool
 JSObject::preventExtensions(JSContext *cx, js::AutoIdVector *props)
 {
     JS_ASSERT(isExtensible());
 
+    RootedVarObject self(cx, this);
+
     if (props) {
         if (js::FixOp fix = getOps()->fix) {
             bool success;
             if (!fix(cx, this, &success, props))
                 return false;
             if (!success) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CHANGE_EXTENSIBILITY);
                 return false;
             }
         } else {
             if (!js::GetPropertyNames(cx, this, JSITER_HIDDEN | JSITER_OWNONLY, props))
                 return false;
         }
     }
 
-    return setFlag(cx, BaseShape::NOT_EXTENSIBLE, GENERATE_SHAPE);
+    return self->setFlag(cx, BaseShape::NOT_EXTENSIBLE, GENERATE_SHAPE);
 }
 
 bool
 JSObject::setFlag(JSContext *cx, /*BaseShape::Flag*/ uint32_t flag_, GenerateShape generateShape)
 {
     BaseShape::Flag flag = (BaseShape::Flag) flag_;
 
     if (lastProperty()->getObjectFlags() & flag)
         return true;
 
+    RootedVarObject self(cx, this);
+
     if (inDictionaryMode()) {
         if (generateShape == GENERATE_SHAPE && !generateOwnShape(cx))
             return false;
-
-        BaseShape base(*lastProperty()->base()->unowned());
-        base.setObjectFlag(flag);
+        StackBaseShape base(self->lastProperty());
+        base.flags |= flag;
         UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
         if (!nbase)
             return false;
 
-        lastProperty()->base()->adoptUnowned(nbase);
+        self->lastProperty()->base()->adoptUnowned(nbase);
         return true;
     }
 
-    return Shape::setObjectFlag(cx, flag, getProto(), &shape_);
+    Shape *newShape = Shape::setObjectFlag(cx, flag, getProto(), lastProperty());
+    if (!newShape)
+        return false;
+
+    self->shape_ = newShape;
+    return true;
 }
 
-/* static */ bool
-Shape::setObjectFlag(JSContext *cx, BaseShape::Flag flag, JSObject *proto, HeapPtrShape *listp)
+/* static */ Shape *
+Shape::setObjectFlag(JSContext *cx, BaseShape::Flag flag, JSObject *proto, Shape *last)
 {
-    if ((*listp)->getObjectFlags() & flag)
-        return true;
+    if (last->getObjectFlags() & flag)
+        return last;
 
-    BaseShape base(*(*listp)->base()->unowned());
+    StackBaseShape base(last);
     base.flags |= flag;
 
-    return replaceLastProperty(cx, base, proto, listp);
+    return replaceLastProperty(cx, base, proto, last);
 }
 
 /* static */ inline HashNumber
-BaseShapeEntry::hash(const js::BaseShape *base)
+StackBaseShape::hash(const StackBaseShape *base)
 {
-    JS_ASSERT(!base->isOwned());
-
     JSDHashNumber hash = base->flags;
     hash = JS_ROTATE_LEFT32(hash, 4) ^ (jsuword(base->clasp) >> 3);
-    hash = JS_ROTATE_LEFT32(hash, 4) ^ (jsuword(base->parent.get()) >> 3);
-    if (base->rawGetter)
-        hash = JS_ROTATE_LEFT32(hash, 4) ^ jsuword(base->rawGetter);
-    if (base->rawSetter)
-        hash = JS_ROTATE_LEFT32(hash, 4) ^ jsuword(base->rawSetter);
+    hash = JS_ROTATE_LEFT32(hash, 4) ^ (jsuword(base->parent) >> 3);
+    hash = JS_ROTATE_LEFT32(hash, 4) ^ jsuword(base->rawGetter);
+    hash = JS_ROTATE_LEFT32(hash, 4) ^ jsuword(base->rawSetter);
     return hash;
 }
 
 /* static */ inline bool
-BaseShapeEntry::match(UnownedBaseShape *key, const BaseShape *lookup)
+StackBaseShape::match(UnownedBaseShape *key, const StackBaseShape *lookup)
 {
-    JS_ASSERT(!lookup->isOwned());
-
     return key->flags == lookup->flags
         && key->clasp == lookup->clasp
         && key->parent == lookup->parent
-        && key->getterObj == lookup->getterObj
-        && key->setterObj == lookup->setterObj;
+        && (void *) key->rawGetter == lookup->rawGetter
+        && (void *) key->rawSetter == lookup->rawSetter;
 }
 
+/* Root for stack allocated base shapes. */
+class RootStackBaseShape
+{
+    Root<const JSObject*> parentRoot;
+    Maybe<RootObject> getterRoot;
+    Maybe<RootObject> setterRoot;
+
+  public:
+    RootStackBaseShape(JSContext *cx, const StackBaseShape *base)
+        : parentRoot(cx, &base->parent)
+    {
+        if (base->flags & BaseShape::HAS_GETTER_OBJECT)
+            getterRoot.construct(cx, (JSObject **) &base->rawGetter);
+        if (base->flags & BaseShape::HAS_SETTER_OBJECT)
+            setterRoot.construct(cx, (JSObject **) &base->rawSetter);
+    }
+};
+
 /* static */ UnownedBaseShape *
-BaseShape::getUnowned(JSContext *cx, const BaseShape &base)
+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;
 
+    RootStackBaseShape root(cx, &base);
+
     BaseShape *nbase_ = js_NewGCBaseShape(cx);
     if (!nbase_)
         return NULL;
     new (nbase_) BaseShape(base);
 
     UnownedBaseShape *nbase = static_cast<UnownedBaseShape *>(nbase_);
 
     if (!table.relookupOrAdd(p, &base, nbase))
@@ -1307,45 +1339,63 @@ void
 BaseShape::finalize(JSContext *cx, bool background)
 {
     if (table_) {
         cx->delete_(table_);
         table_ = NULL;
     }
 }
 
-/* static */ bool
-Shape::setExtensibleParents(JSContext *cx, HeapPtrShape *listp)
+/* static */ Shape *
+Shape::setExtensibleParents(JSContext *cx, Shape *shape)
 {
-    Shape *shape = *listp;
     JS_ASSERT(!shape->inDictionary());
 
-    BaseShape base(*shape->base()->unowned());
+    StackBaseShape base(shape);
     base.flags |= BaseShape::EXTENSIBLE_PARENTS;
 
     /* This is only used for Block and Call objects, which have a NULL proto. */
-    return replaceLastProperty(cx, base, NULL, listp);
+    return replaceLastProperty(cx, base, NULL, shape);
 }
 
 bool
 Bindings::setExtensibleParents(JSContext *cx)
 {
     if (!ensureShape(cx))
         return false;
-    return Shape::setExtensibleParents(cx, &lastBinding);
+    Shape *newShape = Shape::setExtensibleParents(cx, lastBinding);
+    if (!newShape)
+        return false;
+    lastBinding = newShape;
+    return true;
 }
 
 bool
 Bindings::setParent(JSContext *cx, JSObject *obj)
 {
+    /*
+     * This may be invoked on GC heap allocated bindings, in which case this
+     * is pointing to an internal value of a JSScript that can't itself be
+     * relocated. The script itself will be rooted, and will not be moved, so
+     * mark the stack value as non-relocatable for the stack root analysis.
+     */
+    Bindings *self = this;
+    CheckRoot root(cx, &self);
+
+    RootObject rootObj(cx, &obj);
+
     if (!ensureShape(cx))
         return false;
 
     /* This is only used for Block objects, which have a NULL proto. */
-    return Shape::setObjectParent(cx, obj, NULL, &lastBinding);
+    Shape *newShape = Shape::setObjectParent(cx, obj, NULL, self->lastBinding);
+    if (!newShape)
+        return false;
+    self->lastBinding = newShape;
+    return true;
 }
 
 /* static */ inline HashNumber
 InitialShapeEntry::hash(const Lookup &lookup)
 {
     JSDHashNumber hash = jsuword(lookup.clasp) >> 3;
     hash = JS_ROTATE_LEFT32(hash, 4) ^ (jsuword(lookup.proto) >> 3);
     hash = JS_ROTATE_LEFT32(hash, 4) ^ (jsuword(lookup.parent) >> 3);
@@ -1374,18 +1424,20 @@ EmptyShape::getInitialShape(JSContext *c
     size_t nfixed = GetGCKindSlots(kind, clasp);
     InitialShapeEntry::Lookup lookup(clasp, proto, parent, nfixed, objectFlags);
 
     InitialShapeSet::AddPtr p = table.lookupForAdd(lookup);
 
     if (p)
         return p->shape;
 
-    BaseShape base(clasp, parent, objectFlags);
-    UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
+    RootedVar<UnownedBaseShape*> nbase(cx);
+
+    StackBaseShape base(clasp, parent, objectFlags);
+    nbase = BaseShape::getUnowned(cx, base);
     if (!nbase)
         return NULL;
 
     Shape *shape = JS_PROPERTY_TREE(cx).newShape(cx);
     if (!shape)
         return NULL;
     new (shape) EmptyShape(nbase, nfixed);
 
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -340,17 +340,18 @@ class PropertyTree;
  */
 
 class UnownedBaseShape;
 
 class BaseShape : public js::gc::Cell
 {
   public:
     friend struct Shape;
-    friend struct BaseShapeEntry;
+    friend struct StackBaseShape;
+    friend struct StackShape;
 
     enum Flag {
         /* Owned by the referring shape. */
         OWNED_SHAPE        = 0x1,
 
         /* getterObj/setterObj are active in unions below. */
         HAS_GETTER_OBJECT  = 0x2,
         HAS_SETTER_OBJECT  = 0x4,
@@ -402,46 +403,47 @@ class BaseShape : public js::gc::Cell
     PropertyTable       *table_;
 
   public:
     void finalize(JSContext *cx, bool background);
 
     inline BaseShape(Class *clasp, JSObject *parent, uint32_t objectFlags);
     inline BaseShape(Class *clasp, JSObject *parent, uint32_t objectFlags,
                      uint8_t attrs, PropertyOp rawGetter, StrictPropertyOp rawSetter);
+    inline BaseShape(const StackBaseShape &base);
+
+    /* Not defined: BaseShapes must not be stack allocated. */
+    ~BaseShape();
 
     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 setObjectParent(JSObject *obj);
-    JSObject *getObjectParent() { return parent; }
-
-    void setObjectFlag(Flag flag) { JS_ASSERT(!(flag & ~OBJECT_FLAG_MASK)); flags |= flag; }
+    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); }
     JSObject *setterObject() const { JS_ASSERT(hasSetterObject()); return setterObj; }
 
     bool hasTable() const { JS_ASSERT_IF(table_, isOwned()); return table_ != NULL; }
     PropertyTable &table() const { JS_ASSERT(table_ && isOwned()); return *table_; }
     void setTable(PropertyTable *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 BaseShape &base);
+    static UnownedBaseShape *getUnowned(JSContext *cx, const StackBaseShape &base);
 
     /* Get the canonical base shape. */
     inline UnownedBaseShape *unowned();
 
     /* Get the canonical base shape for an owned one. */
     inline UnownedBaseShape *baseUnowned();
 
     /* Get the canonical base shape for an unowned one (i.e. identity). */
@@ -454,16 +456,18 @@ class BaseShape : public js::gc::Cell
     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 ThingRootKind rootKind() { return THING_ROOT_BASE_SHAPE; }
+
   private:
     static void staticAsserts() {
         JS_STATIC_ASSERT(offsetof(BaseShape, clasp) == offsetof(js::shadow::BaseShape, clasp));
     }
 };
 
 class UnownedBaseShape : public BaseShape {};
 
@@ -481,31 +485,64 @@ BaseShape::toUnowned()
 
 UnownedBaseShape *
 BaseShape::baseUnowned()
 {
     JS_ASSERT(isOwned() && unowned_); return unowned_;
 }
 
 /* Entries for the per-compartment baseShapes set of unowned base shapes. */
-struct BaseShapeEntry
+struct StackBaseShape
 {
-    typedef const BaseShape *Lookup;
+    typedef const StackBaseShape *Lookup;
+
+    uint32_t flags;
+    Class *clasp;
+    JSObject *parent;
+    PropertyOp rawGetter;
+    StrictPropertyOp rawSetter;
+
+    StackBaseShape(BaseShape *base)
+      : flags(base->flags & BaseShape::OBJECT_FLAG_MASK),
+        clasp(base->clasp),
+        parent(base->parent),
+        rawGetter(NULL),
+        rawSetter(NULL)
+    {}
 
-    static inline HashNumber hash(const BaseShape *base);
-    static inline bool match(UnownedBaseShape *key, const BaseShape *lookup);
+    StackBaseShape(Class *clasp, JSObject *parent, uint32_t objectFlags)
+      : flags(objectFlags),
+        clasp(clasp),
+        parent(parent),
+        rawGetter(NULL),
+        rawSetter(NULL)
+    {}
+
+    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);
 };
-typedef HashSet<ReadBarriered<UnownedBaseShape>, BaseShapeEntry, SystemAllocPolicy> BaseShapeSet;
+
+typedef HashSet<ReadBarriered<UnownedBaseShape>,
+                StackBaseShape,
+                SystemAllocPolicy> BaseShapeSet;
 
 struct Shape : public js::gc::Cell
 {
     friend struct ::JSObject;
     friend struct ::JSFunction;
     friend class js::PropertyTree;
     friend class js::Bindings;
+    friend struct js::StackShape;
+    friend struct js::StackBaseShape;
     friend bool IsShapeAboutToBeFinalized(JSContext *cx, const js::Shape *shape);
 
   protected:
     HeapPtrBaseShape    base_;
     HeapId              propid_;
 
     JS_ENUM_HEADER(SlotInfo, uint32_t)
     {
@@ -544,29 +581,30 @@ struct Shape : public js::gc::Cell
         KidsPointer kids;       /* null, single child, or a tagged ptr
                                    to many-kids data structure */
         HeapPtrShape *listp;    /* dictionary list starting at lastProp
                                    has a double-indirect back pointer,
                                    either to shape->parent if not last,
                                    else to obj->lastProp */
     };
 
-    static inline Shape **search(JSContext *cx, HeapPtrShape *pstart, jsid id,
-                                 bool adding = false);
-    static js::Shape *newDictionaryList(JSContext *cx, HeapPtrShape *listp);
+    static inline Shape *search(JSContext *cx, Shape *start, jsid id,
+                                Shape ***pspp, bool adding = false);
 
     inline void removeFromDictionary(JSObject *obj);
     inline void insertIntoDictionary(HeapPtrShape *dictp);
 
-    inline void initDictionaryShape(const Shape &child, HeapPtrShape *dictp);
+    inline void initDictionaryShape(const StackShape &child, uint32_t nfixed,
+                                    HeapPtrShape *dictp);
 
-    Shape *getChildBinding(JSContext *cx, const Shape &child, HeapPtrShape *lastBinding);
+    Shape *getChildBinding(JSContext *cx, const StackShape &child);
 
     /* Replace the base shape of the last shape in a non-dictionary lineage with base. */
-    static bool replaceLastProperty(JSContext *cx, const BaseShape &base, JSObject *proto, HeapPtrShape *lastp);
+    static Shape *replaceLastProperty(JSContext *cx, const StackBaseShape &base,
+                                      JSObject *proto, Shape *shape);
 
     bool hashify(JSContext *cx);
     void handoffTableTo(Shape *newShape);
 
     inline void setParent(js::Shape *p);
 
     bool ensureOwnBaseShape(JSContext *cx) {
         if (base()->isOwned())
@@ -598,19 +636,17 @@ struct Shape : public js::gc::Cell
 
     const HeapPtrShape &previous() const {
         return parent;
     }
 
     class Range {
       protected:
         friend struct Shape;
-
         const Shape *cursor;
-        const Shape *end;
 
       public:
         Range(const Shape *shape) : cursor(shape) { }
 
         bool empty() const {
             return cursor->isEmptyShape();
         }
 
@@ -618,27 +654,35 @@ struct Shape : public js::gc::Cell
             JS_ASSERT(!empty());
             return *cursor;
         }
 
         void popFront() {
             JS_ASSERT(!empty());
             cursor = cursor->parent;
         }
+
+        class Root {
+            js::Root<const Shape*> cursorRoot;
+          public:
+            Root(JSContext *cx, Range *range)
+              : cursorRoot(cx, &range->cursor)
+            {}
+        };
     };
 
     Range all() const {
         return Range(this);
     }
 
     Class *getObjectClass() const { return base()->clasp; }
     JSObject *getObjectParent() const { return base()->parent; }
 
-    static bool setObjectParent(JSContext *cx, JSObject *obj, JSObject *proto, HeapPtrShape *listp);
-    static bool setObjectFlag(JSContext *cx, BaseShape::Flag flag, JSObject *proto, HeapPtrShape *listp);
+    static Shape *setObjectParent(JSContext *cx, JSObject *obj, JSObject *proto, Shape *last);
+    static Shape *setObjectFlag(JSContext *cx, BaseShape::Flag flag, JSObject *proto, Shape *last);
 
     uint32_t getObjectFlags() const { return base()->getObjectFlags(); }
     bool hasObjectFlag(BaseShape::Flag flag) const {
         JS_ASSERT(!(flag & ~BaseShape::OBJECT_FLAG_MASK));
         return !!(base()->flags & flag);
     }
 
   protected:
@@ -652,28 +696,28 @@ struct Shape : public js::gc::Cell
         NON_NATIVE      = 0x01,
 
         /* Property stored in per-object dictionary, not shared property tree. */
         IN_DICTIONARY   = 0x02,
 
         UNUSED_BITS     = 0x3C
     };
 
-    Shape(UnownedBaseShape *base, jsid id, uint32_t slot, uint32_t nfixed, uintN attrs,
-          uintN flags, intN shortid);
-
     /* Get a shape identical to this one, without parent/kids information. */
-    Shape(const Shape *other);
+    Shape(const StackShape &other, uint32_t nfixed);
 
     /* Used by EmptyShape (see jsscopeinlines.h). */
     Shape(UnownedBaseShape *base, uint32_t nfixed);
 
     /* Copy constructor disabled, to avoid misuse of the above form. */
     Shape(const Shape &other);
 
+    /* Not defined: Shapes must not be stack allocated. */
+    ~Shape();
+
     /*
      * 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
      * assigned yet. After construction, hasSlot() implies !hasMissingSlot().
      */
     bool hasMissingSlot() const { return maybeSlot() == SHAPE_INVALID_SLOT; }
 
@@ -747,18 +791,18 @@ struct Shape : public js::gc::Cell
     Value setterOrUndefined() const {
         return (hasSetterValue() && base()->setterObj)
                ? ObjectValue(*base()->setterObj)
                : UndefinedValue();
     }
 
     void update(js::PropertyOp getter, js::StrictPropertyOp setter, uint8_t attrs);
 
-    inline JSDHashNumber hash() const;
-    inline bool matches(const js::Shape *p) const;
+    inline bool matches(const Shape *other) const;
+    inline bool matches(const StackShape &other) const;
     inline bool matchesParamsAfterId(BaseShape *base,
                                      uint32_t aslot, uintN aattrs, uintN aflags,
                                      intN ashortid) const;
 
     bool get(JSContext* cx, JSObject *receiver, JSObject *obj, JSObject *pobj, js::Value* vp) const;
     bool set(JSContext* cx, JSObject *obj, bool strict, js::Value* vp) const;
 
     BaseShape *base() const { return base_; }
@@ -775,17 +819,17 @@ struct Shape : public js::gc::Cell
     uint32_t slotSpan() const {
         JS_ASSERT(!inDictionary());
         uint32_t free = JSSLOT_FREE(getObjectClass());
         return hasMissingSlot() ? free : Max(free, maybeSlot() + 1);
     }
 
     void setSlot(uint32_t slot) {
         JS_ASSERT(slot <= SHAPE_INVALID_SLOT);
-        slotInfo = slotInfo & ~SLOT_MASK;
+        slotInfo = slotInfo & ~Shape::SLOT_MASK;
         slotInfo = slotInfo | slot;
     }
 
     uint32_t numFixedSlots() const {
         return (slotInfo >> FIXED_SLOTS_SHIFT);
     }
 
     void setNumFixedSlots(uint32_t nfixed) {
@@ -885,17 +929,17 @@ struct Shape : public js::gc::Cell
      * extensible, and the other sorts of objects that appear in the scope
      * chains ('with' blocks, say) are not CacheableNonGlobalScopes.
      *
      * If the hasExtensibleParents flag is set for the last property in a
      * script's bindings or a compiler-generated Block object, then created
      * Call or Block objects need unique shapes. If the flag is clear, then we
      * can use lastBinding's shape.
      */
-    static bool setExtensibleParents(JSContext *cx, HeapPtrShape *listp);
+    static Shape *setExtensibleParents(JSContext *cx, Shape *shape);
     bool extensibleParents() const { return !!(base()->flags & BaseShape::EXTENSIBLE_PARENTS); }
 
     uint32_t entryCount() const {
         if (hasTable())
             return table().entryCount;
 
         const js::Shape *shape = this;
         uint32_t count = 0;
@@ -929,16 +973,18 @@ struct Shape : public js::gc::Cell
 
     /*
      * All weak references need a read barrier for incremental GC. This getter
      * method implements the read barrier. It's used to obtain initial shapes
      * from the compartment.
      */
     static inline void readBarrier(const Shape *shape);
 
+    static inline ThingRootKind rootKind() { return THING_ROOT_SHAPE; }
+
     /* For JIT usage */
     static inline size_t offsetOfBase() { return offsetof(Shape, base_); }
 
   private:
     static void staticAsserts() {
         JS_STATIC_ASSERT(offsetof(Shape, base_) == offsetof(js::shadow::Shape, base));
         JS_STATIC_ASSERT(offsetof(Shape, slotInfo) == offsetof(js::shadow::Shape, slotInfo));
         JS_STATIC_ASSERT(FIXED_SLOTS_SHIFT == js::shadow::Shape::FIXED_SLOTS_SHIFT);
@@ -995,18 +1041,83 @@ struct InitialShapeEntry
             : clasp(clasp), proto(proto), parent(parent),
               nfixed(nfixed), baseFlags(baseFlags)
         {}
     };
 
     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;
+
+    StackShape(UnownedBaseShape *base, jsid propid, uint32_t slot,
+               uint32_t nfixed, uintN attrs, uintN flags, intN shortid)
+      : base(base),
+        propid(propid),
+        slot_(slot),
+        attrs(uint8_t(attrs)),
+        flags(uint8_t(flags)),
+        shortid(int16_t(shortid))
+    {
+        JS_ASSERT(base);
+        JS_ASSERT(!JSID_IS_VOID(propid));
+        JS_ASSERT(slot <= SHAPE_INVALID_SLOT);
+    }
+
+    StackShape(const Shape *shape)
+      : base(shape->base()->unowned()),
+        propid(shape->maybePropid()),
+        slot_(shape->slotInfo & Shape::SLOT_MASK),
+        attrs(shape->attrs),
+        flags(shape->flags),
+        shortid(shape->shortid_)
+    {}
+
+    bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; }
+    bool hasMissingSlot() const { return maybeSlot() == SHAPE_INVALID_SLOT; }
+
+    uint32_t slot() const { JS_ASSERT(hasSlot() && !hasMissingSlot()); return slot_; }
+    uint32_t maybeSlot() const { return slot_; }
+
+    uint32_t slotSpan() const {
+        uint32_t free = JSSLOT_FREE(base->clasp);
+        return hasMissingSlot() ? free : (maybeSlot() + 1);
+    }
+
+    void setSlot(uint32_t slot) {
+        JS_ASSERT(slot <= SHAPE_INVALID_SLOT);
+        slot_ = slot;
+    }
+
+    inline JSDHashNumber hash() const;
+};
+
+/* Rooter for stack allocated shapes. */
+class RootStackShape
+{
+    Root<const UnownedBaseShape*> baseShapeRoot;
+    Root<const jsid> propidRoot;
+
+  public:
+    RootStackShape(JSContext *cx, const StackShape *shape)
+      : baseShapeRoot(cx, &shape->base),
+        propidRoot(cx, &shape->propid)
+    {}
+};
+
 } /* namespace js */
 
 /* js::Shape pointer tag bit indicating a collision. */
 #define SHAPE_COLLISION                 (jsuword(1))
 #define SHAPE_REMOVED                   ((js::Shape *) SHAPE_COLLISION)
 
 /* Macros to get and set shape pointer values and collision flags. */
 #define SHAPE_IS_FREE(shape)            ((shape) == NULL)
@@ -1020,60 +1131,55 @@ typedef HashSet<InitialShapeEntry, Initi
 #define SHAPE_CLEAR_COLLISION(shape)                                          \
     ((js::Shape *) (jsuword(shape) & ~SHAPE_COLLISION))
 
 #define SHAPE_STORE_PRESERVING_COLLISION(spp, shape)                          \
     (*(spp) = (js::Shape *) (jsuword(shape) | SHAPE_HAD_COLLISION(*(spp))))
 
 namespace js {
 
-/*
- * The search succeeds if it finds a Shape with the given id.  There are two
- * success cases:
- * - If the Shape is the last in its shape lineage, we return |startp|, which
- *   is &obj->lastProp or something similar.
- * - Otherwise, we return &shape->parent, where |shape| is the successor to the
- *   found Shape.
- *
- * There is one failure case:  we return &emptyShape->parent, where
- * |emptyShape| is the EmptyShape at the start of the shape lineage.
- */
-JS_ALWAYS_INLINE Shape **
-Shape::search(JSContext *cx, HeapPtrShape *pstart, jsid id, bool adding)
+inline Shape *
+Shape::search(JSContext *cx, Shape *start, jsid id, Shape ***pspp, bool adding)
 {
-    Shape *start = *pstart;
-    if (start->hasTable())
-        return start->table().search(id, adding);
+    if (start->inDictionary()) {
+        *pspp = start->table().search(id, adding);
+        return SHAPE_FETCH(*pspp);
+    }
+
+    *pspp = NULL;
+
+    if (start->hasTable()) {
+        Shape **spp = start->table().search(id, adding);
+        return SHAPE_FETCH(spp);
+    }
 
     if (start->numLinearSearches() == LINEAR_SEARCHES_MAX) {
-        if (start->isBigEnoughForAPropertyTable() && start->hashify(cx))
-            return start->table().search(id, adding);
+        if (start->isBigEnoughForAPropertyTable()) {
+            RootShape startRoot(cx, &start);
+            RootId idRoot(cx, &id);
+            if (start->hashify(cx)) {
+                Shape **spp = start->table().search(id, adding);
+                return SHAPE_FETCH(spp);
+            }
+        }
         /* 
          * No table built -- there weren't enough entries, or OOM occurred.
          * Don't increment numLinearSearches, to keep hasTable() false.
          */
         JS_ASSERT(!start->hasTable());
     } else {
         start->incrementNumLinearSearches();
     }
 
-    /*
-     * Not enough searches done so far to justify hashing: search linearly
-     * from start.
-     *
-     * We don't use a Range here, or stop at null parent (the empty shape at
-     * the end).  This avoids an extra load per iteration at the cost (if the
-     * search fails) of an extra load and id test at the end.
-     */
-    HeapPtrShape *spp;
-    for (spp = pstart; js::Shape *shape = *spp; spp = &shape->parent) {
+    for (Shape *shape = start; shape; shape = shape->parent) {
         if (shape->maybePropid() == id)
-            return spp->unsafeGet();
+            return shape;
     }
-    return spp->unsafeGet();
+
+    return NULL;
 }
 
 } // namespace js
 
 #ifdef _MSC_VER
 #pragma warning(pop)
 #pragma warning(pop)
 #endif
--- a/js/src/jsscopeinlines.h
+++ b/js/src/jsscopeinlines.h
@@ -86,26 +86,55 @@ BaseShape::BaseShape(Class *clasp, JSObj
         JSObject::writeBarrierPost(this->getterObj, &this->getterObj);
     }
     if ((attrs & JSPROP_SETTER) && rawSetter) {
         flags |= HAS_SETTER_OBJECT;
         JSObject::writeBarrierPost(this->setterObj, &this->setterObj);
     }
 }
 
+inline
+BaseShape::BaseShape(const StackBaseShape &base)
+{
+    PodZero(this);
+    this->clasp = base.clasp;
+    this->parent = base.parent;
+    this->flags = base.flags;
+    this->rawGetter = base.rawGetter;
+    this->rawSetter = base.rawSetter;
+}
+
 inline bool
 BaseShape::matchesGetterSetter(PropertyOp rawGetter, StrictPropertyOp rawSetter) const
 {
     return rawGetter == this->rawGetter && rawSetter == this->rawSetter;
 }
 
+inline
+StackBaseShape::StackBaseShape(Shape *shape)
+  : flags(shape->getObjectFlags()),
+    clasp(shape->getObjectClass()),
+    parent(shape->getObjectParent())
+{
+    updateGetterSetter(shape->attrs, shape->getter(), shape->setter());
+}
+
 inline void
-BaseShape::setObjectParent(JSObject *obj)
+StackBaseShape::updateGetterSetter(uint8_t attrs,
+                                   PropertyOp rawGetter,
+                                   StrictPropertyOp rawSetter)
 {
-    parent = obj;
+    flags &= ~(BaseShape::HAS_GETTER_OBJECT | BaseShape::HAS_SETTER_OBJECT);
+    if ((attrs & JSPROP_GETTER) && rawGetter)
+        flags |= BaseShape::HAS_GETTER_OBJECT;
+    if ((attrs & JSPROP_SETTER) && rawSetter)
+        flags |= BaseShape::HAS_SETTER_OBJECT;
+
+    this->rawGetter = rawGetter;
+    this->rawSetter = rawSetter;
 }
 
 inline void
 BaseShape::adoptUnowned(UnownedBaseShape *other)
 {
     /*
      * This is a base shape owned by a dictionary object, update it to reflect the
      * unowned base shape of a new last property.
@@ -144,41 +173,23 @@ BaseShape::assertConsistency()
         JS_ASSERT_IF(hasSetterObject(), setterObject() == unowned->setterObject());
         JS_ASSERT(getObjectParent() == unowned->getObjectParent());
         JS_ASSERT(getObjectFlags() == unowned->getObjectFlags());
     }
 #endif
 }
 
 inline
-Shape::Shape(UnownedBaseShape *base, jsid propid, uint32_t slot, uint32_t nfixed,
-             uintN attrs, uintN flags, intN shortid)
-  : base_(base),
-    propid_(propid),
-    slotInfo(slot | (nfixed << FIXED_SLOTS_SHIFT)),
-    attrs(uint8_t(attrs)),
-    flags(uint8_t(flags)),
-    shortid_(int16_t(shortid)),
-    parent(NULL)
-{
-    JS_ASSERT(base);
-    JS_ASSERT(!JSID_IS_VOID(propid));
-    JS_ASSERT_IF(isMethod(), !base->rawGetter);
-    JS_ASSERT_IF(attrs & JSPROP_READONLY, !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
-    kids.setNull();
-}
-
-inline
-Shape::Shape(const Shape *other)
-  : base_(other->base()->unowned()),
-    propid_(other->maybePropid()),
-    slotInfo(other->slotInfo & ~LINEAR_SEARCHES_MASK),
-    attrs(other->attrs),
-    flags(other->flags),
-    shortid_(other->maybeShortid()),
+Shape::Shape(const StackShape &other, uint32_t nfixed)
+  : base_(other.base),
+    propid_(other.propid),
+    slotInfo(other.maybeSlot() | (nfixed << FIXED_SLOTS_SHIFT)),
+    attrs(other.attrs),
+    flags(other.flags),
+    shortid_(other.shortid),
     parent(NULL)
 {
     kids.setNull();
 }
 
 inline
 Shape::Shape(UnownedBaseShape *base, uint32_t nfixed)
   : base_(base),
@@ -189,38 +200,45 @@ Shape::Shape(UnownedBaseShape *base, uin
     shortid_(0),
     parent(NULL)
 {
     JS_ASSERT(base);
     kids.setNull();
 }
 
 inline JSDHashNumber
-Shape::hash() const
+StackShape::hash() const
 {
-    JSDHashNumber hash = jsuword(base()->unowned());
+    JSDHashNumber hash = jsuword(base);
 
     /* Accumulate from least to most random so the low bits are most random. */
-    hash = JS_ROTATE_LEFT32(hash, 4) ^ (flags & PUBLIC_FLAGS);
+    hash = JS_ROTATE_LEFT32(hash, 4) ^ (flags & Shape::PUBLIC_FLAGS);
     hash = JS_ROTATE_LEFT32(hash, 4) ^ attrs;
-    hash = JS_ROTATE_LEFT32(hash, 4) ^ shortid_;
-    hash = JS_ROTATE_LEFT32(hash, 4) ^ maybeSlot();
-    hash = JS_ROTATE_LEFT32(hash, 4) ^ JSID_BITS(propid_.get());
+    hash = JS_ROTATE_LEFT32(hash, 4) ^ shortid;
+    hash = JS_ROTATE_LEFT32(hash, 4) ^ slot_;
+    hash = JS_ROTATE_LEFT32(hash, 4) ^ JSID_BITS(propid);
     return hash;
 }
 
 inline bool
 Shape::matches(const js::Shape *other) const
 {
     return propid_.get() == other->propid_.get() &&
            matchesParamsAfterId(other->base(), other->maybeSlot(), other->attrs,
                                 other->flags, other->shortid_);
 }
 
 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,
                             uintN aattrs, uintN aflags, intN ashortid) const
 {
     return base->unowned() == this->base()->unowned() &&
            maybeSlot() == aslot &&
            attrs == aattrs &&
            ((flags ^ aflags) & PUBLIC_FLAGS) == 0 &&
            shortid_ == ashortid;
@@ -292,44 +310,41 @@ Shape::removeFromDictionary(JSObject *ob
 
     if (parent)
         parent->listp = listp;
     *listp = parent;
     listp = NULL;
 }
 
 inline void
-Shape::insertIntoDictionary(HeapPtr<js::Shape> *dictp)
+Shape::insertIntoDictionary(HeapPtrShape *dictp)
 {
     /*
      * Don't assert inDictionaryMode() here because we may be called from
      * JSObject::toDictionaryMode via JSObject::newDictionaryShape.
      */
     JS_ASSERT(inDictionary());
     JS_ASSERT(!listp);
 
     JS_ASSERT_IF(*dictp, (*dictp)->inDictionary());
     JS_ASSERT_IF(*dictp, (*dictp)->listp == dictp);
     JS_ASSERT_IF(*dictp, compartment() == (*dictp)->compartment());
 
     setParent(*dictp);
     if (parent)
         parent->listp = &parent;
-    listp = dictp;
+    listp = (HeapPtrShape *) dictp;
     *dictp = this;
 }
 
 void
-Shape::initDictionaryShape(const Shape &child, HeapPtrShape *dictp)
+Shape::initDictionaryShape(const StackShape &child, uint32_t nfixed, HeapPtrShape *dictp)
 {
-    UnownedBaseShape *base = child.base()->unowned();
-
-    new (this) Shape(base, child.maybePropid(),
-                     child.maybeSlot(), child.numFixedSlots(), child.attrs,
-                     child.flags | IN_DICTIONARY, child.maybeShortid());
+    new (this) Shape(child, nfixed);
+    this->flags |= IN_DICTIONARY;
 
     this->listp = NULL;
     insertIntoDictionary(dictp);
 }
 
 inline
 EmptyShape::EmptyShape(UnownedBaseShape *base, uint32_t nfixed)
   : js::Shape(base, nfixed)
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -85,19 +85,18 @@ using namespace js::frontend;
 namespace js {
 
 BindingKind
 Bindings::lookup(JSContext *cx, JSAtom *name, uintN *indexp) const
 {
     if (!lastBinding)
         return NONE;
 
-    Shape *shape =
-        SHAPE_FETCH(Shape::search(cx, const_cast<HeapPtrShape *>(&lastBinding),
-                                  ATOM_TO_JSID(name)));
+    Shape **spp;
+    Shape *shape = Shape::search(cx, lastBinding, ATOM_TO_JSID(name), &spp);
     if (!shape)
         return NONE;
 
     if (indexp)
         *indexp = shape->shortid();
 
     if (shape->getter() == GetCallArg)
         return ARGUMENT;
@@ -161,29 +160,31 @@ Bindings::add(JSContext *cx, JSAtom *nam
     jsid id;
     if (!name) {
         JS_ASSERT(kind == ARGUMENT); /* destructuring */
         id = INT_TO_JSID(nargs);
     } else {
         id = ATOM_TO_JSID(name);
     }
 
-    BaseShape base(&CallClass, NULL, BaseShape::VAROBJ, attrs, getter, setter);
+    StackBaseShape base(&CallClass, NULL, BaseShape::VAROBJ);
+    base.updateGetterSetter(attrs, getter, setter);
+
     UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
     if (!nbase)
         return NULL;
 
-    Shape child(nbase, id, slot, 0, attrs, Shape::HAS_SHORTID, *indexp);
+    StackShape child(nbase, id, slot, 0, attrs, Shape::HAS_SHORTID, *indexp);
 
     /* Shapes in bindings cannot be dictionaries. */
-    Shape *shape = lastBinding->getChildBinding(cx, child, &lastBinding);
+    Shape *shape = lastBinding->getChildBinding(cx, child);
     if (!shape)
         return false;
 
-    JS_ASSERT(lastBinding == shape);
+    lastBinding = shape;
     ++*indexp;
     return true;
 }
 
 bool
 Bindings::getLocalNameArray(JSContext *cx, Vector<JSAtom *> *namesp)
 {
     JS_ASSERT(lastBinding);
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -43,16 +43,17 @@
 /*
  * JS script descriptor.
  */
 #include "jsatom.h"
 #include "jsprvtd.h"
 #include "jsdbgapi.h"
 #include "jsclist.h"
 #include "jsinfer.h"
+#include "jsscope.h"
 
 #include "gc/Barrier.h"
 
 /*
  * Type of try note associated with each catch or finally block, and also with
  * for-in loops.
  */
 typedef enum JSTryNoteKind {
@@ -202,17 +203,17 @@ class Bindings {
 
     bool hasUpvars() const { return nupvars > 0; }
     bool hasLocalNames() const { return countLocalNames() > 0; }
 
     /* Ensure these bindings have a shape lineage. */
     inline bool ensureShape(JSContext *cx);
 
     /* Returns the shape lineage generated for these bindings. */
-    inline js::Shape *lastShape() const;
+    inline Shape *lastShape() const;
 
     /* See Scope::extensibleParents */
     inline bool extensibleParents();
     bool setExtensibleParents(JSContext *cx);
 
     bool setParent(JSContext *cx, JSObject *obj);
 
     enum {
@@ -307,16 +308,24 @@ class Bindings {
      * Sometimes iteration order must be from oldest to youngest, however. For
      * such cases, use js::Bindings::getLocalNameArray.
      */
     const js::Shape *lastArgument() const;
     const js::Shape *lastVariable() const;
     const js::Shape *lastUpvar() const;
 
     void trace(JSTracer *trc);
+
+    /* Rooter for stack allocated Bindings. */
+    struct StackRoot {
+        RootShape root;
+        StackRoot(JSContext *cx, Bindings *bindings)
+            : root(cx, (Shape **) &bindings->lastBinding)
+        {}
+    };
 };
 
 } /* namespace js */
 
 #define JS_OBJECT_ARRAY_SIZE(length)                                          \
     (offsetof(JSObjectArray, vector) + sizeof(JSObject *) * (length))
 
 #ifdef JS_METHODJIT
@@ -815,16 +824,18 @@ struct JSScript : public js::gc::Cell {
 #ifdef DEBUG
     uint32_t stepModeCount() { return debug ? (debug->stepMode & stepCountMask) : 0; }
 #endif
 
     void finalize(JSContext *cx, bool background);
 
     static inline void writeBarrierPre(JSScript *script);
     static inline void writeBarrierPost(JSScript *script, void *addr);
+
+    static inline js::ThingRootKind rootKind() { return js::THING_ROOT_SCRIPT; }
 };
 
 /* If this fails, padding_ can be removed. */
 JS_STATIC_ASSERT(sizeof(JSScript) % js::gc::Cell::CellSize == 0);
 
 #define SHARP_NSLOTS            2       /* [#array, #depth] slots if the script
                                            uses sharp variables */
 static JS_INLINE uintN
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -1618,25 +1618,25 @@ js::str_match(JSContext *cx, uintN argc,
         return BuildFlatMatchArray(cx, str, *fm, vp);
     if (cx->isExceptionPending())  /* from tryFlatMatch */
         return false;
 
     const RegExpPair *rep = g.normalizeRegExp(false, 1, argc, vp);
     if (!rep)
         return false;
 
-    AutoObjectRooter array(cx);
-    MatchArgType arg = array.addr();
+    JSObject *array = NULL;
+    MatchArgType arg = &array;
     RegExpStatics *res = cx->regExpStatics();
     Value rval;
     if (!DoMatch(cx, res, str, *rep, MatchCallback, arg, MATCH_ARGS, &rval))
         return false;
 
     if (rep->matcher().global())
-        vp->setObjectOrNull(array.object());
+        vp->setObjectOrNull(array);
     else
         *vp = rval;
     return true;
 }
 
 JSBool
 js::str_search(JSContext *cx, uintN argc, Value *vp)
 {
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -5570,17 +5570,16 @@ xml_attribute(JSContext *cx, uintN argc,
 static JSBool
 xml_attributes(JSContext *cx, uintN argc, jsval *vp)
 {
     jsval name = STRING_TO_JSVAL(cx->runtime->atomState.starAtom);
     JSObject *qn = ToAttributeName(cx, name);
     if (!qn)
         return JS_FALSE;
 
-    AutoObjectRooter tvr(cx, qn);
     jsid id = OBJECT_TO_JSID(qn);
     JSObject *obj = ToObject(cx, &vp[1]);
     if (!obj)
         return JS_FALSE;
     return GetProperty(cx, obj, id, vp);
 }
 
 static JSXML *
--- a/js/src/perf/jsperf.cpp
+++ b/js/src/perf/jsperf.cpp
@@ -250,23 +250,25 @@ GetPMFromThis(JSContext* cx, jsval* vp)
         JS_GetInstancePrivate(cx, this_, &pm_class, JS_ARGV(cx, vp));
 }
 
 namespace JS {
 
 JSObject*
 RegisterPerfMeasurement(JSContext *cx, JSObject *global)
 {
-    JSObject *prototype = JS_InitClass(cx, global, 0 /* parent */,
-                                       &pm_class, pm_construct, 1,
-                                       pm_props, pm_fns, 0, 0);
+    js::RootedVarObject prototype(cx);
+    prototype = JS_InitClass(cx, global, 0 /* parent */,
+                             &pm_class, pm_construct, 1,
+                             pm_props, pm_fns, 0, 0);
     if (!prototype)
         return 0;
 
-    JSObject *ctor = JS_GetConstructor(cx, prototype);
+    js::RootedVarObject ctor(cx);
+    ctor = JS_GetConstructor(cx, prototype);
     if (!ctor)
         return 0;
 
     for (const pm_const *c = pm_consts; c->name; c++) {
         if (!JS_DefineProperty(cx, ctor, c->name, INT_TO_JSVAL(c->value),
                                JS_PropertyStub, JS_StrictPropertyStub, PM_CATTRS))
             return 0;
     }
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -432,16 +432,18 @@ Process(JSContext *cx, JSObject *obj, co
     size_t size;
     jschar *uc_buffer;
     size_t uc_len;
     int lineno;
     int startline;
     FILE *file;
     uint32_t oldopts;
 
+    RootObject root(cx, &obj);
+
     if (forceTTY || !filename || strcmp(filename, "-") == 0) {
         file = stdin;
     } else {
         file = fopen(filename, "r");
         if (!file) {
             JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
                                  JSSMSG_CANT_OPEN, filename, strerror(errno));
             gExitCode = EXITCODE_FILE_NOT_FOUND;
@@ -2943,20 +2945,19 @@ NewSandbox(JSContext *cx, bool lazy)
         if (!lazy && !JS_InitStandardClasses(cx, obj))
             return NULL;
 
         AutoValueRooter root(cx, BooleanValue(lazy));
         if (!JS_SetProperty(cx, obj, "lazy", root.jsval_addr()))
             return NULL;
     }
 
-    AutoObjectRooter objroot(cx, obj);
-    if (!cx->compartment->wrap(cx, objroot.addr()))
+    if (!cx->compartment->wrap(cx, &obj))
         return NULL;
-    return objroot.object();
+    return obj;
 }
 
 static JSBool
 EvalInContext(JSContext *cx, uintN argc, jsval *vp)
 {
     JSString *str;
     JSObject *sobj = NULL;
     if (!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "S / o", &str, &sobj))
@@ -4964,19 +4965,21 @@ DestroyContext(JSContext *cx, bool withG
     JS_SetContextPrivate(cx, NULL);
     free(data);
     WITH_SIGNALS_DISABLED(withGC ? JS_DestroyContext(cx) : JS_DestroyContextNoGC(cx));
 }
 
 static JSObject *
 NewGlobalObject(JSContext *cx, CompartmentKind compartment)
 {
-    JSObject *glob = (compartment == NEW_COMPARTMENT)
-                     ? JS_NewCompartmentAndGlobalObject(cx, &global_class, NULL)
-                     : JS_NewGlobalObject(cx, &global_class);
+    RootedVarObject glob(cx);
+
+    glob = (compartment == NEW_COMPARTMENT)
+           ? JS_NewCompartmentAndGlobalObject(cx, &global_class, NULL)
+           : JS_NewGlobalObject(cx, &global_class);
     if (!glob)
         return NULL;
 
     {
         JSAutoEnterCompartment ac;
         if (!ac.enter(cx, glob))
             return NULL;
 
@@ -5009,27 +5012,30 @@ NewGlobalObject(JSContext *cx, Compartme
         if (!JS_DefineProperty(cx, glob, "custom", JSVAL_VOID, its_getter,
                                its_setter, 0))
             return NULL;
         if (!JS_DefineProperty(cx, glob, "customRdOnly", JSVAL_VOID, its_getter,
                                its_setter, JSPROP_READONLY))
             return NULL;
     }
 
-    if (compartment == NEW_COMPARTMENT && !JS_WrapObject(cx, &glob))
+    if (compartment == NEW_COMPARTMENT && !JS_WrapObject(cx, glob.address()))
         return NULL;
 
     return glob;
 }
 
 static bool
 BindScriptArgs(JSContext *cx, JSObject *obj, OptionParser *op)
 {
+    RootObject root(cx, &obj);
+
     MultiStringRange msr = op->getMultiStringArg("scriptArgs");
-    JSObject *scriptArgs = JS_NewArrayObject(cx, 0, NULL);
+    RootedVarObject scriptArgs(cx);
+    scriptArgs = JS_NewArrayObject(cx, 0, NULL);
     if (!scriptArgs)
         return false;
 
     /* 
      * Script arguments are bound as a normal |arguments| property on the
      * global object. It has no special significance, like |arguments| in
      * function scope does -- this identifier is used de-facto across shell
      * implementations, see bug 675269.
@@ -5048,16 +5054,18 @@ BindScriptArgs(JSContext *cx, JSObject *
     }
 
     return true;
 }
 
 static int
 ProcessArgs(JSContext *cx, JSObject *obj, OptionParser *op)
 {
+    RootObject root(cx, &obj);
+
     if (op->getBoolOption('a'))
         JS_ToggleOptions(cx, JSOPTION_METHODJIT_ALWAYS);
 
     if (op->getBoolOption('m')) {
         enableMethodJit = true;
         JS_ToggleOptions(cx, JSOPTION_METHODJIT);
     }
 
@@ -5129,17 +5137,18 @@ Shell(JSContext *cx, OptionParser *op, c
      * First check to see if type inference is enabled. This flag must be set
      * on the compartment when it is constructed.
      */
     if (op->getBoolOption('n')) {
         enableTypeInference = !enableTypeInference;
         JS_ToggleOptions(cx, JSOPTION_TYPE_INFERENCE);
     }
 
-    JSObject *glob = NewGlobalObject(cx, NEW_COMPARTMENT);
+    RootedVarObject glob(cx);
+    glob = NewGlobalObject(cx, NEW_COMPARTMENT);
     if (!glob)
         return 1;
 
     JSAutoEnterCompartment ac;
     if (!ac.enter(cx, glob))
         return 1;
 
     JS_SetGlobalObject(cx, glob);
--- a/js/src/vm/CallObject.cpp
+++ b/js/src/vm/CallObject.cpp
@@ -52,25 +52,30 @@ namespace js {
  * must be null.
  */
 CallObject *
 CallObject::create(JSContext *cx, JSScript *script, JSObject &scopeChain, JSObject *callee)
 {
     Bindings &bindings = script->bindings;
     gc::AllocKind kind = gc::GetGCObjectKind(bindings.lastShape()->numFixedSlots() + 1);
 
-    js::types::TypeObject *type = cx->compartment->getEmptyType(cx);
+    RootedVarTypeObject type(cx);
+
+    type = cx->compartment->getEmptyType(cx);
     if (!type)
         return NULL;
 
     HeapValue *slots;
     if (!PreallocateObjectDynamicSlots(cx, bindings.lastShape(), &slots))
         return NULL;
 
-    JSObject *obj = JSObject::create(cx, kind, bindings.lastShape(), type, slots);
+    RootedVarShape shape(cx);
+    shape = bindings.lastShape();
+
+    JSObject *obj = JSObject::create(cx, kind, shape, type, slots);
     if (!obj)
         return NULL;
 
     /*
      * Update the parent for bindings associated with non-compileAndGo scripts,
      * whose call objects do not have a consistent global variable and need
      * to be updated dynamically.
      */
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2631,17 +2631,18 @@ DebuggerFrame_getArguments(JSContext *cx
         JS_ASSERT(argumentsv.isObjectOrNull());
         args.rval() = argumentsv;
         return true;
     }
 
     JSObject *argsobj;
     if (fp->hasArgs()) {
         /* Create an arguments object. */
-        GlobalObject *global = args.callee().getGlobal();
+        RootedVar<GlobalObject*> global(cx);
+        global = args.callee().getGlobal();
         JSObject *proto;
         if (!js_GetClassPrototype(cx, global, JSProto_Array, &proto))
             return false;
         argsobj = NewObjectWithGivenProto(cx, &DebuggerArguments_class, proto, global);
         if (!argsobj ||
             !js_SetReservedSlot(cx, argsobj, JSSLOT_DEBUGARGUMENTS_FRAME, ObjectValue(*thisobj)))
         {
             return false;
@@ -3785,45 +3786,55 @@ static JSFunctionSpec DebuggerEnv_method
 
 
 
 /*** Glue ****************************************************************************************/
 
 extern JS_PUBLIC_API(JSBool)
 JS_DefineDebuggerObject(JSContext *cx, JSObject *obj)
 {
-    JSObject *objProto;
-    if (!js_GetClassPrototype(cx, obj, JSProto_Object, &objProto))
+    RootObject objRoot(cx, &obj);
+
+    RootedVarObject
+        objProto(cx),
+        debugCtor(cx),
+        debugProto(cx),
+        frameProto(cx),
+        scriptProto(cx),
+        objectProto(cx);
+
+    if (!js_GetClassPrototype(cx, obj, JSProto_Object, objProto.address()))
         return false;
 
-    JSObject *debugCtor;
-    JSObject *debugProto = js_InitClass(cx, obj, objProto, &Debugger::jsclass, Debugger::construct,
-                                        1, Debugger::properties, Debugger::methods, NULL, NULL,
-                                        &debugCtor);
+
+    debugProto = js_InitClass(cx, objRoot,
+                              objProto, &Debugger::jsclass, Debugger::construct,
+                              1, Debugger::properties, Debugger::methods, NULL, NULL,
+                              debugCtor.address());
     if (!debugProto)
         return false;
 
-    JSObject *frameProto = js_InitClass(cx, debugCtor, objProto, &DebuggerFrame_class,
-                                        DebuggerFrame_construct, 0,
-                                        DebuggerFrame_properties, DebuggerFrame_methods,
-                                        NULL, NULL);
+    frameProto = js_InitClass(cx, debugCtor, objProto, &DebuggerFrame_class,
+                              DebuggerFrame_construct, 0,
+                              DebuggerFrame_properties, DebuggerFrame_methods,
+                              NULL, NULL);
     if (!frameProto)
         return false;
 
-    JSObject *scriptProto = js_InitClass(cx, debugCtor, objProto, &DebuggerScript_class,
-                                         DebuggerScript_construct, 0,
-                                         DebuggerScript_properties, DebuggerScript_methods,
-                                         NULL, NULL);
+    scriptProto = js_InitClass(cx, debugCtor, objProto, &DebuggerScript_class,
+                               DebuggerScript_construct, 0,
+                               DebuggerScript_properties, DebuggerScript_methods,
+                               NULL, NULL);
     if (!scriptProto)
         return false;
 
-    JSObject *objectProto = js_InitClass(cx, debugCtor, objProto, &DebuggerObject_class,
-                                         DebuggerObject_construct, 0,
-                                         DebuggerObject_properties, DebuggerObject_methods,
-                                         NULL, NULL);
+    objectProto = js_InitClass(cx, debugCtor, objProto, &DebuggerObject_class,
+                               DebuggerObject_construct, 0,
+                               DebuggerObject_properties, DebuggerObject_methods,
+                               NULL, NULL);
     if (!objectProto)
         return false;
 
     JSObject *envProto = js_InitClass(cx, debugCtor, objProto, &DebuggerEnv_class,
                                       DebuggerEnv_construct, 0,
                                       DebuggerEnv_properties, DebuggerEnv_methods,
                                       NULL, NULL);
     if (!envProto)
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -79,209 +79,217 @@ ThrowTypeError(JSContext *cx, uintN argc
     return false;
 }
 
 namespace js {
 
 JSObject *
 GlobalObject::initFunctionAndObjectClasses(JSContext *cx)
 {
+    RootedVar<GlobalObject*> self(cx, this);
+
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     JS_ASSERT(isNative());
 
     /*
      * Calling a function from a cleared global triggers this (yeah, I know).
      * Uncomment this once bug 470510 is fixed (if that bug doesn't remove
      * isCleared entirely).
      */
     // JS_ASSERT(!isCleared());
 
     /* If cx has no global object, make this the global object. */
     if (!cx->globalObject)
-        JS_SetGlobalObject(cx, this);
+        JS_SetGlobalObject(cx, self);
+
+    RootedVarObject objectProto(cx);
 
     /*
      * Create |Object.prototype| first, mirroring CreateBlankProto but for the
      * prototype of the created object.
      */
-    JSObject *objectProto = NewObjectWithGivenProto(cx, &ObjectClass, NULL, this);
+    objectProto = NewObjectWithGivenProto(cx, &ObjectClass, NULL, self);
     if (!objectProto || !objectProto->setSingletonType(cx))
         return NULL;
 
     /*
      * The default 'new' type of Object.prototype is required by type inference
      * to have unknown properties, to simplify handling of e.g. heterogenous
      * objects in JSON and script literals.
      */
     if (!objectProto->setNewTypeUnknown(cx))
         return NULL;
 
     /* Create |Function.prototype| next so we can create other functions. */
-    JSFunction *functionProto;
+    RootedVarFunction functionProto(cx);
     {
-        JSObject *proto = NewObjectWithGivenProto(cx, &FunctionClass, objectProto, this);
-        if (!proto)
+        JSObject *functionProto_ = NewObjectWithGivenProto(cx, &FunctionClass, objectProto, self);
+        if (!functionProto_)
             return NULL;
+        functionProto = functionProto_->toFunction();
 
         /*
          * Bizarrely, |Function.prototype| must be an interpreted function, so
          * give it the guts to be one.
          */
-        functionProto = js_NewFunction(cx, proto, NULL, 0, JSFUN_INTERPRETED, this, NULL);
-        if (!functionProto)
+        JSObject *proto = js_NewFunction(cx, functionProto,
+                                         NULL, 0, JSFUN_INTERPRETED, self, NULL);
+        if (!proto)
             return NULL;
         JS_ASSERT(proto == functionProto);
         functionProto->flags |= JSFUN_PROTOTYPE;
 
         JSScript *script =
             JSScript::NewScript(cx, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, JSVERSION_DEFAULT);
         if (!script)
             return NULL;
         script->noScriptRval = true;
         script->code[0] = JSOP_STOP;
         script->code[1] = SRC_NULL;
         functionProto->initScript(script);
         functionProto->getType(cx)->interpretedFunction = functionProto;
         script->setFunction(functionProto);
 
-        if (!proto->setSingletonType(cx))
+        if (!functionProto->setSingletonType(cx))
             return NULL;
 
         /*
          * The default 'new' type of Function.prototype is required by type
          * inference to have unknown properties, to simplify handling of e.g.
          * CloneFunctionObject.
          */
-        if (!proto->setNewTypeUnknown(cx))
+        if (!functionProto->setNewTypeUnknown(cx))
             return NULL;
     }
 
     /* Create the Object function now that we have a [[Prototype]] for it. */
-    jsid objectId = ATOM_TO_JSID(CLASS_ATOM(cx, Object));
-    JSFunction *objectCtor;
+    RootedVarFunction objectCtor(cx);
     {
-        JSObject *ctor = NewObjectWithGivenProto(cx, &FunctionClass, functionProto, this);
+        JSObject *ctor = NewObjectWithGivenProto(cx, &FunctionClass, functionProto, self);
         if (!ctor)
             return NULL;
-        objectCtor = js_NewFunction(cx, ctor, js_Object, 1, JSFUN_CONSTRUCTOR, this,
-                                    JSID_TO_ATOM(objectId));
+        objectCtor = js_NewFunction(cx, ctor, js_Object, 1, JSFUN_CONSTRUCTOR, self,
+                                    CLASS_ATOM(cx, Object));
         if (!objectCtor)
             return NULL;
-        JS_ASSERT(ctor == objectCtor);
 
         objectCtor->setConstructorClass(&ObjectClass);
     }
 
     /*
      * Install |Object| and |Object.prototype| for the benefit of subsequent
      * code that looks for them.
      */
-    setObjectClassDetails(objectCtor, objectProto);
+    self->setObjectClassDetails(objectCtor, objectProto);
 
     /* Create |Function| so it and |Function.prototype| can be installed. */
-    jsid functionId = ATOM_TO_JSID(CLASS_ATOM(cx, Function));
-    JSFunction *functionCtor;
+    RootedVarFunction functionCtor(cx);
     {
         JSObject *ctor =
-            NewObjectWithGivenProto(cx, &FunctionClass, functionProto, this);
+            NewObjectWithGivenProto(cx, &FunctionClass, functionProto, self);
         if (!ctor)
             return NULL;
-        functionCtor = js_NewFunction(cx, ctor, Function, 1, JSFUN_CONSTRUCTOR, this,
-                                      JSID_TO_ATOM(functionId));
+        functionCtor = js_NewFunction(cx, ctor, Function, 1, JSFUN_CONSTRUCTOR, self,
+                                      CLASS_ATOM(cx, Function));
         if (!functionCtor)
             return NULL;
         JS_ASSERT(ctor == functionCtor);
 
         functionCtor->setConstructorClass(&FunctionClass);
     }
 
     /*
      * Install |Function| and |Function.prototype| so that we can freely create
      * functions and objects without special effort.
      */
-    setFunctionClassDetails(functionCtor, functionProto);
+    self->setFunctionClassDetails(functionCtor, functionProto);
 
     /*
      * The hard part's done: now go back and add all the properties these
      * primordial values have.
      */
     if (!LinkConstructorAndPrototype(cx, objectCtor, objectProto) ||
         !DefinePropertiesAndBrand(cx, objectProto, object_props, object_methods) ||
         !DefinePropertiesAndBrand(cx, objectCtor, NULL, object_static_methods) ||
         !LinkConstructorAndPrototype(cx, functionCtor, functionProto) ||
         !DefinePropertiesAndBrand(cx, functionProto, NULL, function_methods) ||
         !DefinePropertiesAndBrand(cx, functionCtor, NULL, NULL))
     {
         return NULL;
     }
 
     /* Add the global Function and Object properties now. */
-    if (!addDataProperty(cx, objectId, JSProto_Object + JSProto_LIMIT * 2, 0))
+    jsid objectId = ATOM_TO_JSID(CLASS_ATOM(cx, Object));
+    if (!self->addDataProperty(cx, objectId, JSProto_Object + JSProto_LIMIT * 2, 0))
         return NULL;
-    if (!addDataProperty(cx, functionId, JSProto_Function + JSProto_LIMIT * 2, 0))
+    jsid functionId = ATOM_TO_JSID(CLASS_ATOM(cx, Function));
+    if (!self->addDataProperty(cx, functionId, JSProto_Function + JSProto_LIMIT * 2, 0))
         return NULL;
 
     /* Heavy lifting done, but lingering tasks remain. */
 
     /* ES5 15.1.2.1. */
     jsid id = ATOM_TO_JSID(cx->runtime->atomState.evalAtom);
-    JSObject *evalobj = js_DefineFunction(cx, this, id, eval, 1, JSFUN_STUB_GSOPS);
+    JSObject *evalobj = js_DefineFunction(cx, self, id, eval, 1, JSFUN_STUB_GSOPS);
     if (!evalobj)
         return NULL;
-    setOriginalEval(evalobj);
+    self->setOriginalEval(evalobj);
 
     /* ES5 13.2.3: Construct the unique [[ThrowTypeError]] function object. */
-    JSFunction *throwTypeError = js_NewFunction(cx, NULL, ThrowTypeError, 0, 0, this, NULL);
+    RootedVarFunction throwTypeError(cx);
+    throwTypeError = js_NewFunction(cx, NULL, ThrowTypeError, 0, 0, self, NULL);
     if (!throwTypeError)
         return NULL;
     AutoIdVector ids(cx);
     if (!throwTypeError->preventExtensions(cx, &ids))
         return NULL;
-    setThrowTypeError(throwTypeError);
+    self->setThrowTypeError(throwTypeError);
 
     /*
      * The global object should have |Object.prototype| as its [[Prototype]].
      * Eventually we'd like to have standard classes be there from the start,
      * and thus we would know we were always setting what had previously been a
      * null [[Prototype]], but right now some code assumes it can set the
      * [[Prototype]] before standard classes have been initialized.  For now,
      * only set the [[Prototype]] if it hasn't already been set.
      */
-    if (shouldSplicePrototype(cx) && !splicePrototype(cx, objectProto))
+    if (self->shouldSplicePrototype(cx) && !self->splicePrototype(cx, objectProto))
         return NULL;
 
     /*
      * Notify any debuggers about the creation of the script for
      * |Function.prototype| -- after all initialization, for simplicity.
      */
     js_CallNewScriptHook(cx, functionProto->script(), functionProto);
     return functionProto;
 }
 
 GlobalObject *
 GlobalObject::create(JSContext *cx, Class *clasp)
 {
     JS_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL);
 
-    JSObject *obj = NewObjectWithGivenProto(cx, clasp, NULL, NULL);
-    if (!obj || !obj->setSingletonType(cx))
+    RootedVar<GlobalObject*> obj(cx);
+
+    JSObject *obj_ = NewObjectWithGivenProto(cx, clasp, NULL, NULL);
+    if (!obj_)
         return NULL;
+    obj = obj_->asGlobal();
 
-    GlobalObject *globalObj = obj->asGlobal();
-    if (!globalObj->setVarObj(cx))
+    if (!obj->setSingletonType(cx) || !obj->setVarObj(cx))
         return NULL;
 
     /* Construct a regexp statics object for this global object. */
-    JSObject *res = RegExpStatics::create(cx, globalObj);
+    JSObject *res = RegExpStatics::create(cx, obj);
     if (!res)
         return NULL;
-    globalObj->initSlot(REGEXP_STATICS, ObjectValue(*res));
-    globalObj->initFlags(0);
+    obj->initSlot(REGEXP_STATICS, ObjectValue(*res));
+    obj->initFlags(0);
 
-    return globalObj;
+    return obj;
 }
 
 bool
 GlobalObject::initStandardClasses(JSContext *cx)
 {
     JSAtomState &state = cx->runtime->atomState;
 
     /* Define a top-level property 'undefined' with the undefined value. */
@@ -366,17 +374,20 @@ GlobalObject::isRuntimeCodeGenEnabled(JS
     }
     return !v.isFalse();
 }
 
 JSFunction *
 GlobalObject::createConstructor(JSContext *cx, Native ctor, Class *clasp, JSAtom *name,
                                 uintN length, gc::AllocKind kind)
 {
-    JSFunction *fun = js_NewFunction(cx, NULL, ctor, length, JSFUN_CONSTRUCTOR, this, name, kind);
+    RootedVarObject self(cx, this);
+
+    JSFunction *fun = js_NewFunction(cx, NULL, ctor, length,
+                                     JSFUN_CONSTRUCTOR, self, name, kind);
     if (!fun)
         return NULL;
 
     /*
      * Remember the class this function is a constructor for so that we know to
      * create an object of this class when we call the constructor.
      */
     fun->setConstructorClass(clasp);
@@ -410,26 +421,31 @@ JSObject *
 GlobalObject::createBlankPrototypeInheriting(JSContext *cx, Class *clasp, JSObject &proto)
 {
     return CreateBlankProto(cx, clasp, proto, *this);
 }
 
 bool
 LinkConstructorAndPrototype(JSContext *cx, JSObject *ctor, JSObject *proto)
 {
+    RootObject ctorRoot(cx, &ctor);
+    RootObject protoRoot(cx, &proto);
+
     return ctor->defineProperty(cx, cx->runtime->atomState.classPrototypeAtom,
                                 ObjectValue(*proto), JS_PropertyStub, JS_StrictPropertyStub,
                                 JSPROP_PERMANENT | JSPROP_READONLY) &&
            proto->defineProperty(cx, cx->runtime->atomState.constructorAtom,
                                  ObjectValue(*ctor), JS_PropertyStub, JS_StrictPropertyStub, 0);
 }
 
 bool
 DefinePropertiesAndBrand(JSContext *cx, JSObject *obj, JSPropertySpec *ps, JSFunctionSpec *fs)
 {
+    RootObject root(cx, &obj);
+
     if ((ps && !JS_DefineProperties(cx, obj, ps)) || (fs && !JS_DefineFunctions(cx, obj, fs)))
         return false;
     return true;
 }
 
 void
 GlobalDebuggees_finalize(JSContext *cx, JSObject *obj)
 {
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -210,84 +210,104 @@ class GlobalObject : public ::JSObject {
 
     /*
      * Identical to createBlankPrototype, but uses proto as the [[Prototype]]
      * of the returned blank prototype.
      */
     JSObject *createBlankPrototypeInheriting(JSContext *cx, js::Class *clasp, JSObject &proto);
 
     JSObject *getOrCreateObjectPrototype(JSContext *cx) {
+        GlobalObject *self = this;
         if (!functionObjectClassesInitialized()) {
+            Root<GlobalObject*> root(cx, &self);
             if (!initFunctionAndObjectClasses(cx))
                 return NULL;
         }
-        return &getPrototype(JSProto_Object).toObject();
+        return &self->getPrototype(JSProto_Object).toObject();
     }
 
     JSObject *getOrCreateFunctionPrototype(JSContext *cx) {
+        GlobalObject *self = this;
         if (!functionObjectClassesInitialized()) {
+            Root<GlobalObject*> root(cx, &self);
             if (!initFunctionAndObjectClasses(cx))
                 return NULL;
         }
-        return &getPrototype(JSProto_Function).toObject();
+        return &self->getPrototype(JSProto_Function).toObject();
     }
 
     JSObject *getOrCreateArrayPrototype(JSContext *cx) {
+        GlobalObject *self = this;
         if (!arrayClassInitialized()) {
+            Root<GlobalObject*> root(cx, &self);
             if (!js_InitArrayClass(cx, this))
                 return NULL;
         }
-        return &getPrototype(JSProto_Array).toObject();
+        return &self->getPrototype(JSProto_Array).toObject();
     }
 
     JSObject *getOrCreateBooleanPrototype(JSContext *cx) {
+        GlobalObject *self = this;
         if (!booleanClassInitialized()) {
+            Root<GlobalObject*> root(cx, &self);
             if (!js_InitBooleanClass(cx, this))
                 return NULL;
         }
-        return &getPrototype(JSProto_Boolean).toObject();
+        return &self->getPrototype(JSProto_Boolean).toObject();
     }
 
     JSObject *getOrCreateNumberPrototype(JSContext *cx) {
+        GlobalObject *self = this;
         if (!numberClassInitialized()) {
+            Root<GlobalObject*> root(cx, &self);
             if (!js_InitNumberClass(cx, this))
                 return NULL;
         }
-        return &getPrototype(JSProto_Number).toObject();
+        return &self->getPrototype(JSProto_Number).toObject();
     }
 
     JSObject *getOrCreateStringPrototype(JSContext *cx) {
+        GlobalObject *self = this;
         if (!stringClassInitialized()) {
+            Root<GlobalObject*> root(cx, &self);
             if (!js_InitStringClass(cx, this))
                 return NULL;
         }
-        return &getPrototype(JSProto_String).toObject();
+        return &self->getPrototype(JSProto_String).toObject();
     }
 
     JSObject *getOrCreateRegExpPrototype(JSContext *cx) {
+        GlobalObject *self = this;
         if (!regexpClassInitialized()) {
+            Root<GlobalObject*> root(cx, &self);
             if (!js_InitRegExpClass(cx, this))
                 return NULL;
         }
-        return &getPrototype(JSProto_RegExp).toObject();
+        return &self->getPrototype(JSProto_RegExp).toObject();
     }
 
     JSObject *getOrCreateArrayBufferPrototype(JSContext *cx) {
+        GlobalObject *self = this;
         if (!arrayBufferClassInitialized()) {
+            Root<GlobalObject*> root(cx, &self);
             if (!js_InitTypedArrayClasses(cx, this))
                 return NULL;
         }
-        return &getPrototype(JSProto_ArrayBuffer).toObject();
+        return &self->getPrototype(JSProto_ArrayBuffer).toObject();
     }
 
     JSObject *getOrCreateGeneratorPrototype(JSContext *cx) {
-        HeapValue &v = getSlotRef(GENERATOR_PROTO);
-        if (!v.isObject() && !js_InitIteratorClasses(cx, this))
-            return NULL;
-        return &v.toObject();
+        GlobalObject *self = this;
+        Value v = getSlotRef(GENERATOR_PROTO);
+        if (!v.isObject()) {
+            Root<GlobalObject*> root(cx, &self);
+            if (!js_InitIteratorClasses(cx, this))
+                return NULL;
+        }
+        return &self->getSlot(GENERATOR_PROTO).toObject();
     }
 
     inline RegExpStatics *getRegExpStatics() const;
 
     JSObject *getThrowTypeError() const {
         JS_ASSERT(functionObjectClassesInitialized());
         return &getSlot(THROWTYPEERROR).toObject();
     }
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -417,16 +417,18 @@ class JSString : public js::gc::Cell
 
     static size_t offsetOfChars() {
         return offsetof(JSString, d.u1.chars);
     }
 
     static inline void writeBarrierPre(JSString *str);
     static inline void writeBarrierPost(JSString *str, void *addr);
     static inline bool needWriteBarrierPre(JSCompartment *comp);
+
+    static inline js::ThingRootKind rootKind() { return js::THING_ROOT_STRING; }
 };
 
 class JSRope : public JSString
 {
     enum UsingBarrier { WithIncrementalBarrier, NoBarrier };
     template<UsingBarrier b>
     JSFlatString *flattenInternal(JSContext *cx);