Add dynamic analysis for identifying moving GC hazards, autorooter classes, some VM rooters, bug 707049. r=billm
☠☠ backed out by 4282a285d381 ☠ ☠
authorBrian Hackett <bhackett1024@gmail.com>
Fri, 30 Dec 2011 20:12:45 -0700
changeset 86046 0d642e4e08cf26daa6edcc91ad22a435c40176d6
parent 86045 7e94dbbde863cbef3f6981989e8dc9b7ac0fd2b1
child 86047 6dc6795b4479cdd213ffaa00b56b6f584ac56835
push id674
push userffxbld
push dateTue, 13 Mar 2012 21:17:50 +0000
treeherdermozilla-beta@e3c4c92dec31 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm
bugs707049
milestone12.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Add dynamic analysis for identifying moving GC hazards, autorooter classes, some VM rooters, bug 707049. r=billm
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);