Get rooting analysis to pass jit-tests in the interpreter, bug 745742. r=billm
authorBrian Hackett <bhackett1024@gmail.com>
Mon, 30 Apr 2012 17:10:30 -0700
changeset 97353 07a4d4b0260ce84432b35d514176d30262cbe992
parent 97352 46e22a07d53a3d97d3c709f935b7821fd846451f
child 97354 f9cc708c97280f101d35de1819f4d778af9331fb
push id173
push userlsblakk@mozilla.com
push dateFri, 24 Aug 2012 15:39:16 +0000
treeherdermozilla-release@bcc45eb1fb41 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm
bugs745742
milestone15.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
Get rooting analysis to pass jit-tests in the interpreter, bug 745742. r=billm
js/src/builtin/MapObject.cpp
js/src/builtin/MapObject.h
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/Parser.cpp
js/src/frontend/TokenStream.cpp
js/src/frontend/TokenStream.h
js/src/gc/Root.h
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarray.cpp
js/src/jsarray.h
js/src/jsatom.cpp
js/src/jsatominlines.h
js/src/jsclass.h
js/src/jsclone.cpp
js/src/jscntxt.h
js/src/jscntxtinlines.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsdate.cpp
js/src/jsdbgapi.cpp
js/src/jsexn.cpp
js/src/jsexn.h
js/src/jsfriendapi.cpp
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsfuninlines.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsgcinlines.h
js/src/jsinfer.cpp
js/src/jsinterp.cpp
js/src/jsinterpinlines.h
js/src/jsiter.cpp
js/src/jsiter.h
js/src/jsnum.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/json.cpp
js/src/jsonparser.h
js/src/jsopcode.cpp
js/src/jsproxy.cpp
js/src/jspubtd.h
js/src/jsreflect.cpp
js/src/jsscope.cpp
js/src/jsscope.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsstr.cpp
js/src/jstypedarray.cpp
js/src/jswatchpoint.cpp
js/src/jswatchpoint.h
js/src/jsweakmap.cpp
js/src/jswrapper.cpp
js/src/jsxml.cpp
js/src/methodjit/MonoIC.cpp
js/src/methodjit/PolyIC.cpp
js/src/methodjit/StubCalls.cpp
js/src/perf/jsperf.cpp
js/src/shell/js.cpp
js/src/shell/jsheaptools.cpp
js/src/vm/ArgumentsObject.cpp
js/src/vm/Debugger.cpp
js/src/vm/Debugger.h
js/src/vm/GlobalObject.cpp
js/src/vm/GlobalObject.h
js/src/vm/ObjectImpl.h
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpStatics.h
js/src/vm/ScopeObject.cpp
js/src/vm/ScopeObject.h
js/src/vm/Stack-inl.h
js/src/vm/String-inl.h
js/src/vm/StringObject-inl.h
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -51,26 +51,26 @@
 #include "vm/MethodGuard.h"
 #include "vm/Stack.h"
 
 #include "jsobjinlines.h"
 
 using namespace js;
 
 static JSObject *
-InitClass(JSContext *cx, GlobalObject *global, Class *clasp, JSProtoKey key, Native construct,
+InitClass(JSContext *cx, Handle<GlobalObject*> global, Class *clasp, JSProtoKey key, Native construct,
           JSFunctionSpec *methods)
 {
-    JSObject *proto = global->createBlankPrototype(cx, clasp);
+    RootedVarObject proto(cx, global->createBlankPrototype(cx, clasp));
     if (!proto)
         return NULL;
     proto->setPrivate(NULL);
 
     JSAtom *atom = cx->runtime->atomState.classAtoms[key];
-    JSFunction *ctor = global->createConstructor(cx, construct, atom, 1);
+    RootedVarFunction ctor(cx, global->createConstructor(cx, construct, atom, 1));
     if (!ctor ||
         !LinkConstructorAndPrototype(cx, ctor, proto) ||
         !DefinePropertiesAndBrand(cx, proto, NULL, methods) ||
         !DefineConstructorAndPrototype(cx, global, key, ctor, proto))
     {
         return NULL;
     }
     return proto;
@@ -174,17 +174,18 @@ JSFunctionSpec MapObject::methods[] = {
     JS_FN("set", set, 2, 0),
     JS_FN("delete", delete_, 1, 0),
     JS_FS_END
 };
 
 JSObject *
 MapObject::initClass(JSContext *cx, JSObject *obj)
 {
-    return InitClass(cx, &obj->asGlobal(), &class_, JSProto_Map, construct, methods);
+    return InitClass(cx, RootedVar<GlobalObject*>(cx, &obj->asGlobal()),
+                     &class_, JSProto_Map, construct, methods);
 }
 
 void
 MapObject::mark(JSTracer *trc, JSObject *obj)
 {
     MapObject *mapobj = static_cast<MapObject *>(obj);
     if (ValueMap *map = mapobj->getData()) {
         for (ValueMap::Range r = map->all(); !r.empty(); r.popFront()) {
@@ -219,32 +220,34 @@ class AddToMap {
 
         Value key;
         if (!pairobj->getElement(cx, 0, &key))
             return false;
         HashableValue hkey;
         if (!hkey.setValue(cx, key))
             return false;
 
+        HashableValue::StackRoot hkeyRoot(cx, &hkey);
+
         Value val;
         if (!pairobj->getElement(cx, 1, &val))
             return false;
 
         if (!map->put(hkey, val)) {
             js_ReportOutOfMemory(cx);
             return false;
         }
         return true;
     }
 };
 
 JSBool
 MapObject::construct(JSContext *cx, unsigned argc, Value *vp)
 {
-    JSObject *obj = NewBuiltinClassInstance(cx, &class_);
+    RootedVarObject obj(cx, NewBuiltinClassInstance(cx, &class_));
     if (!obj)
         return false;
 
     ValueMap *map = cx->new_<ValueMap>(cx->runtime);
     if (!map)
         return false;
     if (!map->init()) {
         js_ReportOutOfMemory(cx);
@@ -376,17 +379,18 @@ JSFunctionSpec SetObject::methods[] = {
     JS_FN("add", add, 1, 0),
     JS_FN("delete", delete_, 1, 0),
     JS_FS_END
 };
 
 JSObject *
 SetObject::initClass(JSContext *cx, JSObject *obj)
 {
-    return InitClass(cx, &obj->asGlobal(), &class_, JSProto_Set, construct, methods);
+    return InitClass(cx, RootedVar<GlobalObject*>(cx, &obj->asGlobal()),
+                     &class_, JSProto_Set, construct, methods);
 }
 
 void
 SetObject::mark(JSTracer *trc, JSObject *obj)
 {
     SetObject *setobj = static_cast<SetObject *>(obj);
     if (ValueSet *set = setobj->getData()) {
         for (ValueSet::Range r = set->all(); !r.empty(); r.popFront()) {
@@ -423,17 +427,17 @@ class AddToSet {
         }
         return true;
     }
 };
 
 JSBool
 SetObject::construct(JSContext *cx, unsigned argc, Value *vp)
 {
-    JSObject *obj = NewBuiltinClassInstance(cx, &class_);
+    RootedVarObject obj(cx, NewBuiltinClassInstance(cx, &class_));
     if (!obj)
         return false;
 
     ValueSet *set = cx->new_<ValueSet>(cx->runtime);
     if (!set)
         return false;
     if (!set->init()) {
         js_ReportOutOfMemory(cx);
--- a/js/src/builtin/MapObject.h
+++ b/js/src/builtin/MapObject.h
@@ -68,16 +68,21 @@ class HashableValue {
     };
 
     HashableValue() : value(UndefinedValue()) {}
 
     operator const HeapValue &() const { return value; }
     bool setValue(JSContext *cx, const Value &v);
     HashNumber hash() const;
     bool equals(const HashableValue &other) const;
+
+    struct StackRoot {
+        StackRoot(JSContext *cx, HashableValue *pv) : valueRoot(cx, (Value*) &pv->value) {}
+        RootValue valueRoot;
+    };
 };
 
 typedef HashMap<HashableValue, HeapValue, HashableValue::Hasher, RuntimeAllocPolicy> ValueMap;
 typedef HashSet<HashableValue, HashableValue::Hasher, RuntimeAllocPolicy> ValueSet;
 
 class MapObject : public JSObject {
   public:
     static JSObject *initClass(JSContext *cx, JSObject *obj);
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -1145,63 +1145,63 @@ AdjustBlockSlot(JSContext *cx, BytecodeE
 
 static bool
 EmitEnterBlock(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSOp op)
 {
     JS_ASSERT(pn->isKind(PNK_LEXICALSCOPE));
     if (!EmitObjectOp(cx, pn->pn_objbox, op, bce))
         return false;
 
-    StaticBlockObject &blockObj = pn->pn_objbox->object->asStaticBlock();
+    RootedVar<StaticBlockObject*> blockObj(cx, &pn->pn_objbox->object->asStaticBlock());
 
     int depth = bce->stackDepth -
-                (blockObj.slotCount() + ((op == JSOP_ENTERLET1) ? 1 : 0));
+                (blockObj->slotCount() + ((op == JSOP_ENTERLET1) ? 1 : 0));
     JS_ASSERT(depth >= 0);
 
-    blockObj.setStackDepth(depth);
+    blockObj->setStackDepth(depth);
 
     int depthPlusFixed = AdjustBlockSlot(cx, bce, depth);
     if (depthPlusFixed < 0)
         return false;
 
-    for (unsigned i = 0; i < blockObj.slotCount(); i++) {
-        Definition *dn = blockObj.maybeDefinitionParseNode(i);
+    for (unsigned i = 0; i < blockObj->slotCount(); i++) {
+        Definition *dn = blockObj->maybeDefinitionParseNode(i);
 
         /* Beware the empty destructuring dummy. */
         if (!dn) {
-            JS_ASSERT(i + 1 <= blockObj.slotCount());
+            JS_ASSERT(i + 1 <= blockObj->slotCount());
             continue;
         }
 
         JS_ASSERT(dn->isDefn());
         JS_ASSERT(unsigned(dn->frameSlot() + depthPlusFixed) < JS_BIT(16));
         dn->pn_cookie.set(dn->pn_cookie.level(), uint16_t(dn->frameSlot() + depthPlusFixed));
 #ifdef DEBUG
         for (ParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
             JS_ASSERT(pnu->pn_lexdef == dn);
             JS_ASSERT(!(pnu->pn_dflags & PND_BOUND));
             JS_ASSERT(pnu->pn_cookie.isFree());
         }
 #endif
 
         bool aliased = bce->bindingsAccessedDynamically() || bce->shouldNoteClosedName(dn);
-        blockObj.setAliased(i, aliased);
+        blockObj->setAliased(i, aliased);
     }
 
     /*
      * 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()) {
-        Shape *newShape = Shape::setExtensibleParents(cx, blockObj.lastProperty());
+        Shape *newShape = Shape::setExtensibleParents(cx, blockObj->lastProperty());
         if (!newShape)
             return false;
-        blockObj.setLastPropertyInfallible(newShape);
+        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.
@@ -3933,17 +3933,17 @@ ParseNode::getConstantValue(JSContext *c
         vp->setBoolean(false);
         return true;
       case PNK_NULL:
         vp->setNull();
         return true;
       case PNK_RB: {
         JS_ASSERT(isOp(JSOP_NEWINIT) && !(pn_xflags & PNX_NONCONST));
 
-        JSObject *obj = NewDenseAllocatedArray(cx, pn_count);
+        RootedVarObject obj(cx, NewDenseAllocatedArray(cx, pn_count));
         if (!obj)
             return false;
 
         unsigned idx = 0;
         for (ParseNode *pn = pn_head; pn; idx++, pn = pn->pn_next) {
             Value value;
             if (!pn->getConstantValue(cx, strictChecks, &value))
                 return false;
@@ -4483,38 +4483,38 @@ EmitIf(JSContext *cx, BytecodeEmitter *b
 MOZ_NEVER_INLINE static bool
 EmitLet(JSContext *cx, BytecodeEmitter *bce, ParseNode *pnLet)
 {
     JS_ASSERT(pnLet->isArity(PN_BINARY));
     ParseNode *varList = pnLet->pn_left;
     JS_ASSERT(varList->isArity(PN_LIST));
     ParseNode *letBody = pnLet->pn_right;
     JS_ASSERT(letBody->isLet() && letBody->isKind(PNK_LEXICALSCOPE));
-    StaticBlockObject &blockObj = letBody->pn_objbox->object->asStaticBlock();
+    RootedVar<StaticBlockObject*> blockObj(cx, &letBody->pn_objbox->object->asStaticBlock());
 
     ptrdiff_t letHeadOffset = bce->offset();
     int letHeadDepth = bce->stackDepth;
 
     LetNotes letNotes(cx);
     if (!EmitVariables(cx, bce, varList, PushInitialValues, &letNotes))
         return false;
 
     /* Push storage for hoisted let decls (e.g. 'let (x) { let y }'). */
     uint32_t alreadyPushed = unsigned(bce->stackDepth - letHeadDepth);
-    uint32_t blockObjCount = blockObj.slotCount();
+    uint32_t blockObjCount = blockObj->slotCount();
     for (uint32_t i = alreadyPushed; i < blockObjCount; ++i) {
         /* Tell the decompiler not to print the decl in the let head. */
         if (NewSrcNote(cx, bce, SRC_CONTINUE) < 0)
             return false;
         if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
             return false;
     }
 
     StmtInfo stmtInfo(cx);
-    PushBlockScope(bce, &stmtInfo, blockObj, bce->offset());
+    PushBlockScope(bce, &stmtInfo, *blockObj, bce->offset());
 
     if (!letNotes.update(cx, bce, bce->offset()))
         return false;
 
     ptrdiff_t declNote = NewSrcNote(cx, bce, SRC_DECL);
     if (declNote < 0)
         return false;
 
@@ -4527,17 +4527,17 @@ EmitLet(JSContext *cx, BytecodeEmitter *
 
     JSOp leaveOp = letBody->getOp();
     if (leaveOp == JSOP_LEAVEBLOCKEXPR) {
         if (NewSrcNote2(cx, bce, SRC_PCBASE, bce->offset() - letHeadOffset) < 0)
             return false;
     }
 
     JS_ASSERT(leaveOp == JSOP_LEAVEBLOCK || leaveOp == JSOP_LEAVEBLOCKEXPR);
-    EMIT_UINT16_IMM_OP(leaveOp, blockObj.slotCount());
+    EMIT_UINT16_IMM_OP(leaveOp, blockObj->slotCount());
 
     ptrdiff_t bodyEnd = bce->offset();
     JS_ASSERT(bodyEnd > bodyBegin);
 
     if (!PopStatementBCE(cx, bce))
         return false;
 
     ptrdiff_t o = PackLetData((bodyEnd - bodyBegin) -
@@ -4632,16 +4632,17 @@ MOZ_NEVER_INLINE static bool
 EmitLexicalScope(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     JS_ASSERT(pn->isKind(PNK_LEXICALSCOPE));
     JS_ASSERT(pn->getOp() == JSOP_LEAVEBLOCK);
 
     StmtInfo stmtInfo(cx);
     ObjectBox *objbox = pn->pn_objbox;
     StaticBlockObject &blockObj = objbox->object->asStaticBlock();
+    size_t slots = blockObj.slotCount();
     PushBlockScope(bce, &stmtInfo, blockObj, bce->offset());
 
     /*
      * For compound statements (i.e. { stmt-list }), the decompiler does not
      * emit curlies by default. However, if this stmt-list contains a let
      * declaration, this is semantically invalid so we need to add a srcnote to
      * enterblock to tell the decompiler to add curlies. This condition
      * shouldn't be so complicated; try to find a simpler condition.
@@ -4670,17 +4671,17 @@ EmitLexicalScope(JSContext *cx, Bytecode
     if (!EmitTree(cx, bce, pn->pn_expr))
         return false;
 
     if (noteIndex >= 0) {
         if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 0, bce->offset() - bodyBegin))
             return false;
     }
 
-    EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, blockObj.slotCount());
+    EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, slots);
 
     return PopStatementBCE(cx, bce);
 }
 
 static bool
 EmitWith(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     StmtInfo stmtInfo(cx);
@@ -4705,17 +4706,17 @@ EmitForIn(JSContext *cx, BytecodeEmitter
 
     ParseNode *forHead = pn->pn_left;
     ParseNode *forBody = pn->pn_right;
 
     ParseNode *pn1 = forHead->pn_kid1;
     bool letDecl = pn1 && pn1->isKind(PNK_LEXICALSCOPE);
     JS_ASSERT_IF(letDecl, pn1->isLet());
 
-    StaticBlockObject *blockObj = letDecl ? &pn1->pn_objbox->object->asStaticBlock() : NULL;
+    RootedVar<StaticBlockObject*> blockObj(cx, letDecl ? &pn1->pn_objbox->object->asStaticBlock() : NULL);
     uint32_t blockObjCount = blockObj ? blockObj->slotCount() : 0;
 
     if (letDecl) {
         /*
          * The let's slot(s) will be under the iterator, but the block must not
          * be entered (i.e. fp->blockChain set) until after evaluating the rhs.
          * Thus, push to reserve space and enterblock after. The same argument
          * applies when leaving the loop. Thus, a for-let-in loop looks like:
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -943,29 +943,30 @@ typedef JSBool
 
 static JSBool
 BindLet(JSContext *cx, BindData *data, JSAtom *atom, TreeContext *tc);
 
 static JSBool
 BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, TreeContext *tc);
 
 struct BindData {
-    BindData() : fresh(true) {}
+    BindData(JSContext *cx) : let(cx), fresh(true) {}
 
     ParseNode       *pn;        /* name node for definition processing and
                                    error source coordinates */
     JSOp            op;         /* prolog bytecode or nop */
     Binder          binder;     /* binder, discriminates u */
-    union {
-        struct {
-            VarContext varContext;
-            StaticBlockObject *blockObj;
-            unsigned   overflow;
-        } let;
-    };
+
+    struct LetData {
+        LetData(JSContext *cx) : blockObj(cx) {}
+        VarContext varContext;
+        RootedVar<StaticBlockObject*> blockObj;
+        unsigned   overflow;
+    } let;
+
     bool fresh;
 
     void initLet(VarContext varContext, StaticBlockObject &blockObj, unsigned overflow) {
         this->pn = NULL;
         this->op = JSOP_NOP;
         this->binder = BindLet;
         this->let.varContext = varContext;
         this->let.blockObj = &blockObj;
@@ -1035,20 +1036,20 @@ Parser::newFunction(TreeContext *tc, JSA
      * clear parent and proto.
      */
     while (tc->parent)
         tc = tc->parent;
 
     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);
+    RootedVarFunction fun(context);
+    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;
         if (!fun->clearType(context))
             return NULL;
         fun->setEnvironment(NULL);
     }
     return fun;
@@ -1263,17 +1264,17 @@ LeaveFunction(ParseNode *fn, TreeContext
     }
 
     funbox->bindings.transfer(funtc->parser->context, &funtc->bindings);
 
     return true;
 }
 
 static bool
-DefineGlobal(ParseNode *pn, BytecodeEmitter *bce, PropertyName *name);
+DefineGlobal(ParseNode *pn, BytecodeEmitter *bce, Handle<PropertyName*> name);
 
 /*
  * FIXME? this Parser method was factored from Parser::functionDef with minimal
  * change, hence the funtc ref param and funbox. It probably should match
  * functionBody, etc., and use tc and tc->funbox instead of taking explicit
  * parameters.
  */
 bool
@@ -1303,17 +1304,17 @@ Parser::functionArguments(TreeContext &f
                 destructuringArg = true;
 
                 /*
                  * A destructuring formal parameter turns into one or more
                  * local variables initialized from properties of a single
                  * anonymous positional parameter, so here we must tweak our
                  * binder and its data.
                  */
-                BindData data;
+                BindData data(context);
                 data.pn = NULL;
                 data.op = JSOP_DEFVAR;
                 data.binder = BindDestructuringArg;
                 ParseNode *lhs = destructuringExpr(&data, tt);
                 if (!lhs)
                     return false;
 
                 /*
@@ -2078,25 +2079,24 @@ OuterLet(TreeContext *tc, StmtInfo *stmt
  *
  * For now, don't try to lookup eval frame variables at compile time. This is
  * sub-optimal: we could handle eval-called-from-global-code gvars since eval
  * gets its own script and frame. The eval-from-function-code case is harder,
  * since functions do not atomize gvars and then reserve their atom indexes as
  * stack frame slots.
  */
 static bool
-DefineGlobal(ParseNode *pn, BytecodeEmitter *bce, PropertyName *name)
+DefineGlobal(ParseNode *pn, BytecodeEmitter *bce, Handle<PropertyName*> name)
 {
     GlobalScope *globalScope = bce->globalScope;
     HandleObject globalObj = globalScope->globalObj;
 
     if (!bce->compileAndGo() || !globalObj || bce->compilingForEval())
         return true;
 
-    JS_ASSERT(!IsPoisonedPtr(name));
     AtomIndexAddPtr p = globalScope->names.lookupForAdd(name);
     if (!p) {
         JSContext *cx = bce->parser->context;
 
         JSObject *holder;
         JSProperty *prop;
         if (!globalObj->lookupProperty(cx, name, &holder, &prop))
             return false;
@@ -2124,18 +2124,16 @@ DefineGlobal(ParseNode *pn, BytecodeEmit
             def = GlobalScope::GlobalDef(shape->slot());
         } else {
             def = GlobalScope::GlobalDef(name, funbox);
         }
 
         if (!globalScope->defs.append(def))
             return false;
 
-        JS_ASSERT(!IsPoisonedPtr(name));
-
         jsatomid index = globalScope->names.count();
         if (!globalScope->names.add(p, name, index))
             return false;
 
         JS_ASSERT(index == globalScope->defs.length() - 1);
     } else {
         /*
          * Functions can be redeclared, and the last one takes effect. Check
@@ -2207,17 +2205,18 @@ BindTopLevelVar(JSContext *cx, BindData 
     if (pn->pn_dflags & PND_CONST)
         return true;
 
     /*
      * If this is a global variable, we're compile-and-go, and a global object
      * is present, try to bake in either an already available slot or a
      * predicted slot that will be defined after compiling is completed.
      */
-    return DefineGlobal(pn, tc->asBytecodeEmitter(), pn->pn_atom->asPropertyName());
+    return DefineGlobal(pn, tc->asBytecodeEmitter(),
+                        RootedVarPropertyName(cx, pn->pn_atom->asPropertyName()));
 }
 
 static bool
 BindFunctionLocal(JSContext *cx, BindData *data, MultiDeclRange &mdl, TreeContext *tc)
 {
     JS_ASSERT(tc->inFunction());
 
     ParseNode *pn = data->pn;
@@ -2591,17 +2590,17 @@ CheckDestructuring(JSContext *cx, BindDa
 
     if (left->isKind(PNK_ARRAYCOMP)) {
         ReportCompileErrorNumber(cx, TS(tc->parser), left, JSREPORT_ERROR,
                                  JSMSG_ARRAY_COMP_LEFTSIDE);
         return false;
     }
 
     RootedVar<StaticBlockObject *> blockObj(cx);
-    blockObj = data && data->binder == BindLet ? data->let.blockObj : NULL;
+    blockObj = data && data->binder == BindLet ? data->let.blockObj.reference() : NULL;
     uint32_t blockCountBefore = blockObj ? blockObj->slotCount() : 0;
 
     if (left->isKind(PNK_RB)) {
         for (ParseNode *pn = left->pn_head; pn; pn = pn->pn_next) {
             /* Nullary comma is an elision; binary comma is an expression.*/
             if (!pn->isArrayHole()) {
                 if (pn->isKind(PNK_RB) || pn->isKind(PNK_RC)) {
                     ok = CheckDestructuring(cx, data, pn, tc, false);
@@ -2890,17 +2889,17 @@ ParseNode *
 Parser::letBlock(LetContext letContext)
 {
     JS_ASSERT(tokenStream.currentToken().type == TOK_LET);
 
     ParseNode *pnlet = BinaryNode::create(PNK_LET, tc);
     if (!pnlet)
         return NULL;
 
-    StaticBlockObject *blockObj = StaticBlockObject::create(context);
+    RootedVar<StaticBlockObject*> blockObj(context, StaticBlockObject::create(context));
     if (!blockObj)
         return NULL;
 
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET);
 
     ParseNode *vars = variables(PNK_LET, blockObj, DontHoistVars);
     if (!vars)
         return NULL;
@@ -3180,17 +3179,17 @@ Parser::forStatement()
 
     /*
      * True if we have 'for (var/let/const ...)', except in the oddball case
      * where 'let' begins a let-expression in 'for (let (...) ...)'.
      */
     bool forDecl = false;
 
     /* Non-null when forDecl is true for a 'for (let ...)' statement. */
-    StaticBlockObject *blockObj = NULL;
+    RootedVar<StaticBlockObject*> blockObj(context);
 
     /* Set to 'x' in 'for (x ;... ;...)' or 'for (x in ...)'. */
     ParseNode *pn1;
 
     {
         TokenKind tt = tokenStream.peekToken(TSF_OPERAND);
         if (tt == TOK_SEMI) {
             if (pn->pn_iflags & JSITER_FOREACH) {
@@ -3573,17 +3572,17 @@ Parser::tryStatement()
         catchList = ListNode::create(PNK_CATCHLIST, tc);
         if (!catchList)
             return NULL;
         catchList->makeEmpty();
         lastCatch = NULL;
 
         do {
             ParseNode *pnblock;
-            BindData data;
+            BindData data(context);
 
             /* Check for another catch after unconditional catch. */
             if (lastCatch && !lastCatch->pn_kid2) {
                 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_CATCH_AFTER_GENERAL);
                 return NULL;
             }
 
             /*
@@ -4315,17 +4314,17 @@ Parser::variables(ParseNodeKind kind, St
     pn->setOp(blockObj ? JSOP_NOP : kind == PNK_VAR ? JSOP_DEFVAR : JSOP_DEFCONST);
     pn->makeEmpty();
 
     /*
      * SpiderMonkey const is really "write once per initialization evaluation"
      * var, whereas let is block scoped. ES-Harmony wants block-scoped const so
      * this code will change soon.
      */
-    BindData data;
+    BindData data(context);
     if (blockObj)
         data.initLet(varContext, *blockObj, JSMSG_TOO_MANY_LOCALS);
     else
         data.initVarOrConst(pn->getOp());
 
     ParseNode *pn2;
     do {
         TokenKind tt = tokenStream.getToken();
@@ -5309,17 +5308,17 @@ CompExprTransplanter::transplant(ParseNo
  */
 ParseNode *
 Parser::comprehensionTail(ParseNode *kid, unsigned blockid, bool isGenexp,
                           ParseNodeKind kind, JSOp op)
 {
     unsigned adjust;
     ParseNode *pn, *pn2, *pn3, **pnp;
     StmtInfo stmtInfo(context);
-    BindData data;
+    BindData data(context);
     TokenKind tt;
 
     JS_ASSERT(tokenStream.currentToken().type == TOK_FOR);
 
     if (kind == PNK_SEMI) {
         /*
          * Generator expression desugars to an immediately applied lambda that
          * yields the next value from a for-in loop (possibly nested, and with
@@ -5382,17 +5381,17 @@ Parser::comprehensionTail(ParseNode *kid
                 pn2->pn_iflags |= JSITER_FOREACH;
             else
                 tokenStream.ungetToken();
         }
         MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
 
         GenexpGuard guard(tc);
 
-        PropertyName *name = NULL;
+        RootedVarPropertyName name(context);
         tt = tokenStream.getToken();
         switch (tt) {
 #if JS_HAS_DESTRUCTURING
           case TOK_LB:
           case TOK_LC:
             tc->flags |= TCF_DECL_DESTRUCTURING;
             pn3 = primaryExpr(tt, JS_FALSE);
             tc->flags &= ~TCF_DECL_DESTRUCTURING;
@@ -6384,17 +6383,17 @@ Parser::xmlElementContent(ParseNode *pn)
  */
 ParseNode *
 Parser::xmlElementOrList(JSBool allowList)
 {
     JS_ASSERT(!tc->inStrictMode());
 
     ParseNode *pn, *pn2, *list;
     TokenKind tt;
-    JSAtom *startAtom, *endAtom;
+    RootedVarAtom startAtom(context), endAtom(context);
 
     JS_CHECK_RECURSION(context, return NULL);
 
     JS_ASSERT(tokenStream.currentToken().type == TOK_XMLSTAGO);
     pn = ListNode::create(PNK_XMLSTAGO, tc);
     if (!pn)
         return NULL;
 
@@ -6402,17 +6401,17 @@ Parser::xmlElementOrList(JSBool allowLis
     tt = tokenStream.getToken();
     if (tt == TOK_ERROR)
         return NULL;
 
     if (tt == TOK_XMLNAME || tt == TOK_LC) {
         /*
          * XMLElement.  Append the tag and its contents, if any, to pn.
          */
-        pn2 = xmlTagContent(PNK_XMLSTAGO, &startAtom);
+        pn2 = xmlTagContent(PNK_XMLSTAGO, startAtom.address());
         if (!pn2)
             return NULL;
         tokenStream.matchToken(TOK_XMLSPACE);
 
         tt = tokenStream.getToken();
         if (tt == TOK_XMLPTAGC) {
             /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */
             if (pn2->isKind(PNK_XMLSTAGO)) {
@@ -6461,17 +6460,17 @@ Parser::xmlElementOrList(JSBool allowLis
             tt = tokenStream.getToken();
             XML_CHECK_FOR_ERROR_AND_EOF(tt, NULL);
             if (tt != TOK_XMLNAME && tt != TOK_LC) {
                 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_TAG_SYNTAX);
                 return NULL;
             }
 
             /* Parse end tag; check mismatch at compile-time if we can. */
-            pn2 = xmlTagContent(PNK_XMLETAGO, &endAtom);
+            pn2 = xmlTagContent(PNK_XMLETAGO, endAtom.address());
             if (!pn2)
                 return NULL;
             if (pn2->isKind(PNK_XMLETAGO)) {
                 /* Oops, end tag has attributes! */
                 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_TAG_SYNTAX);
                 return NULL;
             }
             if (endAtom && startAtom && endAtom != startAtom) {
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -150,17 +150,19 @@ js::IsIdentifier(JSLinearString *str)
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4351)
 #endif
 
 /* Initialize members that aren't initialized in |init|. */
 TokenStream::TokenStream(JSContext *cx, JSPrincipals *prin, JSPrincipals *originPrin)
   : tokens(), tokensRoot(cx, &tokens),
-    cursor(), lookahead(), flags(), userbufRoot(cx, &userbuf), listenerTSData(), tokenbuf(cx),
+    cursor(), lookahead(), flags(),
+    linebaseRoot(cx, &linebase), prevLinebaseRoot(cx, &prevLinebase), userbufRoot(cx, &userbuf),
+    listenerTSData(), tokenbuf(cx),
     cx(cx), originPrincipals(JSScript::normalizeOriginPrincipals(prin, originPrin))
 {
     if (originPrincipals)
         JS_HoldPrincipals(originPrincipals);
 }
 
 #ifdef _MSC_VER
 #pragma warning(pop)
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -814,18 +814,20 @@ class TokenStream
     Token               tokens[ntokens];/* circular token buffer */
     JS::SkipRoot        tokensRoot;     /* prevent overwriting of token buffer */
     unsigned            cursor;         /* index of last parsed token */
     unsigned            lookahead;      /* count of lookahead tokens */
     unsigned            lineno;         /* current line number */
     unsigned            flags;          /* flags -- see above */
     const jschar        *linebase;      /* start of current line;  points into userbuf */
     const jschar        *prevLinebase;  /* start of previous line;  NULL if on the first line */
+    JS::SkipRoot        linebaseRoot;
+    JS::SkipRoot        prevLinebaseRoot;
     TokenBuf            userbuf;        /* user input buffer */
-    JS::SkipRoot        userbufRoot;    /* prevent overwriting of char pointers within userbuf */
+    JS::SkipRoot        userbufRoot;
     const char          *filename;      /* input filename or null */
     jschar              *sourceMap;     /* source map's filename or null */
     void                *listenerTSData;/* listener data for this TokenStream */
     CharBuffer          tokenbuf;       /* current token string buffer */
     int8_t              oneCharTokens[128];  /* table of one-char tokens */
     bool                maybeEOL[256];       /* probabilistic EOL lookup table */
     bool                maybeStrSpecial[256];/* speeds up string scanning */
     JSVersion           version;        /* (i.e. to identify keywords) */
--- a/js/src/gc/Root.h
+++ b/js/src/gc/Root.h
@@ -132,25 +132,25 @@ class Handle
     /*
      * Construct a handle from an explicitly rooted location. This is the
      * normal way to create a handle.
      */
     template <typename S> inline Handle(const Root<S> &root);
     template <typename S> inline Handle(const RootedVar<S> &root);
 
     const T *address() { return ptr; }
+    T value() { return *ptr; }
 
     operator T () { return value(); }
     T operator ->() { return value(); }
 
   private:
     Handle() {}
 
     const T *ptr;
-    T value() { return *ptr; }
 
     template <typename S>
     void testAssign() {
 #ifdef DEBUG
         T a = RootMethods<T>::initial();
         S b = RootMethods<S>::initial();
         a = b;
         (void)a;
@@ -191,19 +191,19 @@ class Root
     {
 #ifdef JSGC_ROOT_ANALYSIS
         ContextFriendFields *cx = ContextFriendFields::get(cx_);
 
         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));
+#endif
 
         this->ptr = ptr;
 
         JS_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     ~Root()
     {
@@ -252,28 +252,40 @@ typedef Root<Value>        RootValue;
 class SkipRoot
 {
 #if defined(DEBUG) && defined(JSGC_ROOT_ANALYSIS)
 
     SkipRoot **stack, *prev;
     const uint8_t *start;
     const uint8_t *end;
 
-  public:
     template <typename T>
-    SkipRoot(JSContext *cx_, const T *ptr
-              JS_GUARD_OBJECT_NOTIFIER_PARAM)
+    void init(ContextFriendFields *cx, const T *ptr, size_t count)
     {
-        ContextFriendFields *cx = ContextFriendFields::get(cx_);
-
         this->stack = &cx->skipGCRooters;
         this->prev = *stack;
         *stack = this;
         this->start = (const uint8_t *) ptr;
-        this->end = this->start + sizeof(T);
+        this->end = this->start + (sizeof(T) * count);
+    }
+
+  public:
+    template <typename T>
+    SkipRoot(JSContext *cx, const T *ptr
+              JS_GUARD_OBJECT_NOTIFIER_PARAM)
+    {
+        init(ContextFriendFields::get(cx), ptr, 1);
+        JS_GUARD_OBJECT_NOTIFIER_INIT;
+    }
+
+    template <typename T>
+    SkipRoot(JSContext *cx, const T *ptr, size_t count
+              JS_GUARD_OBJECT_NOTIFIER_PARAM)
+    {
+        init(ContextFriendFields::get(cx), ptr, count);
         JS_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     ~SkipRoot()
     {
         JS_ASSERT(*stack == this);
         *stack = prev;
     }
@@ -289,16 +301,23 @@ class SkipRoot
   public:
     template <typename T>
     SkipRoot(JSContext *cx, const T *ptr
               JS_GUARD_OBJECT_NOTIFIER_PARAM)
     {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
+    template <typename T>
+    SkipRoot(JSContext *cx, const T *ptr, size_t count
+              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
@@ -358,13 +377,24 @@ Handle<T>::Handle(const RootedVar<S> &ro
 }
 
 typedef RootedVar<JSObject*>    RootedVarObject;
 typedef RootedVar<JSFunction*>  RootedVarFunction;
 typedef RootedVar<JSString*>    RootedVarString;
 typedef RootedVar<jsid>         RootedVarId;
 typedef RootedVar<Value>        RootedVarValue;
 
+/*
+ * Hook for dynamic root analysis. Checks the native stack and poisons
+ * references to GC things which have not been rooted.
+ */
+#if defined(JSGC_ROOT_ANALYSIS) && defined(DEBUG) && !defined(JS_THREADSAFE)
+void CheckStackRoots(JSContext *cx);
+inline void MaybeCheckStackRoots(JSContext *cx) { CheckStackRoots(cx); }
+#else
+inline void MaybeCheckStackRoots(JSContext *cx) {}
+#endif
+
 }  /* namespace JS */
 
 #endif  /* __cplusplus */
 
 #endif  /* jsgc_root_h___ */
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -539,18 +539,17 @@ JS_ValueToSource(JSContext *cx, jsval v)
 
 JS_PUBLIC_API(JSBool)
 JS_ValueToNumber(JSContext *cx, jsval v, double *dp)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, v);
 
-    AutoValueRooter tvr(cx, v);
-    return ToNumber(cx, tvr.value(), dp);
+    return ToNumber(cx, v, dp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DoubleIsInt32(double d, int32_t *ip)
 {
     return MOZ_DOUBLE_IS_INT32(d, ip);
 }
 
@@ -568,51 +567,48 @@ JS_DoubleToUint32(double d)
 
 JS_PUBLIC_API(JSBool)
 JS_ValueToECMAInt32(JSContext *cx, jsval v, int32_t *ip)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, v);
 
-    AutoValueRooter tvr(cx, v);
-    return ToInt32(cx, tvr.value(), ip);
+    RootedVarValue value(cx, v);
+    return ToInt32(cx, value, ip);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32_t *ip)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, v);
 
-    AutoValueRooter tvr(cx, v);
-    return ToUint32(cx, tvr.value(), (uint32_t *)ip);
+    return ToUint32(cx, v, (uint32_t *)ip);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ValueToInt32(JSContext *cx, jsval v, int32_t *ip)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, v);
 
-    AutoValueRooter tvr(cx, v);
-    return NonstandardToInt32(cx, tvr.value(), (int32_t *)ip);
+    return NonstandardToInt32(cx, v, (int32_t *)ip);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ValueToUint16(JSContext *cx, jsval v, uint16_t *ip)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, v);
 
-    AutoValueRooter tvr(cx, v);
-    return ValueToUint16(cx, tvr.value(), (uint16_t *)ip);
+    return ValueToUint16(cx, v, (uint16_t *)ip);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, v);
@@ -741,16 +737,17 @@ JSRuntime::JSRuntime()
     gcIsFull(false),
     gcTriggerReason(gcreason::NO_REASON),
     gcStrictCompartmentChecking(false),
     gcIncrementalState(gc::NO_INCREMENTAL),
     gcLastMarkSlice(false),
     gcInterFrameGC(0),
     gcSliceBudget(SliceBudget::Unlimited),
     gcIncrementalEnabled(true),
+    gcExactScanningEnabled(true),
     gcPoke(false),
     gcRunning(false),
 #ifdef JS_GC_ZEAL
     gcZeal_(0),
     gcZealFrequency(0),
     gcNextScheduled(0),
     gcDeterministicOnly(false),
 #endif
@@ -1718,17 +1715,17 @@ JS_InitStandardClasses(JSContext *cx, JS
      * it before assertSameCompartment. (The API contract is that *after* this,
      * cx and obj must be in the same compartment.)
      */
     if (!cx->globalObject)
         JS_SetGlobalObject(cx, obj);
 
     assertSameCompartment(cx, obj);
 
-    return obj->global().initStandardClasses(cx);
+    return GlobalObject::initStandardClasses(cx, RootedVar<GlobalObject*>(cx, &obj->global()));
 }
 
 #define CLASP(name)                 (&name##Class)
 #define TYPED_ARRAY_CLASP(type)     (&TypedArray::fastClasses[TypedArray::type])
 #define EAGER_ATOM(name)            ATOM_OFFSET(name), NULL
 #define EAGER_CLASS_ATOM(name)      CLASS_ATOM_OFFSET(name), NULL
 #define EAGER_ATOM_AND_CLASP(name)  EAGER_CLASS_ATOM(name), CLASP(name)
 #define LAZY_ATOM(name)             ATOM_OFFSET(lazy.name), js_##name##_str
@@ -1970,26 +1967,28 @@ JS_ResolveStandardClass(JSContext *cx, J
         if (!stdnm->init(cx, obj))
             return JS_FALSE;
         *resolved = JS_TRUE;
     }
     return JS_TRUE;
 }
 
 JS_PUBLIC_API(JSBool)
-JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj)
+JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj_)
 {
     JSRuntime *rt;
     unsigned i;
 
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj);
+    assertSameCompartment(cx, obj_);
     rt = cx->runtime;
 
+    RootedVarObject obj(cx, obj_);
+
     /*
      * Check whether we need to bind 'undefined' and define it if so.
      * Since ES5 15.1.1.3 undefined can't be deleted.
      */
     PropertyName *name = rt->atomState.typeAtoms[JSTYPE_VOID];
     if (!obj->nativeContains(cx, ATOM_TO_JSID(name)) &&
         !obj->defineProperty(cx, name, UndefinedValue(),
                              JS_PropertyStub, JS_StrictPropertyStub,
@@ -3169,17 +3168,17 @@ JS_GetPrototype(JSObject *obj)
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetPrototype(JSContext *cx, JSObject *obj, JSObject *proto)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, proto);
-    return SetProto(cx, obj, proto, JS_FALSE);
+    return SetProto(cx, RootedVarObject(cx, obj), RootedVarObject(cx, proto), JS_FALSE);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetParent(JSObject *obj)
 {
     JS_ASSERT(!obj->isScope());
     return obj->getParent();
 }
@@ -3187,17 +3186,17 @@ JS_GetParent(JSObject *obj)
 JS_PUBLIC_API(JSBool)
 JS_SetParent(JSContext *cx, JSObject *obj, JSObject *parent)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
     JS_ASSERT(!obj->isScope());
     JS_ASSERT(parent || !obj->getParent());
     assertSameCompartment(cx, obj, parent);
-    return obj->setParent(cx, parent);
+    return JSObject::setParent(cx, RootedVarObject(cx, obj), RootedVarObject(cx, parent));
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetConstructor(JSContext *cx, JSObject *proto)
 {
     Value cval;
 
     AssertNoGC(cx);
@@ -3404,22 +3403,22 @@ JS_ConstructObjectWithArguments(JSContex
 
     Class *clasp = Valueify(jsclasp);
     if (!clasp)
         clasp = &ObjectClass;    /* default class is Object */
 
     JSProtoKey protoKey = GetClassProtoKey(clasp);
 
     /* Protect constructor in case a crazy getter for .prototype uproots it. */
-    AutoValueRooter tvr(cx);
-    if (!js_FindClassObject(cx, parent, protoKey, tvr.addr(), clasp))
+    RootedVarValue value(cx);
+    if (!js_FindClassObject(cx, parent, protoKey, value.address(), clasp))
         return NULL;
 
     Value rval;
-    if (!InvokeConstructor(cx, tvr.value(), argc, argv, &rval))
+    if (!InvokeConstructor(cx, value, argc, argv, &rval))
         return NULL;
 
     /*
      * If the instance's class differs from what was requested, throw a type
      * error.
      */
     if (!rval.isObject() || rval.toObject().getClass() != clasp) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
@@ -3619,17 +3618,17 @@ JS_PUBLIC_API(JSBool)
 JS_AlreadyHasOwnUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen,
                            JSBool *foundp)
 {
     JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
     return atom && JS_AlreadyHasOwnPropertyById(cx, obj, ATOM_TO_JSID(atom), foundp);
 }
 
 static JSBool
-DefinePropertyById(JSContext *cx, HandleObject obj, jsid id, const Value &value,
+DefinePropertyById(JSContext *cx, HandleObject obj, HandleId id, HandleValue value,
                    PropertyOp getter, StrictPropertyOp setter, unsigned attrs,
                    unsigned flags, int tinyid)
 {
     /*
      * JSPROP_READONLY has no meaning when accessors are involved. Ideally we'd
      * throw if this happens, but we've accepted it for long enough that it's
      * not worth trying to make callers change their ways. Just flip it off on
      * its way through the API layer so that we can enforce this internally.
@@ -3639,28 +3638,27 @@ DefinePropertyById(JSContext *cx, Handle
 
     /*
      * When we use DefineProperty, we need full scriptable Function objects rather
      * than JSNatives. However, we might be pulling this property descriptor off
      * of something with JSNative property descriptors. If we are, wrap them in
      * JS Function objects.
      */
     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->global(), NULL);
             if (!getobj)
                 return false;
             getter = JS_DATA_TO_FUNC_PTR(PropertyOp, getobj);
             attrs |= JSPROP_GETTER;
         }
         if (setter) {
+            RootObject getRoot(cx, (JSObject **) &getter);
             JSObject *setobj = JS_NewFunction(cx, (Native) setter, 1, 0, &obj->global(), NULL);
             if (!setobj)
                 return false;
             setter = JS_DATA_TO_FUNC_PTR(StrictPropertyOp, setobj);
             attrs |= JSPROP_SETTER;
         }
     }
 
@@ -3679,53 +3677,55 @@ DefinePropertyById(JSContext *cx, Handle
     if (flags != 0 && obj->isNative()) {
         return !!DefineNativeProperty(cx, obj, id, value, getter, setter,
                                       attrs, flags, tinyid);
     }
     return obj->defineGeneric(cx, id, value, getter, setter, attrs);
 }
 
 JS_PUBLIC_API(JSBool)
-JS_DefinePropertyById(JSContext *cx, JSObject *obj_, jsid id, jsval value,
+JS_DefinePropertyById(JSContext *cx, JSObject *obj_, jsid id_, jsval value_,
                       JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
 {
     RootedVarObject obj(cx, obj_);
+    RootedVarId id(cx, id_);
+    RootedVarValue value(cx, value_);
     return DefinePropertyById(cx, obj, id, value, getter, setter, attrs, 0, 0);
 }
 
 JS_PUBLIC_API(JSBool)
-JS_DefineElement(JSContext *cx, JSObject *obj_, uint32_t index, jsval value,
+JS_DefineElement(JSContext *cx, JSObject *obj_, uint32_t index, jsval value_,
                  JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
 {
     RootedVarObject obj(cx, obj_);
-    AssertNoGC(cx);
-    CHECK_REQUEST(cx);
-    jsid id;
-    if (!IndexToId(cx, index, &id))
+    RootedVarValue value(cx, value_);
+    RootGetterSetter gsRoot(cx, attrs, &getter, &setter);
+    AssertNoGC(cx);
+    CHECK_REQUEST(cx);
+    RootedVarId id(cx);
+    if (!IndexToId(cx, index, id.address()))
         return false;
     return DefinePropertyById(cx, obj, id, value, getter, setter, attrs, 0, 0);
 }
 
 static JSBool
 DefineProperty(JSContext *cx, JSObject *obj_, const char *name, const Value &value_,
                PropertyOp getter, StrictPropertyOp setter, unsigned attrs,
                unsigned flags, int tinyid)
 {
-    jsid id;
-    JSAtom *atom;
-
     RootedVarObject obj(cx, obj_);
     RootedVarValue value(cx, value_);
+    RootGetterSetter gsRoot(cx, attrs, &getter, &setter);
+    RootedVarId id(cx);
 
     if (attrs & JSPROP_INDEX) {
         id = INT_TO_JSID(intptr_t(name));
-        atom = NULL;
         attrs &= ~JSPROP_INDEX;
     } else {
-        atom = js_Atomize(cx, name, strlen(name));
+        JSAtom *atom = js_Atomize(cx, name, strlen(name));
         if (!atom)
             return JS_FALSE;
         id = ATOM_TO_JSID(atom);
     }
 
     return DefinePropertyById(cx, obj, id, value, getter, setter, attrs, flags, tinyid);
 }
 
@@ -3740,23 +3740,27 @@ JS_PUBLIC_API(JSBool)
 JS_DefinePropertyWithTinyId(JSContext *cx, JSObject *obj, const char *name, int8_t tinyid,
                             jsval value, PropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
 {
     return DefineProperty(cx, obj, name, value, getter, setter, attrs, Shape::HAS_SHORTID, tinyid);
 }
 
 static JSBool
 DefineUCProperty(JSContext *cx, JSObject *obj_, const jschar *name, size_t namelen,
-                 const Value &value, PropertyOp getter, StrictPropertyOp setter, unsigned attrs,
+                 const Value &value_, PropertyOp getter, StrictPropertyOp setter, unsigned attrs,
                  unsigned flags, int tinyid)
 {
-    RootObject obj(cx, &obj_);
+    RootedVarObject obj(cx, obj_);
+    RootedVarValue value(cx, value_);
+    RootGetterSetter gsRoot(cx, attrs, &getter, &setter);
     JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
-    return atom && DefinePropertyById(cx, obj, ATOM_TO_JSID(atom), value, getter, setter, attrs,
-                                      flags, tinyid);
+    if (!atom)
+        return false;
+    RootedVarId id(cx, ATOM_TO_JSID(atom));
+    return DefinePropertyById(cx, obj, id, value, getter, setter, attrs, flags, tinyid);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DefineUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen,
                     jsval value, JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
 {
     return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, attrs, 0, 0);
 }
@@ -4044,32 +4048,35 @@ JS_GetElementIfPresent(JSContext *cx, JS
     bool isPresent;
     if (!obj->getElementIfPresent(cx, onBehalfOf, index, vp, &isPresent))
         return false;
     *present = isPresent;
     return true;
 }
 
 JS_PUBLIC_API(JSBool)
-JS_GetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp)
-{
+JS_GetProperty(JSContext *cx, JSObject *obj_, const char *name, jsval *vp)
+{
+    RootedVarObject obj(cx, obj_);
     JSAtom *atom = js_Atomize(cx, name, strlen(name));
     return atom && JS_GetPropertyById(cx, obj, ATOM_TO_JSID(atom), vp);
 }
 
 JS_PUBLIC_API(JSBool)
-JS_GetPropertyDefault(JSContext *cx, JSObject *obj, const char *name, jsval def, jsval *vp)
-{
+JS_GetPropertyDefault(JSContext *cx, JSObject *obj_, const char *name, jsval def, jsval *vp)
+{
+    RootedVarObject obj(cx, obj_);
     JSAtom *atom = js_Atomize(cx, name, strlen(name));
     return atom && JS_GetPropertyByIdDefault(cx, obj, ATOM_TO_JSID(atom), def, vp);
 }
 
 JS_PUBLIC_API(JSBool)
-JS_GetUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, jsval *vp)
-{
+JS_GetUCProperty(JSContext *cx, JSObject *obj_, const jschar *name, size_t namelen, jsval *vp)
+{
+    RootedVarObject obj(cx, obj_);
     JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
     return atom && JS_GetPropertyById(cx, obj, ATOM_TO_JSID(atom), vp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetMethodById(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, jsval *vp)
 {
     AssertNoGC(cx);
@@ -4095,28 +4102,32 @@ JS_SetPropertyById(JSContext *cx, JSObje
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, id);
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING);
     return obj->setGeneric(cx, id, vp, false);
 }
 
 JS_PUBLIC_API(JSBool)
-JS_SetElement(JSContext *cx, JSObject *obj, uint32_t index, jsval *vp)
-{
-    AssertNoGC(cx);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj, *vp);
+JS_SetElement(JSContext *cx, JSObject *obj_, uint32_t index, jsval *vp)
+{
+    AssertNoGC(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj_, *vp);
+    RootedVarObject obj(cx, obj_);
+    RootValue vpRoot(cx, vp);
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING);
     return obj->setElement(cx, index, vp, false);
 }
 
 JS_PUBLIC_API(JSBool)
-JS_SetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp)
-{
+JS_SetProperty(JSContext *cx, JSObject *obj_, const char *name, jsval *vp)
+{
+    RootedVarObject obj(cx, obj_);
+    RootValue vpRoot(cx, vp);
     JSAtom *atom = js_Atomize(cx, name, strlen(name));
     return atom && JS_SetPropertyById(cx, obj, ATOM_TO_JSID(atom), vp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, jsval *vp)
 {
     JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
@@ -4378,17 +4389,17 @@ JS_NextProperty(JSContext *cx, JSObject 
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_NewElementIterator(JSContext *cx, JSObject *obj)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
-    return ElementIteratorObject::create(cx, obj);
+    return ElementIteratorObject::create(cx, RootedVarObject(cx, obj));
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_ElementIteratorStub(JSContext *cx, JSObject *obj, JSBool keysonly)
 {
     JS_ASSERT(!keysonly);
     return JS_NewElementIterator(cx, obj);
 }
@@ -4610,19 +4621,19 @@ JS_IsNativeFunction(JSObject *funobj, JS
         return false;
     JSFunction *fun = funobj->toFunction();
     return fun->isNative() && fun->native() == call;
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_BindCallable(JSContext *cx, JSObject *callable, JSObject *newThis)
 {
-    RootedVarObject target(cx);
-    target = callable;
-    return js_fun_bind(cx, target, ObjectValue(*newThis), NULL, 0);
+    RootedVarObject target(cx, callable);
+    RootedVarValue thisArg(cx, ObjectValue(*newThis));
+    return js_fun_bind(cx, target, thisArg, NULL, 0);
 }
 
 JSBool
 js_generic_native_method_dispatcher(JSContext *cx, unsigned argc, Value *vp)
 {
     JSFunctionSpec *fs = (JSFunctionSpec *)
         vp->toObject().toFunction()->getExtendedSlot(0).toPrivate();
     JS_ASSERT((fs->flags & JSFUN_GENERIC_NATIVE) != 0);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1065,17 +1065,17 @@ class AutoStringRooter : private AutoGCR
     JSString *str;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 class AutoArrayRooter : private AutoGCRooter {
   public:
     AutoArrayRooter(JSContext *cx, size_t len, Value *vec
                     JS_GUARD_OBJECT_NOTIFIER_PARAM)
-      : AutoGCRooter(cx, len), array(vec)
+      : AutoGCRooter(cx, len), array(vec), skip(cx, array, len)
     {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
         JS_ASSERT(tag >= 0);
     }
 
     void changeLength(size_t newLength) {
         tag = ptrdiff_t(newLength);
         JS_ASSERT(tag >= 0);
@@ -1087,16 +1087,18 @@ class AutoArrayRooter : private AutoGCRo
     }
 
     Value *array;
 
     friend void AutoGCRooter::trace(JSTracer *trc);
 
   private:
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
+
+    SkipRoot skip;
 };
 
 /* The auto-root for enumeration object and its state. */
 class AutoEnumStateRooter : private AutoGCRooter
 {
   public:
     AutoEnumStateRooter(JSContext *cx, JSObject *obj
                         JS_GUARD_OBJECT_NOTIFIER_PARAM)
@@ -2376,18 +2378,20 @@ ToNumberSlow(JSContext *cx, JS::Value v,
 
 namespace JS {
 
 /* ES5 9.3 ToNumber. */
 JS_ALWAYS_INLINE bool
 ToNumber(JSContext *cx, const Value &v, double *out)
 {
     AssertArgumentsAreSane(cx, v);
+
     if (v.isNumber()) {
         *out = v.toNumber();
+        MaybeCheckStackRoots(cx);
         return true;
     }
     return js::ToNumberSlow(cx, v, out);
 }
 
 } /* namespace JS */
 #endif /* __cplusplus */
 
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -161,27 +161,27 @@ js_GetLengthProperty(JSContext *cx, JSOb
     if (obj->isArguments()) {
         ArgumentsObject &argsobj = obj->asArguments();
         if (!argsobj.hasOverriddenLength()) {
             *lengthp = argsobj.initialLength();
             return true;
         }
     }
 
-    AutoValueRooter tvr(cx);
-    if (!obj->getProperty(cx, cx->runtime->atomState.lengthAtom, tvr.addr()))
+    RootedVarValue value(cx);
+    if (!obj->getProperty(cx, cx->runtime->atomState.lengthAtom, value.address()))
         return false;
 
-    if (tvr.value().isInt32()) {
-        *lengthp = uint32_t(tvr.value().toInt32()); /* uint32_t cast does ToUint32_t */
+    if (value.reference().isInt32()) {
+        *lengthp = uint32_t(value.reference().toInt32()); /* uint32_t cast does ToUint32_t */
         return true;
     }
 
     
-    return ToUint32(cx, tvr.value(), (uint32_t *)lengthp);
+    return ToUint32(cx, value, (uint32_t *)lengthp);
 }
 
 namespace js {
 
 /*
  * Determine if the id represents an array index or an XML property index.
  *
  * An id is an array index according to ECMA by (15.4):
@@ -361,37 +361,38 @@ JSObject::arrayGetOwnDataElement(JSConte
 
 /*
  * If the property at the given index exists, get its value into location
  * pointed by vp and set *hole to false. Otherwise set *hole to true and *vp
  * to JSVAL_VOID. This function assumes that the location pointed by vp is
  * properly rooted and can be used as GC-protected storage for temporaries.
  */
 static inline JSBool
-DoGetElement(JSContext *cx, JSObject *obj, double index, JSBool *hole, Value *vp)
+DoGetElement(JSContext *cx, JSObject *obj_, double index, JSBool *hole, Value *vp)
 {
-    AutoIdRooter idr(cx);
+    RootedVarObject obj(cx, obj_);
+    RootedVarId id(cx);
 
     *hole = JS_FALSE;
-    if (!IndexToId(cx, obj, index, hole, idr.addr()))
+    if (!IndexToId(cx, obj, index, hole, id.address()))
         return JS_FALSE;
     if (*hole) {
         vp->setUndefined();
         return JS_TRUE;
     }
 
     JSObject *obj2;
     JSProperty *prop;
-    if (!obj->lookupGeneric(cx, idr.id(), &obj2, &prop))
+    if (!obj->lookupGeneric(cx, id, &obj2, &prop))
         return JS_FALSE;
     if (!prop) {
         vp->setUndefined();
         *hole = JS_TRUE;
     } else {
-        if (!obj->getGeneric(cx, idr.id(), vp))
+        if (!obj->getGeneric(cx, id, vp))
             return JS_FALSE;
         *hole = JS_FALSE;
     }
     return JS_TRUE;
 }
 
 static inline JSBool
 DoGetElement(JSContext *cx, JSObject *obj, uint32_t index, JSBool *hole, Value *vp)
@@ -438,28 +439,28 @@ GetElement(JSContext *cx, JSObject *obj,
     }
 
     return DoGetElement(cx, obj, index, hole, vp);
 }
 
 namespace js {
 
 static bool
-GetElementsSlow(JSContext *cx, JSObject *aobj, uint32_t length, Value *vp)
+GetElementsSlow(JSContext *cx, HandleObject aobj, uint32_t length, Value *vp)
 {
     for (uint32_t i = 0; i < length; i++) {
         if (!aobj->getElement(cx, i, &vp[i]))
             return false;
     }
 
     return true;
 }
 
 bool
-GetElements(JSContext *cx, JSObject *aobj, uint32_t length, Value *vp)
+GetElements(JSContext *cx, HandleObject aobj, uint32_t length, Value *vp)
 {
     if (aobj->isDenseArray() && length <= aobj->getDenseArrayInitializedLength() &&
         !js_PrototypeHasIndexedProperties(cx, aobj)) {
         /* The prototype does not have indexed properties so hole = undefined */
         const Value *srcbeg = aobj->getDenseArrayElements();
         const Value *srcend = srcbeg + length;
         const Value *src = srcbeg;
         for (Value *dst = vp; src < srcend; ++dst, ++src)
@@ -512,18 +513,18 @@ SetArrayElement(JSContext *cx, HandleObj
     }
 
     AutoIdRooter idr(cx);
 
     if (!IndexToId(cx, obj, index, NULL, idr.addr(), JS_TRUE))
         return JS_FALSE;
     JS_ASSERT(!JSID_IS_VOID(idr.id()));
 
-    Value tmp = v;
-    return obj->setGeneric(cx, idr.id(), &tmp, true);
+    RootedVarValue tmp(cx, v);
+    return obj->setGeneric(cx, idr.id(), tmp.address(), true);
 }
 
 /*
  * Delete the element |index| from |obj|. If |strict|, do a strict
  * deletion: throw if the property is not configurable.
  *
  * - Return 1 if the deletion succeeds (that is, ES5's [[Delete]] would
  *   return true)
@@ -1070,28 +1071,22 @@ array_defineProperty(JSContext *cx, JSOb
 {
     return array_defineGeneric(cx, obj, ATOM_TO_JSID(name), value, getter, setter, attrs);
 }
 
 namespace js {
 
 /* non-static for direct definition of array elements within the engine */
 JSBool
-array_defineElement(JSContext *cx, JSObject *obj_, uint32_t index, const Value *value,
+array_defineElement(JSContext *cx, JSObject *obj, uint32_t index, const Value *value,
                     PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
-    RootedVarObject obj(cx, obj_);
-
     if (!obj->isDenseArray())
         return js_DefineElement(cx, obj, index, value, getter, setter, attrs);
 
-    jsid id;
-    if (!IndexToId(cx, index, &id))
-        return false;
-
     do {
         /*
          * UINT32_MAX is not an array index and must not affect the length
          * property, so specifically reject it.
          */
         if (attrs != JSPROP_ENUMERATE || index == UINT32_MAX)
             break;
 
@@ -1104,17 +1099,21 @@ array_defineElement(JSContext *cx, JSObj
         }
 
         if (index >= obj->getArrayLength())
             obj->setDenseArrayLength(index + 1);
         obj->setDenseArrayElementWithType(cx, index, *value);
         return true;
     } while (false);
 
-    if (!JSObject::makeDenseArraySlow(cx, obj))
+    RootObject objRoot(cx, &obj);
+    RootValue valueRoot(cx, value);
+    RootGetterSetter gsRoot(cx, attrs, &getter, &setter);
+
+    if (!JSObject::makeDenseArraySlow(cx, objRoot))
         return false;
     return js_DefineElement(cx, obj, index, value, getter, setter, attrs);
 }
 
 } // namespace js
 
 static JSBool
 array_defineSpecial(JSContext *cx, JSObject *obj, SpecialId sid, const Value *value,
@@ -1206,17 +1205,17 @@ array_deleteElement(JSContext *cx, JSObj
     if (!obj->isDenseArray())
         return js_DeleteElement(cx, obj, index, rval, strict);
 
     if (index < obj->getDenseArrayInitializedLength()) {
         obj->markDenseArrayNotPacked(cx);
         obj->setDenseArrayElement(index, MagicValue(JS_ARRAY_HOLE));
     }
 
-    if (!js_SuppressDeletedElement(cx, obj, index))
+    if (!js_SuppressDeletedElement(cx, RootedVarObject(cx, obj), index))
         return false;
 
     rval->setBoolean(true);
     return true;
 }
 
 } // namespace js
 
@@ -1463,17 +1462,17 @@ class ArraySharpDetector
   public:
     ArraySharpDetector(JSContext *cx)
       : cx(cx),
         success(false),
         alreadySeen(false),
         sharp(false)
     {}
 
-    bool init(JSObject *obj) {
+    bool init(HandleObject obj) {
         success = js_EnterSharpObject(cx, obj, NULL, &alreadySeen, &sharp);
         if (!success)
             return false;
         return true;
     }
 
     bool initiallySharp() const {
         JS_ASSERT_IF(sharp, alreadySeen);
@@ -1487,17 +1486,17 @@ class ArraySharpDetector
 };
 
 static JSBool
 array_toSource(JSContext *cx, unsigned argc, Value *vp)
 {
     JS_CHECK_RECURSION(cx, return false);
 
     CallArgs args = CallArgsFromVp(argc, vp);
-    JSObject *obj = ToObject(cx, &args.thisv());
+    RootedVarObject obj(cx, ToObject(cx, &args.thisv()));
     if (!obj)
         return false;
     if (!obj->isArray())
         return HandleNonGenericMethodClassMismatch(cx, args, array_toSource, &ArrayClass);
 
     ArraySharpDetector detector(cx);
     if (!detector.init(obj))
         return false;
@@ -1603,27 +1602,25 @@ class AutoArrayCycleDetector
     }
 
     bool foundCycle() { return cycle; }
 
   protected:
 };
 
 static JSBool
-array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale,
-                   JSString *sepstr, CallArgs &args)
+array_toString_sub(JSContext *cx, HandleObject obj, JSBool locale,
+                   HandleString sepstr, CallArgs &args)
 {
     static const jschar comma = ',';
     const jschar *sep;
     size_t seplen;
     if (sepstr) {
+        sep = NULL;
         seplen = sepstr->length();
-        sep = sepstr->getChars(cx);
-        if (!sep)
-            return false;
     } else {
         sep = &comma;
         seplen = 1;
     }
 
     AutoArrayCycleDetector detector(cx, obj);
     if (!detector.init())
         return false;
@@ -1692,17 +1689,18 @@ array_toString_sub(JSContext *cx, JSObje
                     if (!robj->callMethod(cx, id, 0, NULL, &elt))
                         return false;
                 }
                 if (!ValueToStringBuffer(cx, elt, sb))
                     return false;
             }
 
             if (index + 1 != length) {
-                if (!sb.append(sep, seplen))
+                const jschar *sepchars = sep ? sep : sepstr->getChars(cx);
+                if (!sepchars || !sb.append(sepchars, seplen))
                     return false;
             }
         }
     }
 
     JSString *str = sb.finishString();
     if (!str)
         return false;
@@ -1748,25 +1746,25 @@ array_toString(JSContext *cx, unsigned a
 }
 
 static JSBool
 array_toLocaleString(JSContext *cx, unsigned argc, Value *vp)
 {
     JS_CHECK_RECURSION(cx, return false);
 
     CallArgs args = CallArgsFromVp(argc, vp);
-    JSObject *obj = ToObject(cx, &args.thisv());
+    RootedVarObject obj(cx, ToObject(cx, &args.thisv()));
     if (!obj)
         return false;
 
     /*
      *  Passing comma here as the separator. Need a way to get a
      *  locale-specific version.
      */
-    return array_toString_sub(cx, obj, JS_TRUE, NULL, args);
+    return array_toString_sub(cx, obj, JS_TRUE, RootedVarString(cx), args);
 }
 
 static inline bool
 InitArrayTypes(JSContext *cx, TypeObject *type, const Value *vector, unsigned count)
 {
     if (cx->typeInferenceEnabled() && !type->unknownProperties()) {
         AutoEnterTypeInference enter(cx);
 
@@ -1839,23 +1837,23 @@ InitArrayElements(JSContext *cx, HandleO
     if (vector == end)
         return true;
 
     /* Finish out any remaining elements past the max array index. */
     if (obj->isDenseArray() && !JSObject::makeDenseArraySlow(cx, obj))
         return false;
 
     JS_ASSERT(start == MAX_ARRAY_INDEX + 1);
-    AutoValueRooter tvr(cx);
-    AutoIdRooter idr(cx);
+    RootedVarValue value(cx);
+    RootedVarId id(cx);
     Value idval = DoubleValue(MAX_ARRAY_INDEX + 1);
     do {
-        *tvr.addr() = *vector++;
-        if (!js_ValueToStringId(cx, idval, idr.addr()) ||
-            !obj->setGeneric(cx, idr.id(), tvr.addr(), true)) {
+        value = *vector++;
+        if (!js_ValueToStringId(cx, idval, id.address()) ||
+            !obj->setGeneric(cx, id, value.address(), true)) {
             return false;
         }
         idval.getDoubleRef() += 1;
     } while (vector != end);
 
     return true;
 }
 
@@ -1895,26 +1893,26 @@ InitArrayObject(JSContext *cx, JSObject 
  * Perl-inspired join, reverse, and sort.
  */
 static JSBool
 array_join(JSContext *cx, unsigned argc, Value *vp)
 {
     JS_CHECK_RECURSION(cx, return false);
 
     CallArgs args = CallArgsFromVp(argc, vp);
-    JSString *str;
+    RootedVarString str(cx);
     if (args.hasDefined(0)) {
         str = ToString(cx, args[0]);
         if (!str)
             return JS_FALSE;
         args[0].setString(str);
     } else {
         str = NULL;
     }
-    JSObject *obj = ToObject(cx, &args.thisv());
+    RootedVarObject obj(cx, ToObject(cx, &args.thisv()));
     if (!obj)
         return false;
     return array_toString_sub(cx, obj, JS_FALSE, str, args);
 }
 
 static JSBool
 array_reverse(JSContext *cx, unsigned argc, Value *vp)
 {
@@ -2218,16 +2216,18 @@ js::array_sort(JSContext *cx, unsigned a
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_SORT_ARG);
             return false;
         }
         fval = args[0];     /* non-default compare function */
     } else {
         fval.setNull();
     }
 
+    RootValue fvalRoot(cx, &fval);
+
     RootedVarObject obj(cx, ToObject(cx, &args.thisv()));
     if (!obj)
         return false;
 
     uint32_t len;
     if (!js_GetLengthProperty(cx, obj, &len))
         return false;
     if (len == 0) {
@@ -2482,18 +2482,18 @@ array_pop_slowly(JSContext *cx, HandleOb
     if (index == 0) {
         args.rval().setUndefined();
         return js_SetLengthProperty(cx, obj, index);
     }
 
     index--;
 
     JSBool hole;
-    Value elt;
-    if (!GetElement(cx, obj, index, &hole, &elt))
+    RootedVarValue elt(cx);
+    if (!GetElement(cx, obj, index, &hole, elt.address()))
         return false;
 
     if (!hole && DeleteArrayElement(cx, obj, index, true) < 0)
         return false;
 
     args.rval() = elt;
     return js_SetLengthProperty(cx, obj, index);
 }
@@ -2505,18 +2505,18 @@ array_pop_dense(JSContext *cx, HandleObj
     if (index == 0) {
         args.rval().setUndefined();
         return JS_TRUE;
     }
 
     index--;
 
     JSBool hole;
-    Value elt;
-    if (!GetElement(cx, obj, index, &hole, &elt))
+    RootedVarValue elt(cx);
+    if (!GetElement(cx, obj, index, &hole, elt.address()))
         return JS_FALSE;
 
     if (!hole && DeleteArrayElement(cx, obj, index, true) < 0)
         return JS_FALSE;
     if (obj->getDenseArrayInitializedLength() > index)
         obj->setDenseArrayInitializedLength(index);
 
     obj->setArrayLength(cx, index);
@@ -2585,21 +2585,21 @@ js::array_shift(JSContext *cx, unsigned 
             return JS_TRUE;
         }
 
         JSBool hole;
         if (!GetElement(cx, obj, 0u, &hole, &args.rval()))
             return JS_FALSE;
 
         /* Slide down the array above the first element. */
-        AutoValueRooter tvr(cx);
+        RootedVarValue value(cx);
         for (uint32_t i = 0; i < length; i++) {
             if (!JS_CHECK_OPERATION_LIMIT(cx) ||
-                !GetElement(cx, obj, i + 1, &hole, tvr.addr()) ||
-                !SetOrDeleteArrayElement(cx, obj, i, hole, tvr.value())) {
+                !GetElement(cx, obj, i + 1, &hole, value.address()) ||
+                !SetOrDeleteArrayElement(cx, obj, i, hole, value)) {
                 return JS_FALSE;
             }
         }
 
         /* Delete the only or last element when it exists. */
         if (!hole && DeleteArrayElement(cx, obj, length, true) < 0)
             return JS_FALSE;
     }
@@ -2639,23 +2639,23 @@ array_unshift(JSContext *cx, unsigned ar
                 for (uint32_t i = 0; i < args.length(); i++)
                     obj->setDenseArrayElement(i, MagicValue(JS_ARRAY_HOLE));
                 optimized = true;
             } while (false);
 
             if (!optimized) {
                 double last = length;
                 double upperIndex = last + args.length();
-                AutoValueRooter tvr(cx);
+                RootedVarValue value(cx);
                 do {
                     --last, --upperIndex;
                     JSBool hole;
                     if (!JS_CHECK_OPERATION_LIMIT(cx) ||
-                        !GetElement(cx, obj, last, &hole, tvr.addr()) ||
-                        !SetOrDeleteArrayElement(cx, obj, upperIndex, hole, tvr.value())) {
+                        !GetElement(cx, obj, last, &hole, value.address()) ||
+                        !SetOrDeleteArrayElement(cx, obj, upperIndex, hole, value)) {
                         return JS_FALSE;
                     }
                 } while (last != 0);
             }
         }
 
         /* Copy from args to the bottom of the array. */
         if (!InitArrayElements(cx, obj, 0, args.length(), args.array(), UpdateTypes))
@@ -2766,17 +2766,17 @@ array_splice(JSContext *cx, unsigned arg
          * delete to the end of the array.  See bug 668024 for discussion.
          */
         actualDeleteCount = len - actualStart;
     }
 
     JS_ASSERT(len - actualStart >= actualDeleteCount);
 
     /* Steps 2, 8-9. */
-    JSObject *arr;
+    RootedVarObject arr(cx);
     if (CanOptimizeForDenseStorage(obj, actualStart, actualDeleteCount, cx)) {
         arr = NewDenseCopiedArray(cx, actualDeleteCount,
                                   obj->getDenseArrayElements() + actualStart);
         if (!arr)
             return false;
         TryReuseArrayType(obj, arr);
     } else {
         arr = NewDenseAllocatedArray(cx, actualDeleteCount);
@@ -2984,25 +2984,25 @@ js::array_concat(JSContext *cx, unsigned
     }
 
     /* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */
     for (unsigned i = 0; i <= argc; i++) {
         if (!JS_CHECK_OPERATION_LIMIT(cx))
             return false;
         const Value &v = p[i];
         if (v.isObject()) {
-            JSObject &obj = v.toObject();
-            if (ObjectClassIs(obj, ESClass_Array, cx)) {
+            RootedVarObject obj(cx, &v.toObject());
+            if (ObjectClassIs(*obj, ESClass_Array, cx)) {
                 uint32_t alength;
-                if (!js_GetLengthProperty(cx, &obj, &alength))
+                if (!js_GetLengthProperty(cx, obj, &alength))
                     return false;
                 for (uint32_t slot = 0; slot < alength; slot++) {
                     JSBool hole;
                     Value tmp;
-                    if (!JS_CHECK_OPERATION_LIMIT(cx) || !GetElement(cx, &obj, slot, &hole, &tmp))
+                    if (!JS_CHECK_OPERATION_LIMIT(cx) || !GetElement(cx, obj, slot, &hole, &tmp))
                         return false;
 
                     /*
                      * Per ECMA 262, 15.4.4.4, step 9, ignore nonexistent
                      * properties.
                      */
                     if (!hole && !SetArrayElement(cx, nobj, length + slot, tmp))
                         return false;
@@ -3023,17 +3023,17 @@ js::array_concat(JSContext *cx, unsigned
 static JSBool
 array_slice(JSContext *cx, unsigned argc, Value *vp)
 {
     uint32_t length, begin, end, slot;
     JSBool hole;
 
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    JSObject *obj = ToObject(cx, &args.thisv());
+    RootedVarObject obj(cx, ToObject(cx, &args.thisv()));
     if (!obj)
         return false;
 
     if (!js_GetLengthProperty(cx, obj, &length))
         return JS_FALSE;
     begin = 0;
     end = length;
 
@@ -3079,23 +3079,23 @@ array_slice(JSContext *cx, unsigned argc
         return JS_TRUE;
     }
 
     nobj = NewDenseAllocatedArray(cx, end - begin);
     if (!nobj)
         return JS_FALSE;
     TryReuseArrayType(obj, nobj);
 
-    AutoValueRooter tvr(cx);
+    RootedVarValue value(cx);
     for (slot = begin; slot < end; slot++) {
         if (!JS_CHECK_OPERATION_LIMIT(cx) ||
-            !GetElement(cx, obj, slot, &hole, tvr.addr())) {
+            !GetElement(cx, obj, slot, &hole, value.address())) {
             return JS_FALSE;
         }
-        if (!hole && !SetArrayElement(cx, nobj, slot - begin, tvr.value()))
+        if (!hole && !SetArrayElement(cx, nobj, slot - begin, value))
             return JS_FALSE;
     }
 
     args.rval().setObject(*nobj);
     return JS_TRUE;
 }
 
 enum IndexOfKind {
@@ -3229,31 +3229,31 @@ class ArraySomeBehavior
     static Value lateExitValue() { return BooleanValue(false); }
 };
 
 template <class Behavior>
 static inline bool
 array_readonlyCommon(JSContext *cx, CallArgs &args)
 {
     /* Step 1. */
-    JSObject *obj = ToObject(cx, &args.thisv());
+    RootedVarObject obj(cx, ToObject(cx, &args.thisv()));
     if (!obj)
         return false;
 
     /* Step 2-3. */
     uint32_t len;
     if (!js_GetLengthProperty(cx, obj, &len))
         return false;
 
     /* Step 4. */
     if (args.length() == 0) {
         js_ReportMissingArg(cx, args.calleev(), 0);
         return false;
     }
-    JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK);
+    RootedVarObject callable(cx, js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK));
     if (!callable)
         return false;
 
     /* Step 5. */
     Value thisv = args.length() >= 2 ? args[1] : UndefinedValue();
 
     /* Step 6. */
     uint32_t k = 0;
@@ -3321,31 +3321,31 @@ array_forEach(JSContext *cx, unsigned ar
 
 /* ES5 15.4.4.19. */
 static JSBool
 array_map(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* Step 1. */
-    JSObject *obj = ToObject(cx, &args.thisv());
+    RootedVarObject obj(cx, ToObject(cx, &args.thisv()));
     if (!obj)
         return false;
 
     /* Step 2-3. */
     uint32_t len;
     if (!js_GetLengthProperty(cx, obj, &len))
         return false;
 
     /* Step 4. */
     if (args.length() == 0) {
         js_ReportMissingArg(cx, args.calleev(), 0);
         return false;
     }
-    JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK);
+    RootedVarObject callable(cx, js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK));
     if (!callable)
         return false;
 
     /* Step 5. */
     Value thisv = args.length() >= 2 ? args[1] : UndefinedValue();
 
     /* Step 6. */
     RootedVarObject arr(cx, NewDenseAllocatedArray(cx, len));
@@ -3397,31 +3397,31 @@ array_map(JSContext *cx, unsigned argc, 
 
 /* ES5 15.4.4.20. */
 static JSBool
 array_filter(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* Step 1. */
-    JSObject *obj = ToObject(cx, &args.thisv());
+    RootedVarObject obj(cx, ToObject(cx, &args.thisv()));
     if (!obj)
         return false;
 
     /* Step 2-3. */
     uint32_t len;
     if (!js_GetLengthProperty(cx, obj, &len))
         return false;
 
     /* Step 4. */
     if (args.length() == 0) {
         js_ReportMissingArg(cx, args.calleev(), 0);
         return false;
     }
-    JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK);
+    RootedVarObject callable(cx, js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK));
     if (!callable)
         return false;
 
     /* Step 5. */
     Value thisv = args.length() >= 2 ? args[1] : UndefinedValue();
 
     /* Step 6. */
     RootedVarObject arr(cx, NewDenseAllocatedArray(cx, 0));
@@ -3875,16 +3875,19 @@ mjit::stubs::NewDenseUnallocatedArray(VM
 
     return obj;
 }
 #endif
 
 JSObject *
 NewDenseCopiedArray(JSContext *cx, uint32_t length, const Value *vp, JSObject *proto /* = NULL */)
 {
+    // XXX vp may be an internal pointer to an object's dense array elements.
+    SkipRoot skip(cx, &vp);
+
     JSObject* obj = NewArray<true>(cx, length, proto);
     if (!obj)
         return NULL;
 
     JS_ASSERT(obj->getDenseArrayCapacity() >= length);
 
     obj->setDenseArrayInitializedLength(vp ? length : 0);
 
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -131,17 +131,17 @@ array_deleteElement(JSContext *cx, JSObj
 
 /*
  * Copy 'length' elements from aobj to vp.
  *
  * This function assumes 'length' is effectively the result of calling
  * js_GetLengthProperty on aobj.
  */
 extern bool
-GetElements(JSContext *cx, JSObject *aobj, uint32_t length, js::Value *vp);
+GetElements(JSContext *cx, HandleObject aobj, uint32_t length, js::Value *vp);
 
 /* Natives exposed for optimization by the interpreter and JITs. */
 
 extern JSBool
 array_sort(JSContext *cx, unsigned argc, js::Value *vp);
 
 extern JSBool
 array_push(JSContext *cx, unsigned argc, js::Value *vp);
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -335,16 +335,18 @@ AtomizeInline(JSContext *cx, const jscha
         p->setTagged(bool(ib));
         return atom;
     }
 
     SwitchToCompartment sc(cx, cx->runtime->atomsCompartment);
 
     JSFixedString *key;
 
+    SkipRoot skip(cx, &chars);
+
     if (ocb == TakeCharOwnership) {
         key = js_NewString(cx, const_cast<jschar *>(chars), length);
         if (!key)
             return NULL;
         *pchars = NULL; /* Called should not free *pchars. */
     } else {
         JS_ASSERT(ocb == CopyChars);
         key = js_NewStringCopyN(cx, chars, length);
--- a/js/src/jsatominlines.h
+++ b/js/src/jsatominlines.h
@@ -173,16 +173,18 @@ BackfillIndexInCharBuffer(uint32_t index
     } while (index > 0);
 
     return end;
 }
 
 inline bool
 IndexToId(JSContext *cx, uint32_t index, jsid *idp)
 {
+    MaybeCheckStackRoots(cx);
+
     if (index <= JSID_INT_MAX) {
         *idp = INT_TO_JSID(index);
         return true;
     }
 
     extern bool IndexToIdSlow(JSContext *cx, uint32_t index, jsid *idp);
     return IndexToIdSlow(cx, index, idp);
 }
--- a/js/src/jsclass.h
+++ b/js/src/jsclass.h
@@ -408,11 +408,30 @@ inline bool
 ObjectClassIs(JSObject &obj, ESClassValue classValue, JSContext *cx);
 
 /* Just a helper that checks v.isObject before calling ObjectClassIs. */
 inline bool
 IsObjectWithClass(const Value &v, ESClassValue classValue, JSContext *cx);
 
 }  /* namespace js */
 
+namespace JS {
+
+inline bool
+IsPoisonedSpecialId(js::SpecialId iden)
+{
+    if (iden.isObject())
+        return IsPoisonedPtr(iden.toObject());
+    return false;
+}
+
+template <> struct RootMethods<js::SpecialId>
+{
+    static js::SpecialId initial() { return js::SpecialId(); }
+    static ThingRootKind kind() { return THING_ROOT_ID; }
+    static bool poisoned(js::SpecialId id) { return IsPoisonedSpecialId(id); }
+};
+
+} /* namespace JS */
+
 #endif  /* __cplusplus */
 
 #endif  /* jsclass_h__ */
--- a/js/src/jsclone.cpp
+++ b/js/src/jsclone.cpp
@@ -956,20 +956,20 @@ JSStructuredCloneReader::readId(jsid *id
 
 bool
 JSStructuredCloneReader::read(Value *vp)
 {
     if (!startRead(vp))
         return false;
 
     while (objs.length() != 0) {
-        JSObject *obj = &objs.back().toObject();
+        RootedVarObject obj(context(), &objs.back().toObject());
 
-        jsid id;
-        if (!readId(&id))
+        RootedVarId id(context());
+        if (!readId(id.address()))
             return false;
 
         if (JSID_IS_VOID(id)) {
             objs.popBack();
         } else {
             Value v;
             if (!startRead(&v) || !obj->defineGeneric(context(), id, v))
                 return false;
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -123,37 +123,50 @@ struct GSNCache {
     void purge();
 };
 
 inline GSNCache *
 GetGSNCache(JSContext *cx);
 
 struct PendingProxyOperation {
     PendingProxyOperation   *next;
-    JSObject                *object;
+    RootedVarObject         object;
+    PendingProxyOperation(JSContext *cx, JSObject *object) : next(NULL), object(cx, object) {}
 };
 
 typedef Vector<ScriptAndCounts, 0, SystemAllocPolicy> ScriptAndCountsVector;
 
 struct ConservativeGCData
 {
     /*
      * The GC scans conservatively between ThreadData::nativeStackBase and
      * nativeStackTop unless the latter is NULL.
      */
     uintptr_t           *nativeStackTop;
 
+#if defined(JSGC_ROOT_ANALYSIS) && (JS_STACK_GROWTH_DIRECTION < 0)
+    /*
+     * Record old contents of the native stack from the last time there was a
+     * scan, to reduce the overhead involved in repeatedly rescanning the
+     * native stack during root analysis. oldStackData stores words in reverse
+     * order starting at oldStackEnd.
+     */
+    uintptr_t           *oldStackMin, *oldStackEnd;
+    uintptr_t           *oldStackData;
+    size_t              oldStackCapacity; // in sizeof(uintptr_t)
+#endif
+
     union {
         jmp_buf         jmpbuf;
         uintptr_t       words[JS_HOWMANY(sizeof(jmp_buf), sizeof(uintptr_t))];
     } registerSnapshot;
 
-    ConservativeGCData()
-      : nativeStackTop(NULL)
-    {}
+    ConservativeGCData() {
+        PodZero(this);
+    }
 
     ~ConservativeGCData() {
 #ifdef JS_THREADSAFE
         /*
          * The conservative GC scanner should be disabled when the thread leaves
          * the last request.
          */
         JS_ASSERT(!hasStackToScan());
@@ -395,16 +408,23 @@ struct JSRuntime : js::RuntimeFriendFiel
 
     /*
      * We disable incremental GC if we encounter a js::Class with a trace hook
      * that does not implement write barriers.
      */
     bool                gcIncrementalEnabled;
 
     /*
+     * Whether exact stack scanning is enabled for this runtime. This is
+     * currently only used for dynamic root analysis. Exact scanning starts out
+     * enabled, and is disabled if e4x has been used.
+     */
+    bool                gcExactScanningEnabled;
+
+    /*
      * We save all conservative scanned roots in this vector so that
      * conservative scanning can be "replayed" deterministically. In DEBUG mode,
      * this allows us to run a non-incremental GC after every incremental GC to
      * ensure that no objects were missed.
      */
 #ifdef DEBUG
     struct SavedGCRoot {
         void *thing;
@@ -1648,21 +1668,22 @@ class AutoShapeVector : public AutoVecto
 
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 class AutoValueArray : public AutoGCRooter
 {
     js::Value *start_;
     unsigned length_;
+    SkipRoot skip;
 
   public:
     AutoValueArray(JSContext *cx, js::Value *start, unsigned length
                    JS_GUARD_OBJECT_NOTIFIER_PARAM)
-        : AutoGCRooter(cx, VALARRAY), start_(start), length_(length)
+      : AutoGCRooter(cx, VALARRAY), start_(start), length_(length), skip(cx, start, length)
     {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     Value *start() { return start_; }
     unsigned length() const { return length_; }
 
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -357,17 +357,17 @@ CallJSNativeConstructor(JSContext *cx, N
 }
 
 JS_ALWAYS_INLINE bool
 CallJSPropertyOp(JSContext *cx, PropertyOp op, JSObject *receiver, jsid id, Value *vp)
 {
     assertSameCompartment(cx, receiver, id, *vp);
     JSBool ok = op(cx, receiver, id, vp);
     if (ok)
-        assertSameCompartment(cx, receiver, *vp);
+        assertSameCompartment(cx, *vp);
     return ok;
 }
 
 JS_ALWAYS_INLINE bool
 CallJSPropertyOpSetter(JSContext *cx, StrictPropertyOp op, JSObject *obj, jsid id,
                        JSBool strict, Value *vp)
 {
     assertSameCompartment(cx, obj, id, *vp);
@@ -485,16 +485,17 @@ JSContext::ensureGeneratorStackSpace()
     bool ok = genStack.reserve(genStack.length() + 1);
     if (!ok)
         js_ReportOutOfMemory(this);
     return ok;
 }
 
 inline void
 JSContext::setPendingException(js::Value v) {
+    JS_ASSERT(!IsPoisonedValue(v));
     this->throwing = true;
     this->exception = v;
     js::assertSameCompartment(this, v);
 }
 
 inline bool
 JSContext::ensureParseMapPool()
 {
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -205,17 +205,17 @@ JSCompartment::wrap(JSContext *cx, Value
 
     /*
      * Wrappers should really be parented to the wrapped parent of the wrapped
      * object, but in that case a wrapped global object would have a NULL
      * parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead,
      * we parent all wrappers to the global object in their home compartment.
      * This loses us some transparency, and is generally very cheesy.
      */
-    JSObject *global;
+    RootedVarObject global(cx);
     if (cx->hasfp()) {
         global = &cx->fp()->global();
     } else {
         global = JS_ObjectToInnerObject(cx, cx->globalObject);
         if (!global)
             return false;
     }
 
@@ -266,121 +266,121 @@ JSCompartment::wrap(JSContext *cx, Value
         }
 #endif
     }
 
     /* If we already have a wrapper for this value, use it. */
     if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(*vp)) {
         *vp = p->value;
         if (vp->isObject()) {
-            JSObject *obj = &vp->toObject();
+            RootedVarObject obj(cx, &vp->toObject());
             JS_ASSERT(obj->isCrossCompartmentWrapper());
             if (global->getClass() != &dummy_class && obj->getParent() != global) {
                 do {
-                    if (!obj->setParent(cx, global))
+                    if (!JSObject::setParent(cx, obj, global))
                         return false;
                     obj = obj->getProto();
                 } while (obj && obj->isCrossCompartmentWrapper());
             }
         }
         return true;
     }
 
     if (vp->isString()) {
-        Value orig = *vp;
+        RootedVarValue orig(cx, *vp);
         JSString *str = vp->toString();
         const jschar *chars = str->getChars(cx);
         if (!chars)
             return false;
         JSString *wrapped = js_NewStringCopyN(cx, chars, str->length());
         if (!wrapped)
             return false;
         vp->setString(wrapped);
         return crossCompartmentWrappers.put(orig, *vp);
     }
 
-    JSObject *obj = &vp->toObject();
+    RootedVarObject obj(cx, &vp->toObject());
 
     /*
      * Recurse to wrap the prototype. Long prototype chains will run out of
      * stack, causing an error in CHECK_RECURSE.
      *
      * Wrapping the proto before creating the new wrapper and adding it to the
      * cache helps avoid leaving a bad entry in the cache on OOM. But note that
      * if we wrapped both proto and parent, we would get infinite recursion
      * here (since Object.prototype->parent->proto leads to Object.prototype
      * itself).
      */
-    JSObject *proto = obj->getProto();
-    if (!wrap(cx, &proto))
+    RootedVarObject proto(cx, obj->getProto());
+    if (!wrap(cx, proto.address()))
         return false;
 
     /*
      * We hand in the original wrapped object into the wrap hook to allow
      * the wrap hook to reason over what wrappers are currently applied
      * to the object.
      */
-    JSObject *wrapper = cx->runtime->wrapObjectCallback(cx, obj, proto, global, flags);
+    RootedVarObject wrapper(cx, cx->runtime->wrapObjectCallback(cx, obj, proto, global, flags));
     if (!wrapper)
         return false;
 
     vp->setObject(*wrapper);
 
     if (wrapper->getProto() != proto && !SetProto(cx, wrapper, proto, false))
         return false;
 
     if (!crossCompartmentWrappers.put(GetProxyPrivate(wrapper), *vp))
         return false;
 
-    if (!wrapper->setParent(cx, global))
+    if (!JSObject::setParent(cx, wrapper, global))
         return false;
     return true;
 }
 
 bool
 JSCompartment::wrap(JSContext *cx, JSString **strp)
 {
-    AutoValueRooter tvr(cx, StringValue(*strp));
-    if (!wrap(cx, tvr.addr()))
+    RootedVarValue value(cx, StringValue(*strp));
+    if (!wrap(cx, value.address()))
         return false;
-    *strp = tvr.value().toString();
+    *strp = value.reference().toString();
     return true;
 }
 
 bool
 JSCompartment::wrap(JSContext *cx, HeapPtrString *strp)
 {
-    AutoValueRooter tvr(cx, StringValue(*strp));
-    if (!wrap(cx, tvr.addr()))
+    RootedVarValue value(cx, StringValue(*strp));
+    if (!wrap(cx, value.address()))
         return false;
-    *strp = tvr.value().toString();
+    *strp = value.reference().toString();
     return true;
 }
 
 bool
 JSCompartment::wrap(JSContext *cx, JSObject **objp)
 {
     if (!*objp)
         return true;
-    AutoValueRooter tvr(cx, ObjectValue(**objp));
-    if (!wrap(cx, tvr.addr()))
+    RootedVarValue value(cx, ObjectValue(**objp));
+    if (!wrap(cx, value.address()))
         return false;
-    *objp = &tvr.value().toObject();
+    *objp = &value.reference().toObject();
     return true;
 }
 
 bool
 JSCompartment::wrapId(JSContext *cx, jsid *idp)
 {
     if (JSID_IS_INT(*idp))
         return true;
-    AutoValueRooter tvr(cx, IdToValue(*idp));
-    if (!wrap(cx, tvr.addr()))
+    RootedVarValue value(cx, IdToValue(*idp));
+    if (!wrap(cx, value.address()))
         return false;
-    return ValueToId(cx, tvr.value(), idp);
+    return ValueToId(cx, value.reference(), idp);
 }
 
 bool
 JSCompartment::wrap(JSContext *cx, PropertyOp *propp)
 {
     Value v = CastAsObjectJsval(*propp);
     if (!wrap(cx, &v))
         return false;
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -165,16 +165,17 @@ typedef HashSet<ScriptFilenameEntry *,
 /* If HashNumber grows, need to change WrapperHasher. */
 JS_STATIC_ASSERT(sizeof(HashNumber) == 4);
 
 struct WrapperHasher
 {
     typedef Value Lookup;
 
     static HashNumber hash(Value key) {
+        JS_ASSERT(!IsPoisonedValue(key));
         uint64_t bits = JSVAL_TO_IMPL(key).asBits;
         return uint32_t(bits) ^ uint32_t(bits >> 32);
     }
 
     static bool match(const Value &l, const Value &k) { return l == k; }
 };
 
 typedef HashMap<Value, ReadBarrieredValue, WrapperHasher, SystemAllocPolicy> WrapperMap;
@@ -577,20 +578,20 @@ class AutoCompartment
 /*
  * Use this to change the behavior of an AutoCompartment slightly on error. If
  * the exception happens to be an Error object, copy it to the origin compartment
  * instead of wrapping it.
  */
 class ErrorCopier
 {
     AutoCompartment &ac;
-    JSObject *scope;
+    RootedVarObject scope;
 
   public:
-    ErrorCopier(AutoCompartment &ac, JSObject *scope) : ac(ac), scope(scope) {
+    ErrorCopier(AutoCompartment &ac, JSObject *scope) : ac(ac), scope(ac.context, scope) {
         JS_ASSERT(scope->compartment() == ac.origin);
     }
     ~ErrorCopier();
 };
 
 class CompartmentsIter {
   private:
     JSCompartment **it, **end;
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -1729,17 +1729,17 @@ date_getTimezoneOffset(JSContext *cx, un
 }
 
 static JSBool
 date_setTime(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     bool ok;
-    JSObject *obj = NonGenericMethodGuard(cx, args, date_setTime, &DateClass, &ok);
+    RootedVarObject obj(cx, NonGenericMethodGuard(cx, args, date_setTime, &DateClass, &ok));
     if (!obj)
         return ok;
 
     if (args.length() == 0) {
         SetDateToNaN(cx, obj, &args.rval());
         return true;
     }
 
@@ -1751,17 +1751,17 @@ date_setTime(JSContext *cx, unsigned arg
 }
 
 static JSBool
 date_makeTime(JSContext *cx, Native native, unsigned maxargs, JSBool local, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     bool ok;
-    JSObject *obj = NonGenericMethodGuard(cx, args, native, &DateClass, &ok);
+    RootedVarObject obj(cx, NonGenericMethodGuard(cx, args, native, &DateClass, &ok));
     if (!obj)
         return ok;
 
     double result = obj->getDateUTCTime().toNumber();
 
     /*
      * Satisfy the ECMA rule that if a function is called with
      * fewer arguments than the specified formal arguments, the
@@ -1895,17 +1895,17 @@ date_setUTCHours(JSContext *cx, unsigned
 }
 
 static JSBool
 date_makeDate(JSContext *cx, Native native, unsigned maxargs, JSBool local, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     bool ok;
-    JSObject *obj = NonGenericMethodGuard(cx, args, native, &DateClass, &ok);
+    RootedVarObject obj(cx, NonGenericMethodGuard(cx, args, native, &DateClass, &ok));
     if (!obj)
         return ok;
 
     double result = obj->getDateUTCTime().toNumber();
 
     /* See complaint about ECMA in date_makeTime. */
     if (args.length() == 0) {
         SetDateToNaN(cx, obj, &args.rval());
@@ -2013,17 +2013,17 @@ date_setUTCFullYear(JSContext *cx, unsig
 }
 
 static JSBool
 date_setYear(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     bool ok;
-    JSObject *obj = NonGenericMethodGuard(cx, args, date_setYear, &DateClass, &ok);
+    RootedVarObject obj(cx, NonGenericMethodGuard(cx, args, date_setYear, &DateClass, &ok));
     if (!obj)
         return ok;
 
     if (args.length() == 0) {
         /* Call this only after verifying that obj.[[Class]] = "Date". */
         SetDateToNaN(cx, obj, &args.rval());
         return true;
     }
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -254,43 +254,44 @@ JS_ClearInterrupt(JSRuntime *rt, JSInter
     rt->debugHooks.interruptHook = 0;
     rt->debugHooks.interruptHookData = 0;
     return JS_TRUE;
 }
 
 /************************************************************************/
 
 JS_PUBLIC_API(JSBool)
-JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsid id,
-                 JSWatchPointHandler handler, JSObject *closure)
+JS_SetWatchPoint(JSContext *cx, JSObject *obj_, jsid id,
+                 JSWatchPointHandler handler, JSObject *closure_)
 {
-    assertSameCompartment(cx, obj);
+    assertSameCompartment(cx, obj_);
     id = js_CheckForStringIndex(id);
 
+    RootedVarObject obj(cx, obj_), closure(cx, closure_);
+
     JSObject *origobj;
     Value v;
     unsigned attrs;
-    jsid propid;
 
     origobj = obj;
-    OBJ_TO_INNER_OBJECT(cx, obj);
+    OBJ_TO_INNER_OBJECT(cx, obj.reference());
     if (!obj)
         return false;
 
-    AutoValueRooter idroot(cx);
+    RootedVarId propid(cx);
+
     if (JSID_IS_INT(id)) {
         propid = id;
     } else if (JSID_IS_OBJECT(id)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH_PROP);
         return false;
     } else {
-        if (!js_ValueToStringId(cx, IdToValue(id), &propid))
+        if (!js_ValueToStringId(cx, IdToValue(id), propid.address()))
             return false;
         propid = js_CheckForStringIndex(propid);
-        idroot.set(IdToValue(propid));
     }
 
     /*
      * If, by unwrapping and innerizing, we changed the object, check
      * again to make sure that we're allowed to set a watch point.
      */
     if (origobj != obj && !CheckAccess(cx, obj, propid, JSACC_WATCH, &v, &attrs))
         return false;
@@ -733,17 +734,19 @@ JS_PUBLIC_API(JSBool)
 JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fpArg,
                           const jschar *chars, unsigned length,
                           const char *filename, unsigned lineno,
                           jsval *rval)
 {
     if (!CheckDebugMode(cx))
         return false;
 
-    Env *env = JS_GetFrameScopeChain(cx, fpArg);
+    SkipRoot skip(cx, &chars);
+
+    RootedVar<Env*> env(cx, JS_GetFrameScopeChain(cx, fpArg));
     if (!env)
         return false;
 
     js::AutoCompartment ac(cx, env);
     if (!ac.enter())
         return false;
 
     StackFrame *fp = Valueify(fpArg);
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -1086,17 +1086,17 @@ js_ErrorToException(JSContext *cx, const
     JS_SetPendingException(cx, OBJECT_TO_JSVAL(errObject));
 
     /* Flag the error report passed in to indicate an exception was raised. */
     reportp->flags |= JSREPORT_EXCEPTION;
     return true;
 }
 
 static bool
-IsDuckTypedErrorObject(JSContext *cx, JSObject *exnObject, const char **filename_strp)
+IsDuckTypedErrorObject(JSContext *cx, HandleObject exnObject, const char **filename_strp)
 {
     JSBool found;
     if (!JS_HasProperty(cx, exnObject, js_message_str, &found) || !found)
         return false;
 
     const char *filename_str = *filename_strp;
     if (!JS_HasProperty(cx, exnObject, filename_str, &found) || !found) {
         /* DOMException duck quacks "filename" (all lowercase) */
@@ -1110,20 +1110,19 @@ IsDuckTypedErrorObject(JSContext *cx, JS
 
     *filename_strp = filename_str;
     return true;
 }
 
 JSBool
 js_ReportUncaughtException(JSContext *cx)
 {
-    JSObject *exnObject;
     jsval roots[6];
     JSErrorReport *reportp, report;
-    JSString *str;
+    RootedVarString str(cx);
 
     if (!JS_IsExceptionPending(cx))
         return true;
 
     RootedVarValue exn(cx);
     if (!JS_GetPendingException(cx, exn.address()))
         return false;
 
@@ -1131,16 +1130,17 @@ js_ReportUncaughtException(JSContext *cx
     AutoArrayRooter tvr(cx, ArrayLength(roots), roots);
 
     /*
      * Because ToString below could error and an exception object could become
      * unrooted, we must root exnObject.  Later, if exnObject is non-null, we
      * need to root other intermediates, so allocate an operand stack segment
      * to protect all of these values.
      */
+    RootedVarObject exnObject(cx);
     if (JSVAL_IS_PRIMITIVE(exn)) {
         exnObject = NULL;
     } else {
         exnObject = JSVAL_TO_OBJECT(exn);
         roots[0] = exn;
     }
 
     JS_ClearPendingException(cx);
@@ -1152,24 +1152,24 @@ js_ReportUncaughtException(JSContext *cx
         roots[1] = StringValue(str);
 
     const char *filename_str = js_fileName_str;
     JSAutoByteString filename;
     if (!reportp && exnObject &&
         (exnObject->isError() ||
          IsDuckTypedErrorObject(cx, exnObject, &filename_str)))
     {
-        JSString *name = NULL;
+        RootedVarString name(cx);
         if (JS_GetProperty(cx, exnObject, js_name_str, &roots[2]) &&
             JSVAL_IS_STRING(roots[2]))
         {
             name = JSVAL_TO_STRING(roots[2]);
         }
 
-        JSString *msg = NULL;
+        RootedVarString msg(cx);
         if (JS_GetProperty(cx, exnObject, js_message_str, &roots[3]) &&
             JSVAL_IS_STRING(roots[3]))
         {
             msg = JSVAL_TO_STRING(roots[3]);
         }
 
         if (name && msg) {
             JSString *colon = JS_NewStringCopyZ(cx, ": ");
@@ -1229,17 +1229,17 @@ js_ReportUncaughtException(JSContext *cx
         js_ReportErrorAgain(cx, bytes, reportp);
         JS_ClearPendingException(cx);
     }
 
     return true;
 }
 
 extern JSObject *
-js_CopyErrorObject(JSContext *cx, JSObject *errobj, JSObject *scope)
+js_CopyErrorObject(JSContext *cx, HandleObject errobj, HandleObject scope)
 {
     assertSameCompartment(cx, scope);
     JSExnPrivate *priv = GetExnPrivate(errobj);
 
     size_t size = offsetof(JSExnPrivate, stackElems) +
                   priv->stackDepth * sizeof(JSStackTraceElem);
 
     JSExnPrivate *copy = (JSExnPrivate *)cx->malloc_(size);
--- a/js/src/jsexn.h
+++ b/js/src/jsexn.h
@@ -94,17 +94,17 @@ js_GetLocalizedErrorMessage(JSContext* c
  * Make a copy of errobj parented to scope.
  *
  * cx must be in the same compartment as scope. errobj may be in a different
  * compartment, but it must be an Error object (not a wrapper of one) and it
  * must not be one of the prototype objects created by js_InitExceptionClasses
  * (errobj->getPrivate() must not be NULL).
  */
 extern JSObject *
-js_CopyErrorObject(JSContext *cx, JSObject *errobj, JSObject *scope);
+js_CopyErrorObject(JSContext *cx, js::HandleObject errobj, js::HandleObject scope);
 
 static JS_INLINE JSProtoKey
 GetExceptionProtoKey(int exn)
 {
     JS_ASSERT(JSEXN_ERR <= exn);
     JS_ASSERT(exn < JSEXN_LIMIT);
     return JSProtoKey(JSProto_Error + exn);
 }
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -182,17 +182,17 @@ JS_WrapPropertyDescriptor(JSContext *cx,
 
 JS_FRIEND_API(void)
 JS_TraceShapeCycleCollectorChildren(JSTracer *trc, void *shape)
 {
     MarkCycleCollectorChildren(trc, (Shape *)shape);
 }
 
 static bool
-DefineHelpProperty(JSContext *cx, JSObject *obj, const char *prop, const char *value)
+DefineHelpProperty(JSContext *cx, HandleObject obj, const char *prop, const char *value)
 {
     JSAtom *atom = js_Atomize(cx, value, strlen(value));
     if (!atom)
         return false;
     jsval v = STRING_TO_JSVAL(atom);
     return JS_DefineProperty(cx, obj, prop, v,
                              JS_PropertyStub, JS_StrictPropertyStub,
                              JSPROP_READONLY | JSPROP_PERMANENT);
@@ -207,18 +207,19 @@ JS_DefineFunctionsWithHelp(JSContext *cx
 
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     for (; fs->name; fs++) {
         JSAtom *atom = js_Atomize(cx, fs->name, strlen(fs->name));
         if (!atom)
             return false;
 
-        JSFunction *fun = js_DefineFunction(cx, objRoot,
-                                            ATOM_TO_JSID(atom), fs->call, fs->nargs, fs->flags);
+        RootedVarFunction fun(cx);
+        fun = js_DefineFunction(cx, objRoot,
+                                ATOM_TO_JSID(atom), fs->call, fs->nargs, fs->flags);
         if (!fun)
             return false;
 
         if (fs->usage) {
             if (!DefineHelpProperty(cx, fun, "usage", fs->usage))
                 return false;
         }
 
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -641,17 +641,17 @@ js_fun_call(JSContext *cx, unsigned argc
     return ok;
 }
 
 /* ES5 15.3.4.3 */
 JSBool
 js_fun_apply(JSContext *cx, unsigned argc, Value *vp)
 {
     /* Step 1. */
-    Value fval = vp[1];
+    RootedVarValue fval(cx, vp[1]);
     if (!js_IsCallable(fval)) {
         ReportIncompatibleMethod(cx, CallReceiverFromVp(vp), &FunctionClass);
         return false;
     }
 
     /* Step 2. */
     if (argc < 2 || vp[3].isNullOrUndefined())
         return js_fun_call(cx, (argc > 0) ? 1 : 0, vp);
@@ -683,17 +683,17 @@ js_fun_apply(JSContext *cx, unsigned arg
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_APPLY_ARGS, js_apply_str);
             return false;
         }
 
         /*
          * Steps 4-5 (note erratum removing steps originally numbered 5 and 7 in
          * original version of ES5).
          */
-        JSObject *aobj = &vp[3].toObject();
+        RootedVarObject aobj(cx, &vp[3].toObject());
         uint32_t length;
         if (!js_GetLengthProperty(cx, aobj, &length))
             return false;
 
         /* Step 6. */
         if (length > StackSpace::ARGS_LENGTH_MAX) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TOO_MANY_FUN_APPLY_ARGS);
             return false;
@@ -727,39 +727,41 @@ CallOrConstructBoundFunction(JSContext *
 }
 
 static const uint32_t JSSLOT_BOUND_FUNCTION_THIS       = 0;
 static const uint32_t JSSLOT_BOUND_FUNCTION_ARGS_COUNT = 1;
 
 static const uint32_t BOUND_FUNCTION_RESERVED_SLOTS = 2;
 
 inline bool
-JSFunction::initBoundFunction(JSContext *cx, const Value &thisArg,
+JSFunction::initBoundFunction(JSContext *cx, HandleValue thisArg,
                               const Value *args, unsigned argslen)
 {
     JS_ASSERT(isFunction());
 
+    RootedVarFunction self(cx, this);
+
     /*
      * Convert to a dictionary to set the BOUND_FUNCTION flag and increase
      * the slot span to cover the arguments and additional slots for the 'this'
      * value and arguments count.
      */
-    if (!toDictionaryMode(cx))
+    if (!self->toDictionaryMode(cx))
         return false;
 
-    if (!setFlag(cx, BaseShape::BOUND_FUNCTION))
+    if (!self->setFlag(cx, BaseShape::BOUND_FUNCTION))
         return false;
 
-    if (!setSlotSpan(cx, BOUND_FUNCTION_RESERVED_SLOTS + argslen))
+    if (!self->setSlotSpan(cx, BOUND_FUNCTION_RESERVED_SLOTS + argslen))
         return false;
 
-    setSlot(JSSLOT_BOUND_FUNCTION_THIS, thisArg);
-    setSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT, PrivateUint32Value(argslen));
+    self->setSlot(JSSLOT_BOUND_FUNCTION_THIS, thisArg);
+    self->setSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT, PrivateUint32Value(argslen));
 
-    initSlotRange(BOUND_FUNCTION_RESERVED_SLOTS, args, argslen);
+    self->initSlotRange(BOUND_FUNCTION_RESERVED_SLOTS, args, argslen);
 
     return true;
 }
 
 inline JSObject *
 JSFunction::getBoundFunctionTarget() const
 {
     JS_ASSERT(isFunction());
@@ -890,50 +892,50 @@ fun_bind(JSContext *cx, unsigned argc, V
     Value *boundArgs = NULL;
     unsigned argslen = 0;
     if (args.length() > 1) {
         boundArgs = args.array() + 1;
         argslen = args.length() - 1;
     }
 
     /* Steps 7-9. */
-    Value thisArg = args.length() >= 1 ? args[0] : UndefinedValue();
+    RootedVarValue thisArg(cx, args.length() >= 1 ? args[0] : UndefinedValue());
 
     JSObject *boundFunction = js_fun_bind(cx, target, thisArg, boundArgs, argslen);
     if (!boundFunction)
         return false;
 
     /* Step 22. */
     args.rval().setObject(*boundFunction);
     return true;
 }
 
 JSObject*
-js_fun_bind(JSContext *cx, HandleObject target, Value thisArg,
+js_fun_bind(JSContext *cx, HandleObject target, HandleValue thisArg,
             Value *boundArgs, unsigned argslen)
 {
     /* Steps 15-16. */
     unsigned length = 0;
     if (target->isFunction()) {
         unsigned nargs = target->toFunction()->nargs;
         if (nargs > argslen)
             length = nargs - argslen;
     }
 
     /* Step 4-6, 10-11. */
     JSAtom *name = target->isFunction() ? target->toFunction()->atom.get() : NULL;
 
-    JSObject *funobj =
-        js_NewFunction(cx, NULL, CallOrConstructBoundFunction, length,
-                       JSFUN_CONSTRUCTOR, target, name);
+    RootedVarObject funobj(cx);
+    funobj = js_NewFunction(cx, NULL, CallOrConstructBoundFunction, length,
+                            JSFUN_CONSTRUCTOR, target, name);
     if (!funobj)
         return NULL;
 
     /* NB: Bound functions abuse |parent| to store their target. */
-    if (!funobj->setParent(cx, target))
+    if (!JSObject::setParent(cx, funobj, target))
         return NULL;
 
     if (!funobj->toFunction()->initBoundFunction(cx, thisArg, boundArgs, argslen))
         return NULL;
 
     /* Steps 17, 19-21 are handled by fun_resolve. */
     /* Step 18 is the default for new functions. */
     return funobj;
@@ -978,16 +980,17 @@ Function(JSContext *cx, unsigned argc, V
     RootedVar<GlobalObject*> global(cx);
     global = &args.callee().global();
     if (!global->isRuntimeCodeGenEnabled(cx)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CSP_BLOCKED_FUNCTION);
         return false;
     }
 
     Bindings bindings(cx);
+    Bindings::StackRoot bindingsRoot(cx, &bindings);
 
     const char *filename;
     unsigned lineno;
     JSPrincipals *originPrincipals;
     CurrentScriptFileLineOrigin(cx, &filename, &lineno, &originPrincipals);
     JSPrincipals *principals = PrincipalsForCompiledCode(args, cx);
 
     unsigned n = args.length() ? args.length() - 1 : 0;
@@ -1107,16 +1110,18 @@ Function(JSContext *cx, unsigned argc, V
             }
         }
     }
 
     JS::Anchor<JSString *> strAnchor(NULL);
     const jschar *chars;
     size_t length;
 
+    SkipRoot skip(cx, &chars);
+
     if (args.length()) {
         JSString *str = ToString(cx, args[args.length() - 1]);
         if (!str)
             return false;
         strAnchor.set(str);
         chars = str->getChars(cx);
         length = str->length();
     } else {
@@ -1125,18 +1130,18 @@ Function(JSContext *cx, unsigned argc, V
     }
 
     /*
      * NB: (new Function) is not lexically closed by its caller, it's just an
      * anonymous function in the top-level scope that its constructor inhabits.
      * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42,
      * and so would a call to f from another top-level's script or function.
      */
-    JSFunction *fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_LAMBDA | JSFUN_INTERPRETED,
-                                     global, cx->runtime->atomState.anonymousAtom);
+    RootedVarFunction fun(cx, js_NewFunction(cx, NULL, NULL, 0, JSFUN_LAMBDA | JSFUN_INTERPRETED,
+                                             global, cx->runtime->atomState.anonymousAtom));
     if (!fun)
         return false;
 
     bool ok = frontend::CompileFunctionBody(cx, fun, principals, originPrincipals,
                                             &bindings, chars, length, filename, lineno,
                                             cx->findVersion());
     args.rval().setObject(*fun);
     return ok;
@@ -1224,17 +1229,17 @@ js_CloneFunctionObject(JSContext *cx, Ha
                        HandleObject proto, gc::AllocKind kind)
 {
     JS_ASSERT(parent);
     JS_ASSERT(proto);
 
     JSObject *cloneobj = NewObjectWithClassProto(cx, &FunctionClass, NULL, SkipScopeParent(parent), kind);
     if (!cloneobj)
         return NULL;
-    JSFunction *clone = static_cast<JSFunction *>(cloneobj);
+    RootedVarFunction clone(cx, static_cast<JSFunction *>(cloneobj));
 
     clone->nargs = fun->nargs;
     clone->flags = fun->flags & ~JSFUN_EXTENDED;
     if (fun->isInterpreted()) {
         clone->initScript(fun->script());
         clone->initEnvironment(parent);
     } else {
         clone->u.native = fun->native();
@@ -1349,17 +1354,16 @@ js_ValueToCallableObject(JSContext *cx, 
     js_ReportIsNotFunction(cx, vp, flags);
     return NULL;
 }
 
 void
 js_ReportIsNotFunction(JSContext *cx, const Value *vp, unsigned flags)
 {
     const char *name = NULL, *source = NULL;
-    AutoValueRooter tvr(cx);
     unsigned error = (flags & JSV2F_CONSTRUCT) ? JSMSG_NOT_CONSTRUCTOR : JSMSG_NOT_FUNCTION;
 
     /*
      * We try to the print the code that produced vp if vp is a value in the
      * most recent interpreted stack frame. Note that additional values, not
      * directly produced by the script, may have been pushed onto the frame's
      * expression stack (e.g. by pushInvokeArgs) thereby incrementing sp past
      * the depth simulated by ReconstructPCStack.
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -183,17 +183,17 @@ struct JSFunction : public JSObject
     static const js::gc::AllocKind ExtendedFinalizeKind = js::gc::FINALIZE_OBJECT8;
 # endif
 #endif
 
     inline void trace(JSTracer *trc);
 
     /* Bound function accessors. */
 
-    inline bool initBoundFunction(JSContext *cx, const js::Value &thisArg,
+    inline bool initBoundFunction(JSContext *cx, js::HandleValue thisArg,
                                   const js::Value *args, unsigned argslen);
 
     inline JSObject *getBoundFunctionTarget() const;
     inline const js::Value &getBoundFunctionThis() const;
     inline const js::Value &getBoundFunctionArgument(unsigned which) const;
     inline size_t getBoundFunctionArgumentCount() const;
 
   private:
@@ -318,12 +318,12 @@ XDRInterpretedFunction(XDRState<mode> *x
 
 extern JSBool
 js_fun_apply(JSContext *cx, unsigned argc, js::Value *vp);
 
 extern JSBool
 js_fun_call(JSContext *cx, unsigned argc, js::Value *vp);
 
 extern JSObject*
-js_fun_bind(JSContext *cx, js::HandleObject target, js::Value thisArg,
+js_fun_bind(JSContext *cx, js::HandleObject target, js::HandleValue thisArg,
             js::Value *boundArgs, unsigned argslen);
 
 #endif /* jsfun_h___ */
--- a/js/src/jsfuninlines.h
+++ b/js/src/jsfuninlines.h
@@ -233,17 +233,17 @@ CloneFunctionObjectIfNotSingleton(JSCont
     /*
      * For attempts to clone functions at a function definition opcode,
      * don't perform the clone if the function has singleton type. This
      * was called pessimistically, and we need to preserve the type's
      * property that if it is singleton there is only a single object
      * with its type in existence.
      */
     if (fun->hasSingletonType()) {
-        if (!fun->setParent(cx, SkipScopeParent(parent)))
+        if (!JSObject::setParent(cx, fun, RootedVarObject(cx, SkipScopeParent(parent))))
             return NULL;
         fun->setEnvironment(parent);
         return fun;
     }
 
     return CloneFunctionObject(cx, fun, parent);
 }
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -3937,30 +3937,32 @@ void
 SetDeterministicGC(JSContext *cx, bool enabled)
 {
 #ifdef JS_GC_ZEAL
     JSRuntime *rt = cx->runtime;
     rt->gcDeterministicOnly = enabled;
 #endif
 }
 
+} /* namespace gc */
+} /* namespace js */
+
 #if defined(DEBUG) && defined(JSGC_ROOT_ANALYSIS) && !defined(JS_THREADSAFE)
 
 static void
 CheckStackRoot(JSTracer *trc, uintptr_t *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);
 
     if (test == CGCT_VALID) {
-        JSContext *iter = NULL;
         bool matched = false;
         JSRuntime *rt = trc->runtime;
         for (ContextIter cx(rt); !cx.done(); cx.next()) {
             for (unsigned i = 0; i < THING_ROOT_LIMIT; i++) {
                 Root<void*> *rooter = cx->thingGCRooters[i];
                 while (rooter) {
                     if (rooter->address() == static_cast<void*>(w))
                         matched = true;
@@ -3989,47 +3991,90 @@ CheckStackRoot(JSTracer *trc, uintptr_t 
 static void
 CheckStackRootsRange(JSTracer *trc, uintptr_t *begin, uintptr_t *end)
 {
     JS_ASSERT(begin <= end);
     for (uintptr_t *i = begin; i != end; ++i)
         CheckStackRoot(trc, i);
 }
 
+static void
+EmptyMarkCallback(JSTracer *jstrc, void **thingp, JSGCTraceKind kind)
+{}
+
 void
-CheckStackRoots(JSContext *cx)
+JS::CheckStackRoots(JSContext *cx)
 {
-    AutoCopyFreeListToArenas copy(cx->runtime);
+    JSRuntime *rt = cx->runtime;
+
+    if (!rt->gcExactScanningEnabled)
+        return;
+
+    AutoCopyFreeListToArenas copy(rt);
 
     JSTracer checker;
-    JS_TracerInit(&checker, cx, EmptyMarkCallback);
-
-    ThreadData *td = JS_THREAD_DATA(cx);
-
-    ConservativeGCThreadData *ctd = &td->conservativeGC;
-    ctd->recordStackTop();
-
-    JS_ASSERT(ctd->hasStackToScan());
+    JS_TracerInit(&checker, rt, EmptyMarkCallback);
+
+    ConservativeGCData *cgcd = &rt->conservativeGC;
+    cgcd->recordStackTop();
+
+    JS_ASSERT(cgcd->hasStackToScan());
     uintptr_t *stackMin, *stackEnd;
 #if JS_STACK_GROWTH_DIRECTION > 0
     stackMin = rt->nativeStackBase;
     stackEnd = cgcd->nativeStackTop;
 #else
     stackMin = cgcd->nativeStackTop + 1;
     stackEnd = reinterpret_cast<uintptr_t *>(rt->nativeStackBase);
+
+    uintptr_t *&oldStackMin = cgcd->oldStackMin, *&oldStackEnd = cgcd->oldStackEnd;
+    uintptr_t *&oldStackData = cgcd->oldStackData;
+    uintptr_t &oldStackCapacity = cgcd->oldStackCapacity;
+
+    /*
+     * Adjust the stack to remove regions which have not changed since the
+     * stack was last scanned, and update the last scanned state.
+     */
+    if (stackEnd != oldStackEnd) {
+        rt->free_(oldStackData);
+        oldStackCapacity = rt->nativeStackQuota / sizeof(uintptr_t);
+        oldStackData = (uintptr_t *) rt->malloc_(oldStackCapacity * sizeof(uintptr_t));
+        if (!oldStackData) {
+            oldStackCapacity = 0;
+        } else {
+            uintptr_t *existing = stackEnd - 1, *copy = oldStackData;
+            while (existing >= stackMin && size_t(copy - oldStackData) < oldStackCapacity)
+                *copy++ = *existing--;
+            oldStackEnd = stackEnd;
+            oldStackMin = existing + 1;
+        }
+    } else {
+        uintptr_t *existing = stackEnd - 1, *copy = oldStackData;
+        while (existing >= stackMin && existing >= oldStackMin && *existing == *copy) {
+            copy++;
+            existing--;
+        }
+        stackEnd = existing + 1;
+        while (existing >= stackMin && size_t(copy - oldStackData) < oldStackCapacity)
+            *copy++ = *existing--;
+        oldStackMin = existing + 1;
+    }
 #endif
 
     JS_ASSERT(stackMin <= stackEnd);
     CheckStackRootsRange(&checker, stackMin, stackEnd);
     CheckStackRootsRange(&checker, cgcd->registerSnapshot.words,
                          ArrayEnd(cgcd->registerSnapshot.words));
 }
 
 #endif /* DEBUG && JSGC_ROOT_ANALYSIS && !JS_THREADSAFE */
 
+namespace js {
+namespace gc {
+
 #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
@@ -1967,25 +1967,16 @@ NewCompartment(JSContext *cx, JSPrincipa
 
 /* Tries to run a GC no matter what (used for GC zeal). */
 void
 RunDebugGC(JSContext *cx);
 
 void
 SetDeterministicGC(JSContext *cx, bool enabled);
 
-#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 ZealPokeValue = 1;
 const int ZealAllocValue = 2;
 const int ZealFrameGCValue = 3;
 const int ZealVerifierValue = 4;
 const int ZealFrameVerifierValue = 5;
 
 #ifdef JS_GC_ZEAL
 
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -442,17 +442,17 @@ NewGCThing(JSContext *cx, js::gc::AllocK
     /* For testing out of memory conditions */
     JS_OOM_POSSIBLY_FAIL_REPORT(cx);
 
 #ifdef JS_GC_ZEAL
     if (cx->runtime->needZealousGC())
         js::gc::RunDebugGC(cx);
 #endif
 
-    js::gc::MaybeCheckStackRoots(cx);
+    MaybeCheckStackRoots(cx);
 
     JSCompartment *comp = cx->compartment;
     void *t = comp->arenas.allocateFromFreeList(kind, thingSize);
     if (!t)
         t = js::gc::ArenaLists::refillFreeList(cx, kind);
 
     JS_ASSERT_IF(t && comp->needsBarrier(),
                  static_cast<T *>(t)->arenaHeader()->allocatedDuringIncremental);
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -5704,17 +5704,17 @@ JSObject::getNewType(JSContext *cx, JSFu
         type->flags |= OBJECT_FLAG_SETS_MARKED_UNKNOWN;
 
     return type;
 }
 
 TypeObject *
 JSCompartment::getLazyType(JSContext *cx, JSObject *proto)
 {
-    gc::MaybeCheckStackRoots(cx);
+    MaybeCheckStackRoots(cx);
 
     TypeObjectSet &table = cx->compartment->lazyTypeObjects;
 
     if (!table.initialized() && !table.init())
         return NULL;
 
     TypeObjectSet::AddPtr p = table.lookupForAdd(proto);
     if (p) {
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -148,24 +148,26 @@ js::GetScopeChain(JSContext *cx, StackFr
          * Don't force a call object for a lightweight function call, but do
          * insist that there is a call object for a heavyweight function call.
          */
         JS_ASSERT_IF(fp->isNonEvalFunctionFrame() && fp->fun()->isHeavyweight(),
                      fp->hasCallObj());
         return fp->scopeChain();
     }
 
+    Root<StaticBlockObject*> sharedBlockRoot(cx, &sharedBlock);
+
     /*
      * We have one or more lexical scopes to reflect into fp->scopeChain, so
      * make sure there's a call object at the current head of the scope chain,
      * if this frame is a call frame.
      *
      * Also, identify the innermost compiler-allocated block we needn't clone.
      */
-    JSObject *limitBlock, *limitClone;
+    RootedVarObject limitBlock(cx), limitClone(cx);
     if (fp->isNonEvalFunctionFrame() && !fp->hasCallObj()) {
         JS_ASSERT_IF(fp->scopeChain()->isClonedBlock(), fp->scopeChain()->getPrivate() != fp);
         if (!CallObject::createForFunction(cx, fp))
             return NULL;
 
         /* We know we must clone everything on blockChain. */
         limitBlock = limitClone = NULL;
     } else {
@@ -207,17 +209,17 @@ js::GetScopeChain(JSContext *cx, StackFr
 
     /*
      * Special-case cloning the innermost block; this doesn't have enough in
      * common with subsequent steps to include in the loop.
      *
      * create() leaves the clone's enclosingScope unset. We set it below.
      */
     RootedVar<ClonedBlockObject *> innermostNewChild(cx);
-    innermostNewChild = ClonedBlockObject::create(cx, *sharedBlock, fp);
+    innermostNewChild = ClonedBlockObject::create(cx, sharedBlockRoot, fp);
     if (!innermostNewChild)
         return NULL;
 
     /*
      * Clone our way towards outer scopes until we reach the innermost
      * enclosing function, or the innermost block we've already cloned.
      */
     RootedVar<ClonedBlockObject *> newChild(cx, innermostNewChild);
@@ -225,17 +227,17 @@ js::GetScopeChain(JSContext *cx, StackFr
         JS_ASSERT(newChild->getProto() == sharedBlock);
         sharedBlock = sharedBlock->enclosingBlock();
 
         /* Sometimes limitBlock will be NULL, so check that first.  */
         if (sharedBlock == limitBlock || !sharedBlock)
             break;
 
         /* As in the call above, we don't know the real parent yet.  */
-        RootedVar<ClonedBlockObject *> clone(cx, ClonedBlockObject::create(cx, *sharedBlock, fp));
+        RootedVar<ClonedBlockObject *> clone(cx, ClonedBlockObject::create(cx, sharedBlockRoot, fp));
         if (!clone)
             return NULL;
 
         if (!newChild->setEnclosingScope(cx, clone))
             return NULL;
         newChild = clone;
     }
     if (!newChild->setEnclosingScope(cx, fp->scopeChain()))
@@ -324,16 +326,17 @@ js::BoxNonStrictThis(JSContext *cx, cons
 
 #ifdef DEBUG
     JSFunction *fun = call.callee().isFunction() ? call.callee().toFunction() : NULL;
     JS_ASSERT_IF(fun && fun->isInterpreted(), !fun->inStrictMode());
 #endif
 
     if (thisv.isNullOrUndefined()) {
         JSObject *thisp = call.callee().global().thisObject(cx);
+        JS_ASSERT(!IsPoisonedPtr(thisp));
         if (!thisp)
             return false;
         call.thisv().setObject(*thisp);
         return true;
     }
 
     if (!thisv.isObject())
         return !!js_PrimitiveToObject(cx, &thisv);
@@ -363,41 +366,43 @@ Class js_NoSuchMethodClass = {
  *
  *   this.__noSuchMethod__(id, args)
  *
  * where id is the name of the method that this invocation attempted to
  * call by name, and args is an Array containing this invocation's actual
  * parameters.
  */
 bool
-js::OnUnknownMethod(JSContext *cx, HandleObject obj, Value idval, Value *vp)
+js::OnUnknownMethod(JSContext *cx, HandleObject obj, Value idval_, Value *vp)
 {
+    RootedVarValue idval(cx, idval_);
+
     jsid id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom);
-    AutoValueRooter tvr(cx);
-    if (!js_GetMethod(cx, obj, id, 0, tvr.addr()))
+    RootedVarValue value(cx);
+    if (!js_GetMethod(cx, obj, id, 0, value.address()))
         return false;
     TypeScript::MonitorUnknown(cx, cx->fp()->script(), cx->regs().pc);
 
-    if (tvr.value().isPrimitive()) {
-        *vp = tvr.value();
+    if (value.reference().isPrimitive()) {
+        *vp = value;
     } else {
 #if JS_HAS_XML_SUPPORT
         /* Extract the function name from function::name qname. */
-        if (idval.isObject()) {
-            JSObject *obj = &idval.toObject();
+        if (idval.reference().isObject()) {
+            JSObject *obj = &idval.reference().toObject();
             if (js_GetLocalNameFromFunctionQName(obj, &id, cx))
                 idval = IdToValue(id);
         }
 #endif
 
         JSObject *obj = NewObjectWithClassProto(cx, &js_NoSuchMethodClass, NULL, NULL);
         if (!obj)
             return false;
 
-        obj->setSlot(JSSLOT_FOUND_FUNCTION, tvr.value());
+        obj->setSlot(JSSLOT_FOUND_FUNCTION, value);
         obj->setSlot(JSSLOT_SAVED_ID, idval);
         vp->setObject(*obj);
     }
     return true;
 }
 
 static JSBool
 NoSuchMethod(JSContext *cx, unsigned argc, Value *vp)
@@ -445,19 +450,19 @@ js::RunScript(JSContext *cx, JSScript *s
             return false;
         }
     }
 
 #ifdef DEBUG
     struct CheckStackBalance {
         JSContext *cx;
         StackFrame *fp;
-        JSObject *enumerators;
+        RootedVarObject enumerators;
         CheckStackBalance(JSContext *cx)
-          : cx(cx), fp(cx->fp()), enumerators(cx->enumerators)
+          : cx(cx), fp(cx->fp()), enumerators(cx, cx->enumerators)
         {}
         ~CheckStackBalance() {
             JS_ASSERT(fp == cx->fp());
             JS_ASSERT_IF(!fp->isGeneratorFrame(), enumerators == cx->enumerators);
         }
     } check(cx);
 #endif
 
@@ -508,17 +513,17 @@ js::InvokeKernel(JSContext *cx, CallArgs
         if (!clasp->call) {
             js_ReportIsNotFunction(cx, &args.calleev(), ToReportFlags(initial));
             return false;
         }
         return CallJSNative(cx, clasp->call, args);
     }
 
     /* Invoke native functions. */
-    JSFunction *fun = callee.toFunction();
+    RootedVarFunction fun(cx, callee.toFunction());
     JS_ASSERT_IF(construct, !fun->isNativeConstructor());
     if (fun->isNative())
         return CallJSNative(cx, fun->native(), args);
 
     if (!TypeMonitorCall(cx, args, construct))
         return false;
 
     /* Get pointer to new frame/slots, prepare arguments. */
@@ -779,27 +784,27 @@ js::LooselyEqual(JSContext *cx, const Va
         return true;
     }
 
     if (rval.isNullOrUndefined()) {
         *result = false;
         return true;
     }
 
-    Value lvalue = lval;
-    Value rvalue = rval;
-
-    if (!ToPrimitive(cx, &lvalue))
+    RootedVarValue lvalue(cx, lval);
+    RootedVarValue rvalue(cx, rval);
+
+    if (!ToPrimitive(cx, lvalue.address()))
         return false;
-    if (!ToPrimitive(cx, &rvalue))
+    if (!ToPrimitive(cx, rvalue.address()))
         return false;
 
-    if (lvalue.isString() && rvalue.isString()) {
-        JSString *l = lvalue.toString();
-        JSString *r = rvalue.toString();
+    if (lvalue.reference().isString() && rvalue.reference().isString()) {
+        JSString *l = lvalue.reference().toString();
+        JSString *r = rvalue.reference().toString();
         return EqualStrings(cx, l, r, result);
     }
 
     double l, r;
     if (!ToNumber(cx, lvalue, &l) || !ToNumber(cx, rvalue, &r))
         return false;
     *result = (l == r);
     return true;
@@ -2045,17 +2050,18 @@ END_CASE(JSOP_AND)
 
 BEGIN_CASE(JSOP_IN)
 {
     const Value &rref = regs.sp[-1];
     if (!rref.isObject()) {
         js_ReportValueError(cx, JSMSG_IN_NOT_OBJECT, -1, rref, NULL);
         goto error;
     }
-    JSObject *obj = &rref.toObject();
+    RootedVarObject &obj = rootObject0;
+    obj = &rref.toObject();
     jsid id;
     FETCH_ELEMENT_ID(obj, -2, id);
     JSObject *obj2;
     JSProperty *prop;
     if (!obj->lookupGeneric(cx, id, &obj2, &prop))
         goto error;
     bool cond = prop != NULL;
     TRY_BRANCH_AFTER_COND(cond, 2);
@@ -2201,20 +2207,23 @@ BEGIN_CASE(JSOP_BINDNAME)
          * object reference, which also calls the hooks only after evaluating
          * the rhs. We desire such resolve hook equivalence between the two
          * forms.
          */
         obj = regs.fp()->scopeChain();
         if (obj->isGlobal())
             break;
 
-        PropertyName *name;
+        RootedVarPropertyName &name = rootName0;
         LOAD_NAME(0, name);
 
-        obj = FindIdentifierBase(cx, regs.fp()->scopeChain(), name);
+        RootedVarObject &scopeChain = rootObject0;
+        scopeChain = regs.fp()->scopeChain();
+
+        obj = FindIdentifierBase(cx, scopeChain, name);
         if (!obj)
             goto error;
     } while (0);
     PUSH_OBJECT(*obj);
 }
 END_CASE(JSOP_BINDNAME)
 
 #define BITWISE_OP(OP)                                                        \
@@ -2405,48 +2414,52 @@ BEGIN_CASE(JSOP_ADD)
     if (!AddOperation(cx, lval, rval, &regs.sp[-2]))
         goto error;
     regs.sp--;
 }
 END_CASE(JSOP_ADD)
 
 BEGIN_CASE(JSOP_SUB)
 {
-    Value lval = regs.sp[-2];
-    Value rval = regs.sp[-1];
+    RootedVarValue &lval = rootValue0, &rval = rootValue1;
+    lval = regs.sp[-2];
+    rval = regs.sp[-1];
     if (!SubOperation(cx, lval, rval, &regs.sp[-2]))
         goto error;
     regs.sp--;
 }
 END_CASE(JSOP_SUB)
 
 BEGIN_CASE(JSOP_MUL)
 {
-    Value lval = regs.sp[-2];
-    Value rval = regs.sp[-1];
+    RootedVarValue &lval = rootValue0, &rval = rootValue1;
+    lval = regs.sp[-2];
+    rval = regs.sp[-1];
     if (!MulOperation(cx, lval, rval, &regs.sp[-2]))
         goto error;
     regs.sp--;
 }
 END_CASE(JSOP_MUL)
 
 BEGIN_CASE(JSOP_DIV)
 {
-    Value lval = regs.sp[-2];
-    Value rval = regs.sp[-1];
+    RootedVarValue &lval = rootValue0, &rval = rootValue1;
+    lval = regs.sp[-2];
+    rval = regs.sp[-1];
     if (!DivOperation(cx, lval, rval, &regs.sp[-2]))
         goto error;
     regs.sp--;
 }
 END_CASE(JSOP_DIV)
 
 BEGIN_CASE(JSOP_MOD)
 {
-    Value lval = regs.sp[-2];
-    Value rval = regs.sp[-1];
+    RootedVarValue &lval = rootValue0, &rval = rootValue1;
+    lval = regs.sp[-2];
+    rval = regs.sp[-1];
     if (!ModOperation(cx, lval, rval, &regs.sp[-2]))
         goto error;
     regs.sp--;
 }
 END_CASE(JSOP_MOD)
 
 BEGIN_CASE(JSOP_NOT)
 {
@@ -2519,24 +2532,24 @@ BEGIN_CASE(JSOP_DELNAME)
         if (!obj->deleteProperty(cx, name, &regs.sp[-1], false))
             goto error;
     }
 }
 END_CASE(JSOP_DELNAME)
 
 BEGIN_CASE(JSOP_DELPROP)
 {
-    PropertyName *name;
+    RootedVarPropertyName &name = rootName0;
     LOAD_NAME(0, name);
 
     JSObject *obj;
     FETCH_OBJECT(cx, -1, obj);
 
-    Value rval;
-    if (!obj->deleteProperty(cx, name, &rval, script->strictModeCode))
+    RootedVarValue &rval = rootValue0;
+    if (!obj->deleteProperty(cx, name, rval.address(), script->strictModeCode))
         goto error;
 
     regs.sp[-1] = rval;
 }
 END_CASE(JSOP_DELPROP)
 
 BEGIN_CASE(JSOP_DELELEM)
 {
@@ -2685,37 +2698,39 @@ BEGIN_CASE(JSOP_CALLELEM)
         goto error;
     TypeScript::Monitor(cx, script, regs.pc, regs.sp[-2]);
     regs.sp--;
 }
 END_CASE(JSOP_GETELEM)
 
 BEGIN_CASE(JSOP_SETELEM)
 {
-    JSObject *obj;
+    RootedVarObject &obj = rootObject0;
     FETCH_OBJECT(cx, -3, obj);
     jsid id;
     FETCH_ELEMENT_ID(obj, -2, id);
     Value &value = regs.sp[-1];
     if (!SetObjectElementOperation(cx, obj, id, value, script->strictModeCode))
         goto error;
     regs.sp[-3] = value;
     regs.sp -= 2;
 }
 END_CASE(JSOP_SETELEM)
 
 BEGIN_CASE(JSOP_ENUMELEM)
 {
+    RootedVarObject &obj = rootObject0;
+    RootedVarValue &rval = rootValue0;
+
     /* Funky: the value to set is under the [obj, id] pair. */
-    JSObject *obj;
     FETCH_OBJECT(cx, -2, obj);
     jsid id;
     FETCH_ELEMENT_ID(obj, -1, id);
-    Value rval = regs.sp[-3];
-    if (!obj->setGeneric(cx, id, &rval, script->strictModeCode))
+    rval = regs.sp[-3];
+    if (!obj->setGeneric(cx, id, rval.address(), script->strictModeCode))
         goto error;
     regs.sp -= 3;
 }
 END_CASE(JSOP_ENUMELEM)
 
 BEGIN_CASE(JSOP_EVAL)
 {
     CallArgs args = CallArgsFromSp(GET_ARGC(regs.pc), regs.sp);
@@ -3154,17 +3169,16 @@ BEGIN_CASE(JSOP_DEFFUN)
     /*
      * A top-level function defined in Global or Eval code (see ECMA-262
      * Ed. 3), or else a SpiderMonkey extension: a named function statement in
      * a compound statement (not at the top statement level of global code, or
      * at the top level of a function body).
      */
     RootedVarFunction &fun = rootFunction0;
     fun = script->getFunction(GET_UINT32_INDEX(regs.pc));
-    JSObject *obj = fun;
 
     RootedVarObject &obj2 = rootObject0;
     if (fun->isNullClosure()) {
         /*
          * Even a null closure needs a parent for principals finding.
          * FIXME: bug 476950, although debugger users may also demand some kind
          * of scope link for debugger-assisted eval-in-frame.
          */
@@ -3179,46 +3193,48 @@ BEGIN_CASE(JSOP_DEFFUN)
      * If static link is not current scope, clone fun's object to link to the
      * current scope via parent. We do this to enable sharing of compiled
      * functions among multiple equivalent scopes, amortizing the cost of
      * compilation over a number of executions.  Examples include XUL scripts
      * and event handlers shared among Firefox or other Mozilla app chrome
      * windows, and user-defined JS functions precompiled and then shared among
      * requests in server-side JS.
      */
-    if (obj->toFunction()->environment() != obj2) {
-        obj = CloneFunctionObjectIfNotSingleton(cx, fun, obj2);
-        if (!obj)
+    if (fun->environment() != obj2) {
+        fun = CloneFunctionObjectIfNotSingleton(cx, fun, obj2);
+        if (!fun)
             goto error;
-        JS_ASSERT_IF(script->hasGlobal(), obj->getProto() == fun->getProto());
     }
 
     /*
      * ECMA requires functions defined when entering Eval code to be
      * impermanent.
      */
     unsigned attrs = regs.fp()->isEvalFrame()
                   ? JSPROP_ENUMERATE
                   : JSPROP_ENUMERATE | JSPROP_PERMANENT;
 
     /*
      * We define the function as a property of the variable object and not the
      * current scope chain even for the case of function expression statements
      * and functions defined by eval inside let or with blocks.
      */
-    JSObject *parent = &regs.fp()->varObj();
+    RootedVarObject &parent = rootObject0;
+    parent = &regs.fp()->varObj();
 
     /* ES5 10.5 (NB: with subsequent errata). */
-    PropertyName *name = fun->atom->asPropertyName();
+    RootedVarPropertyName &name = rootName0;
+    name = fun->atom->asPropertyName();
     JSProperty *prop = NULL;
     JSObject *pobj;
     if (!parent->lookupProperty(cx, name, &pobj, &prop))
         goto error;
 
-    Value rval = ObjectValue(*obj);
+    RootedVarValue &rval = rootValue0;
+    rval = ObjectValue(*fun);
 
     do {
         /* Steps 5d, 5f. */
         if (!prop || pobj != parent) {
             if (!parent->defineProperty(cx, name, rval,
                                         JS_PropertyStub, JS_StrictPropertyStub, attrs))
             {
                 goto error;
@@ -3252,17 +3268,17 @@ BEGIN_CASE(JSOP_DEFFUN)
         /*
          * Non-global properties, and global properties which we aren't simply
          * redefining, must be set.  First, this preserves their attributes.
          * Second, this will produce warnings and/or errors as necessary if the
          * specified Call object property is not writable (const).
          */
 
         /* Step 5f. */
-        if (!parent->setProperty(cx, name, &rval, script->strictModeCode))
+        if (!parent->setProperty(cx, name, rval.address(), script->strictModeCode))
             goto error;
     } while (false);
 }
 END_CASE(JSOP_DEFFUN)
 
 BEGIN_CASE(JSOP_LAMBDA)
 {
     /* Load the specified function object literal. */
@@ -3482,20 +3498,22 @@ BEGIN_CASE(JSOP_INITPROP)
 END_CASE(JSOP_INITPROP);
 
 BEGIN_CASE(JSOP_INITELEM)
 {
     /* Pop the element's value into rval. */
     JS_ASSERT(regs.sp - regs.fp()->base() >= 3);
     const Value &rref = regs.sp[-1];
 
+    RootedVarObject &obj = rootObject0;
+
     /* Find the object being initialized at top of stack. */
     const Value &lref = regs.sp[-3];
     JS_ASSERT(lref.isObject());
-    JSObject *obj = &lref.toObject();
+    obj = &lref.toObject();
 
     /* Fetch id now that we have obj. */
     jsid id;
     FETCH_ELEMENT_ID(obj, -2, id);
 
     /*
      * If rref is a hole, do not call JSObject::defineProperty. In this case,
      * obj must be an array, so if the current op is the last element
@@ -3632,16 +3650,18 @@ BEGIN_CASE(JSOP_DEFXMLNS)
     regs.sp--;
 }
 END_CASE(JSOP_DEFXMLNS)
 
 BEGIN_CASE(JSOP_ANYNAME)
 {
     JS_ASSERT(!script->strictModeCode);
 
+    cx->runtime->gcExactScanningEnabled = false;
+
     jsid id;
     if (!js_GetAnyName(cx, &id))
         goto error;
     PUSH_COPY(IdToValue(id));
 }
 END_CASE(JSOP_ANYNAME)
 
 BEGIN_CASE(JSOP_QNAMEPART)
@@ -3849,16 +3869,18 @@ BEGIN_CASE(JSOP_ENDFILTER)
     regs.sp--;
 }
 END_CASE(JSOP_ENDFILTER);
 
 BEGIN_CASE(JSOP_TOXML)
 {
     JS_ASSERT(!script->strictModeCode);
 
+    cx->runtime->gcExactScanningEnabled = false;
+
     Value rval = regs.sp[-1];
     JSObject *obj = js_ValueToXMLObject(cx, rval);
     if (!obj)
         goto error;
     regs.sp[-1].setObject(*obj);
 }
 END_CASE(JSOP_TOXML)
 
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -251,18 +251,17 @@ GetPropertyOperation(JSContext *cx, jsby
     if (!name) {
         AssertValidPropertyCacheHit(cx, obj, obj2, entry);
         if (!NativeGet(cx, obj, obj2, entry->prop, JSGET_CACHE_RESULT, vp))
             return false;
         return true;
     }
 
     RootObject objRoot(cx, &obj);
-
-    jsid id = ATOM_TO_JSID(name);
+    RootedVarId id(cx, ATOM_TO_JSID(name));
 
     if (obj->getOps()->getProperty) {
         if (!GetPropertyGenericMaybeCallXML(cx, op, objRoot, id, vp))
             return false;
     } else {
         if (!GetPropertyHelper(cx, objRoot, id, JSGET_CACHE_RESULT, vp))
             return false;
     }
@@ -605,58 +604,58 @@ AddOperation(JSContext *cx, const Value 
                 types::TypeScript::MonitorOverflow(cx);
             }
         }
     }
     return true;
 }
 
 static JS_ALWAYS_INLINE bool
-SubOperation(JSContext *cx, const Value &lhs, const Value &rhs, Value *res)
+SubOperation(JSContext *cx, HandleValue lhs, HandleValue rhs, Value *res)
 {
     double d1, d2;
     if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2))
         return false;
     double d = d1 - d2;
-    if (!res->setNumber(d) && !(lhs.isDouble() || rhs.isDouble()))
+    if (!res->setNumber(d) && !(lhs.value().isDouble() || rhs.value().isDouble()))
         types::TypeScript::MonitorOverflow(cx);
     return true;
 }
 
 static JS_ALWAYS_INLINE bool
-MulOperation(JSContext *cx, const Value &lhs, const Value &rhs, Value *res)
+MulOperation(JSContext *cx, HandleValue lhs, HandleValue rhs, Value *res)
 {
     double d1, d2;
     if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2))
         return false;
     double d = d1 * d2;
-    if (!res->setNumber(d) && !(lhs.isDouble() || rhs.isDouble()))
+    if (!res->setNumber(d) && !(lhs.value().isDouble() || rhs.value().isDouble()))
         types::TypeScript::MonitorOverflow(cx);
     return true;
 }
 
 static JS_ALWAYS_INLINE bool
-DivOperation(JSContext *cx, const Value &lhs, const Value &rhs, Value *res)
+DivOperation(JSContext *cx, HandleValue lhs, HandleValue rhs, Value *res)
 {
     double d1, d2;
     if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2))
         return false;
     res->setNumber(NumberDiv(d1, d2));
 
-    if (d2 == 0 || (res->isDouble() && !(lhs.isDouble() || rhs.isDouble())))
+    if (d2 == 0 || (res->isDouble() && !(lhs.value().isDouble() || rhs.value().isDouble())))
         types::TypeScript::MonitorOverflow(cx);
     return true;
 }
 
 static JS_ALWAYS_INLINE bool
-ModOperation(JSContext *cx, const Value &lhs, const Value &rhs, Value *res)
+ModOperation(JSContext *cx, HandleValue lhs, HandleValue rhs, Value *res)
 {
     int32_t l, r;
-    if (lhs.isInt32() && rhs.isInt32() &&
-        (l = lhs.toInt32()) >= 0 && (r = rhs.toInt32()) > 0) {
+    if (lhs.value().isInt32() && rhs.value().isInt32() &&
+        (l = lhs.value().toInt32()) >= 0 && (r = rhs.value().toInt32()) > 0) {
         int32_t mod = l % r;
         res->setInt32(mod);
         return true;
     }
 
     double d1, d2;
     if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2))
         return false;
@@ -833,16 +832,17 @@ SetObjectElementOperation(JSContext *cx,
 #define RELATIONAL_OP(OP)                                                     \
     JS_BEGIN_MACRO                                                            \
         Value lval = lhs;                                                     \
         Value rval = rhs;                                                     \
         /* Optimize for two int-tagged operands (typical loop control). */    \
         if (lval.isInt32() && rval.isInt32()) {                               \
             *res = lval.toInt32() OP rval.toInt32();                          \
         } else {                                                              \
+            RootValue lvalRoot(cx, &lval), rvalRoot(cx, &rval);               \
             if (!ToPrimitive(cx, JSTYPE_NUMBER, &lval))                       \
                 return false;                                                 \
             if (!ToPrimitive(cx, JSTYPE_NUMBER, &rval))                       \
                 return false;                                                 \
             if (lval.isString() && rval.isString()) {                         \
                 JSString *l = lval.toString(), *r = rval.toString();          \
                 int32_t result;                                               \
                 if (!CompareStrings(cx, l, r, &result))                       \
@@ -881,17 +881,17 @@ GreaterThanOrEqualOperation(JSContext *c
 #undef RELATIONAL_OP
 
 static inline bool
 GuardFunApplySpeculation(JSContext *cx, FrameRegs &regs)
 {
     if (regs.sp[-1].isMagic(JS_OPTIMIZED_ARGUMENTS)) {
         CallArgs args = CallArgsFromSp(GET_ARGC(regs.pc), regs.sp);
         if (!IsNativeFunction(args.calleev(), js_fun_apply)) {
-            if (!regs.fp()->script()->applySpeculationFailed(cx))
+            if (!JSScript::applySpeculationFailed(cx, regs.fp()->script()))
                 return false;
             args[1] = ObjectValue(regs.fp()->argsObj());
         }
     }
     return true;
 }
 
 }  /* namespace js */
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -948,17 +948,17 @@ js::CloseIterator(JSContext *cx, JSObjec
     }
 #endif
     return JS_TRUE;
 }
 
 bool
 js::UnwindIteratorForException(JSContext *cx, JSObject *obj)
 {
-    Value v = cx->getPendingException();
+    RootedVarValue v(cx, cx->getPendingException());
     cx->clearPendingException();
     if (!CloseIterator(cx, obj))
         return false;
     cx->setPendingException(v);
     return true;
 }
 
 void
@@ -987,17 +987,17 @@ js::UnwindIteratorForUncatchableExceptio
  *
  * This function can suppress multiple properties at once. The |predicate|
  * argument is an object which can be called on an id and returns true or
  * false. It also must have a method |matchesAtMostOne| which allows us to
  * stop searching after the first deletion if true.
  */
 template<typename StringPredicate>
 static bool
-SuppressDeletedPropertyHelper(JSContext *cx, JSObject *obj, StringPredicate predicate)
+SuppressDeletedPropertyHelper(JSContext *cx, HandleObject obj, StringPredicate predicate)
 {
     JSObject *iterobj = cx->enumerators;
     while (iterobj) {
       again:
         NativeIterator *ni = iterobj->getNativeIterator();
         /* This only works for identified surpressed keys, not values. */
         if (ni->isKeyIter() && ni->obj == obj && ni->props_cursor < ni->props_end) {
             /* Check whether id is still to come. */
@@ -1067,35 +1067,35 @@ SuppressDeletedPropertyHelper(JSContext 
             }
         }
         iterobj = ni->next;
     }
     return true;
 }
 
 class SingleStringPredicate {
-    JSFlatString *str;
+    Handle<JSFlatString*> str;
 public:
-    SingleStringPredicate(JSFlatString *str) : str(str) {}
+    SingleStringPredicate(JSContext *cx, Handle<JSFlatString*> str) : str(str) {}
 
     bool operator()(JSFlatString *str) { return EqualStrings(str, this->str); }
     bool matchesAtMostOne() { return true; }
 };
 
 bool
-js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id)
+js_SuppressDeletedProperty(JSContext *cx, HandleObject obj, jsid id)
 {
-    JSFlatString *str = IdToString(cx, id);
+    RootedVar<JSFlatString*> str(cx, IdToString(cx, id));
     if (!str)
         return false;
-    return SuppressDeletedPropertyHelper(cx, obj, SingleStringPredicate(str));
+    return SuppressDeletedPropertyHelper(cx, obj, SingleStringPredicate(cx, str));
 }
 
 bool
-js_SuppressDeletedElement(JSContext *cx, JSObject *obj, uint32_t index)
+js_SuppressDeletedElement(JSContext *cx, HandleObject obj, uint32_t index)
 {
     jsid id;
     if (!IndexToId(cx, index, &id))
         return false;
     return js_SuppressDeletedProperty(cx, obj, id);
 }
 
 class IndexRangePredicate {
@@ -1108,25 +1108,25 @@ class IndexRangePredicate {
         uint32_t index;
         return str->isIndex(&index) && begin <= index && index < end;
     }
 
     bool matchesAtMostOne() { return false; }
 };
 
 bool
-js_SuppressDeletedElements(JSContext *cx, JSObject *obj, uint32_t begin, uint32_t end)
+js_SuppressDeletedElements(JSContext *cx, HandleObject obj, uint32_t begin, uint32_t end)
 {
     return SuppressDeletedPropertyHelper(cx, obj, IndexRangePredicate(begin, end));
 }
 
 const uint32_t CLOSED_INDEX = UINT32_MAX;
 
 JSObject *
-ElementIteratorObject::create(JSContext *cx, JSObject *obj)
+ElementIteratorObject::create(JSContext *cx, HandleObject obj)
 {
     JS_ASSERT(obj);
     JSObject *iterobj = NewObjectWithGivenProto(cx, &ElementIteratorClass, NULL, obj);
     if (iterobj) {
         iterobj->setReservedSlot(TargetSlot, ObjectValue(*obj));
         iterobj->setReservedSlot(IndexSlot, Int32Value(0));
     }
     return iterobj;
@@ -1148,38 +1148,40 @@ inline void
 ElementIteratorObject::setIndex(uint32_t index)
 {
     setReservedSlot(IndexSlot, Int32Value(int32_t(index)));
 }
 
 bool
 ElementIteratorObject::iteratorNext(JSContext *cx, Value *vp)
 {
+    RootedVar<ElementIteratorObject*> self(cx, this);
+
     uint32_t i, length;
     RootedVarObject obj(cx, getTargetObject());
     if (!js_GetLengthProperty(cx, obj, &length))
         goto error;
 
-    i = getIndex();
+    i = self->getIndex();
     if (i >= length) {
-        setIndex(CLOSED_INDEX);
+        self->setIndex(CLOSED_INDEX);
         vp->setMagic(JS_NO_ITER_VALUE);
         return true;
     }
 
     JS_ASSERT(i + 1 > i);
     if (!obj->getElement(cx, obj, i, vp))
         goto error;
 
     /* On success, bump the index. */
-    setIndex(i + 1);
+    self->setIndex(i + 1);
     return true;
 
   error:
-    setIndex(CLOSED_INDEX);
+    self->setIndex(CLOSED_INDEX);
     return false;
 }
 
 inline js::ElementIteratorObject *
 JSObject::asElementIterator()
 {
     JS_ASSERT(isElementIterator());
     return static_cast<js::ElementIteratorObject *>(this);
@@ -1435,17 +1437,17 @@ Class js::GeneratorClass = {
 JSObject *
 js_NewGenerator(JSContext *cx)
 {
     FrameRegs &stackRegs = cx->regs();
     StackFrame *stackfp = stackRegs.fp();
     JS_ASSERT(stackfp->base() == cx->regs().sp);
     JS_ASSERT(stackfp->actualArgs() <= stackfp->formalArgs());
 
-    GlobalObject *global = &stackfp->global();
+    RootedVar<GlobalObject*> global(cx, &stackfp->global());
     JSObject *proto = global->getOrCreateGeneratorPrototype(cx);
     if (!proto)
         return NULL;
     JSObject *obj = NewObjectWithGivenProto(cx, &GeneratorClass, proto, global);
     if (!obj)
         return NULL;
 
     /* Load and compute stack slot counts. */
@@ -1723,59 +1725,60 @@ static JSFunctionSpec generator_methods[
     JS_FN(js_throw_str,     generator_throw,    1,JSPROP_ROPERM),
     JS_FN(js_close_str,     generator_close,    0,JSPROP_ROPERM),
     JS_FS_END
 };
 
 #endif /* JS_HAS_GENERATORS */
 
 static bool
-InitIteratorClass(JSContext *cx, GlobalObject *global)
+InitIteratorClass(JSContext *cx, Handle<GlobalObject*> global)
 {
-    JSObject *iteratorProto = global->createBlankPrototype(cx, &IteratorClass);
+    RootedVarObject iteratorProto(cx, global->createBlankPrototype(cx, &IteratorClass));
     if (!iteratorProto)
         return false;
 
     AutoIdVector blank(cx);
     NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, blank);
     if (!ni)
         return false;
     ni->init(NULL, 0 /* flags */, 0, 0);
 
     iteratorProto->setNativeIterator(ni);
 
-    JSFunction *ctor = global->createConstructor(cx, Iterator, CLASS_ATOM(cx, Iterator), 2);
+    RootedVarFunction ctor(cx);
+    ctor = global->createConstructor(cx, Iterator, CLASS_ATOM(cx, Iterator), 2);
     if (!ctor)
         return false;
 
     if (!LinkConstructorAndPrototype(cx, ctor, iteratorProto))
         return false;
 
     if (!DefinePropertiesAndBrand(cx, iteratorProto, NULL, iterator_methods))
         return false;
 
     return DefineConstructorAndPrototype(cx, global, JSProto_Iterator, ctor, iteratorProto);
 }
 
-bool
-GlobalObject::initGeneratorClass(JSContext *cx)
+/* static */ bool
+GlobalObject::initGeneratorClass(JSContext *cx, Handle<GlobalObject*> global)
 {
 #if JS_HAS_GENERATORS
-    JSObject *proto = createBlankPrototype(cx, &GeneratorClass);
+    RootedVarObject proto(cx, global->createBlankPrototype(cx, &GeneratorClass));
     if (!proto || !DefinePropertiesAndBrand(cx, proto, NULL, generator_methods))
         return false;
-    setReservedSlot(GENERATOR_PROTO, ObjectValue(*proto));
+    global->setReservedSlot(GENERATOR_PROTO, ObjectValue(*proto));
 #endif
     return true;
 }
 
 static JSObject *
-InitStopIterationClass(JSContext *cx, GlobalObject *global)
+InitStopIterationClass(JSContext *cx, Handle<GlobalObject*> global)
 {
-    JSObject *proto = global->createBlankPrototype(cx, &StopIterationClass);
+    RootedVarObject proto(cx, global->createBlankPrototype(cx, &StopIterationClass));
     if (!proto || !proto->freeze(cx))
         return NULL;
 
     /* This should use a non-JSProtoKey'd slot, but this is easier for now. */
     if (!DefineConstructorAndPrototype(cx, global, JSProto_StopIteration, proto, proto))
         return NULL;
 
     MarkStandardClassInitializedNoProto(global, &StopIterationClass);
@@ -1783,26 +1786,26 @@ InitStopIterationClass(JSContext *cx, Gl
     return proto;
 }
 
 JSObject *
 js_InitIteratorClasses(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isNative());
 
-    GlobalObject *global = &obj->asGlobal();
+    RootedVar<GlobalObject*> global(cx, &obj->asGlobal());
 
     /*
      * Bail if Iterator has already been initialized.  We test for Iterator
      * rather than for StopIteration because if js_InitIteratorClasses recurs,
      * as happens when the StopIteration object is frozen, initializing the
      * Iterator class a second time will assert.
      */
     JSObject *iter;
     if (!js_GetClassObject(cx, global, JSProto_Iterator, &iter))
         return NULL;
     if (iter)
         return iter;
 
-    if (!InitIteratorClass(cx, global) || !global->initGeneratorClass(cx))
+    if (!InitIteratorClass(cx, global) || !GlobalObject::initGeneratorClass(cx, global))
         return NULL;
     return InitStopIterationClass(cx, global);
 }
--- a/js/src/jsiter.h
+++ b/js/src/jsiter.h
@@ -104,17 +104,17 @@ struct NativeIterator {
 class ElementIteratorObject : public JSObject {
   public:
     enum {
         TargetSlot,
         IndexSlot,
         NumSlots
     };
 
-    static JSObject *create(JSContext *cx, JSObject *target);
+    static JSObject *create(JSContext *cx, HandleObject target);
 
     inline uint32_t getIndex() const;
     inline void setIndex(uint32_t index);
     inline JSObject *getTargetObject() const;
 
     /*
         Array iterators are like this:
 
@@ -197,23 +197,23 @@ extern bool
 UnwindIteratorForException(JSContext *cx, JSObject *obj);
 
 extern void
 UnwindIteratorForUncatchableException(JSContext *cx, JSObject *obj);
 
 }
 
 extern bool
-js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id);
+js_SuppressDeletedProperty(JSContext *cx, js::HandleObject obj, jsid id);
 
 extern bool
-js_SuppressDeletedElement(JSContext *cx, JSObject *obj, uint32_t index);
+js_SuppressDeletedElement(JSContext *cx, js::HandleObject obj, uint32_t index);
 
 extern bool
-js_SuppressDeletedElements(JSContext *cx, JSObject *obj, uint32_t begin, uint32_t end);
+js_SuppressDeletedElements(JSContext *cx, js::HandleObject obj, uint32_t begin, uint32_t end);
 
 /*
  * IteratorMore() indicates whether another value is available. It might
  * internally call iterobj.next() and then cache the value until its
  * picked up by IteratorNext(). The value is cached in the current context.
  */
 extern JSBool
 js_IteratorMore(JSContext *cx, js::HandleObject iterobj, js::Value *rval);
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -1223,16 +1223,23 @@ NumberValueToStringBuffer(JSContext *cx,
     size_t cstrlen = strlen(cstr);
     JS_ASSERT(!cbuf.dbuf && cstrlen < cbuf.sbufSize);
     return sb.appendInflated(cstr, cstrlen);
 }
 
 JS_PUBLIC_API(bool)
 ToNumberSlow(JSContext *cx, Value v, double *out)
 {
+#ifdef DEBUG
+    {
+        SkipRoot skip(cx, &v);
+        MaybeCheckStackRoots(cx);
+    }
+#endif
+
     JS_ASSERT(!v.isNumber());
     goto skip_int_double;
     for (;;) {
         if (v.isNumber()) {
             *out = v.toNumber();
             return true;
         }
       skip_int_double:
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -153,58 +153,60 @@ obj_getProto(JSContext *cx, JSObject *ob
     unsigned attrs;
     id = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
     return CheckAccess(cx, obj, id, JSACC_PROTO, vp, &attrs);
 }
 
 size_t sSetProtoCalled = 0;
 
 static JSBool
-obj_setProto(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
-{
+obj_setProto(JSContext *cx, JSObject *obj_, jsid id, JSBool strict, Value *vp)
+{
+    RootedVarObject obj(cx, obj_);
+
     if (!cx->runningWithTrustedPrincipals())
         ++sSetProtoCalled;
 
     /* ECMAScript 5 8.6.2 forbids changing [[Prototype]] if not [[Extensible]]. */
     if (!obj->isExtensible()) {
         obj->reportNotExtensible(cx);
         return false;
     }
 
     if (!vp->isObjectOrNull())
         return true;
 
-    JSObject *pobj = vp->toObjectOrNull();
+    RootedVarObject pobj(cx, vp->toObjectOrNull());
     unsigned attrs;
     id = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
     if (!CheckAccess(cx, obj, id, JSAccessMode(JSACC_PROTO|JSACC_WRITE), vp, &attrs))
         return false;
 
     return SetProto(cx, obj, pobj, true);
 }
 
 #else  /* !JS_HAS_OBJ_PROTO_PROP */
 
 #define object_props NULL
 
 #endif /* !JS_HAS_OBJ_PROTO_PROP */
 
 static bool
-MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap, JSSharpInfo *value)
+MarkSharpObjects(JSContext *cx, HandleObject obj, JSIdArray **idap, JSSharpInfo *value)
 {
     JS_CHECK_RECURSION(cx, return NULL);
 
     JSIdArray *ida;
 
     JSSharpObjectMap *map = &cx->sharpObjectMap;
     JS_ASSERT(map->depth >= 1);
     JSSharpInfo sharpid;
     JSSharpTable::Ptr p = map->table.lookup(obj);
     if (!p) {
-        if (!map->table.put(obj, sharpid))
+        if (!map->table.put(obj.value(), sharpid))
             return false;
 
         ida = JS_Enumerate(cx, obj);
         if (!ida)
             return false;
 
         bool ok = true;
         for (int i = 0, length = ida->length; i < length; i++) {
@@ -212,43 +214,45 @@ MarkSharpObjects(JSContext *cx, JSObject
             JSObject *obj2;
             JSProperty *prop;
             ok = obj->lookupGeneric(cx, id, &obj2, &prop);
             if (!ok)
                 break;
             if (!prop)
                 continue;
             bool hasGetter, hasSetter;
-            AutoValueRooter v(cx);
-            AutoValueRooter setter(cx);
+            Value value = UndefinedValue(), setter = UndefinedValue();
+            RootValue valueRoot(cx, &value), setterRoot(cx, &setter);
             if (obj2->isNative()) {
                 const Shape *shape = (Shape *) prop;
                 hasGetter = shape->hasGetterValue();
                 hasSetter = shape->hasSetterValue();
                 if (hasGetter)
-                    v.set(shape->getterValue());
+                    value = shape->getterValue();
                 if (hasSetter)
-                    setter.set(shape->setterValue());
+                    setter = shape->setterValue();
             } else {
                 hasGetter = hasSetter = false;
             }
             if (hasSetter) {
                 /* Mark the getter, then set val to setter. */
-                if (hasGetter && v.value().isObject()) {
-                    ok = MarkSharpObjects(cx, &v.value().toObject(), NULL, NULL);
+                if (hasGetter && value.isObject()) {
+                    ok = MarkSharpObjects(cx, RootedVarObject(cx, &value.toObject()), NULL, NULL);
                     if (!ok)
                         break;
                 }
-                v.set(setter.value());
+                value = setter;
             } else if (!hasGetter) {
-                ok = obj->getGeneric(cx, id, v.addr());
+                ok = obj->getGeneric(cx, id, &value);
                 if (!ok)
                     break;
             }
-            if (v.value().isObject() && !MarkSharpObjects(cx, &v.value().toObject(), NULL, NULL)) {
+            if (value.isObject() &&
+                !MarkSharpObjects(cx, RootedVarObject(cx, &value.toObject()), NULL, NULL))
+            {
                 ok = false;
                 break;
             }
         }
         if (!ok || !idap)
             JS_DestroyIdArray(cx, ida);
         if (!ok)
             return false;
@@ -262,17 +266,17 @@ MarkSharpObjects(JSContext *cx, JSObject
     if (idap)
         *idap = ida;
     if (value)
         *value = sharpid;
     return true;
 }
 
 bool
-js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, bool *alreadySeen, bool *isSharp)
+js_EnterSharpObject(JSContext *cx, HandleObject obj, JSIdArray **idap, bool *alreadySeen, bool *isSharp)
 {
     if (!JS_CHECK_OPERATION_LIMIT(cx))
         return false;
 
     *alreadySeen = false;
 
     JSSharpObjectMap *map = &cx->sharpObjectMap;
 
@@ -315,17 +319,17 @@ js_EnterSharpObject(JSContext *cx, JSObj
          * It's possible that the value of a property has changed from the
          * first time the object's properties are traversed (when the property
          * ids are entered into the hash table) to the second (when they are
          * converted to strings), i.e., the JSObject::getProperty() call is not
          * idempotent.
          */
         p = map->table.lookup(obj);
         if (!p) {
-            if (!map->table.put(obj, sharpid))
+            if (!map->table.put(obj.value(), sharpid))
                 goto bad;
             goto out;
         }
         sharpid = p->value;
     }
 
     if (sharpid.isSharp || sharpid.hasGen)
         *alreadySeen = true;
@@ -419,17 +423,17 @@ obj_toSource(JSContext *cx, unsigned arg
 
     Value localroot[4];
     PodArrayZero(localroot);
     AutoArrayRooter tvr(cx, ArrayLength(localroot), localroot);
 
     /* If outermost, we need parentheses to be an expression, not a block. */
     bool outermost = (cx->sharpObjectMap.depth == 0);
 
-    JSObject *obj = ToObject(cx, &vp[1]);
+    RootedVarObject obj(cx, ToObject(cx, &vp[1]));
     if (!obj)
         return false;
 
     JSIdArray *ida;
     bool alreadySeen = false;
     bool isSharp = false;
     if (!js_EnterSharpObject(cx, obj, &ida, &alreadySeen, &isSharp))
         return false;
@@ -933,17 +937,17 @@ EvalKernel(JSContext *cx, const CallArgs
     /* ES5 15.1.2.1 steps 2-8. */
 
     /*
      * Per ES5, indirect eval runs in the global scope. (eval is specified this
      * way so that the compiler can make assumptions about what bindings may or
      * may not exist in the current frame if it doesn't see 'eval'.)
      */
     unsigned staticLevel;
-    Value thisv;
+    RootedVarValue thisv(cx);
     if (evalType == DIRECT_EVAL) {
         staticLevel = caller->script()->staticLevel + 1;
 
         /*
          * Direct calls to eval are supposed to see the caller's |this|. If we
          * haven't wrapped that yet, do so now, before we make a copy of it for
          * the eval code to use.
          */
@@ -961,22 +965,24 @@ EvalKernel(JSContext *cx, const CallArgs
 
         /* Use the global as 'this', modulo outerization. */
         JSObject *thisobj = scopeobj->thisObject(cx);
         if (!thisobj)
             return false;
         thisv = ObjectValue(*thisobj);
     }
 
-    JSLinearString *linearStr = str->ensureLinear(cx);
+    RootedVar<JSLinearString*> linearStr(cx, str->ensureLinear(cx));
     if (!linearStr)
         return false;
     const jschar *chars = linearStr->chars();
     size_t length = linearStr->length();
 
+    SkipRoot skip(cx, &chars);
+
     /*
      * If the eval string starts with '(' or '[' and ends with ')' or ']', it may be JSON.
      * Try the JSON parser first because it's much faster.  If the eval string
      * isn't JSON, JSON parsing will probably fail quickly, so little time
      * will be lost.
      *
      * Don't use the JSON parser if the caller is strict mode code, because in
      * strict mode object literals must not have repeated properties, and the
@@ -1182,22 +1188,22 @@ obj_watch_handler(JSContext *cx, JSObjec
 static JSBool
 obj_watch(JSContext *cx, unsigned argc, Value *vp)
 {
     if (argc <= 1) {
         js_ReportMissingArg(cx, *vp, 1);
         return false;
     }
 
-    JSObject *callable = js_ValueToCallableObject(cx, &vp[3], 0);
+    RootedVarObject callable(cx, js_ValueToCallableObject(cx, &vp[3], 0));
     if (!callable)
         return false;
 
-    jsid propid;
-    if (!ValueToId(cx, vp[2], &propid))
+    RootedVarId propid(cx);
+    if (!ValueToId(cx, vp[2], propid.address()))
         return false;
 
     RootedVarObject obj(cx, ToObject(cx, &vp[1]));
     if (!obj)
         return false;
 
     Value tmp;
     unsigned attrs;
@@ -1383,21 +1389,21 @@ DefineAccessor(JSContext *cx, unsigned a
 
     if (args.length() < 2 || !js_IsCallable(args[1])) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                              JSMSG_BAD_GETTER_OR_SETTER,
                              Type == Getter ? js_getter_str : js_setter_str);
         return false;
     }
 
-    jsid id;
-    if (!ValueToId(cx, args[0], &id))
+    RootedVarId id(cx);
+    if (!ValueToId(cx, args[0], id.address()))
         return false;
 
-    JSObject *descObj = NewBuiltinClassInstance(cx, &ObjectClass);
+    RootedVarObject descObj(cx, NewBuiltinClassInstance(cx, &ObjectClass));
     if (!descObj)
         return false;
 
     JSAtomState &state = cx->runtime->atomState;
     /* enumerable: true */
     if (!descObj->defineProperty(cx, state.enumerableAtom, BooleanValue(true)))
         return false;
 
@@ -1405,22 +1411,20 @@ DefineAccessor(JSContext *cx, unsigned a
     if (!descObj->defineProperty(cx, state.configurableAtom, BooleanValue(true)))
         return false;
 
     /* enumerable: true */
     PropertyName *acc = (Type == Getter) ? state.getAtom : state.setAtom;
     if (!descObj->defineProperty(cx, acc, args[1]))
         return false;
 
+    RootedVarObject thisObj(cx, &args.thisv().toObject());
+
     JSBool dummy;
-    if (!js_DefineOwnProperty(cx,
-                              RootedVarObject(cx, &args.thisv().toObject()),
-                              RootedVarId(cx, id),
-                              ObjectValue(*descObj), &dummy))
-    {
+    if (!js_DefineOwnProperty(cx, thisObj, id, ObjectValue(*descObj), &dummy)) {
         return false;
     }
     args.rval().setUndefined();
     return true;
 }
 
 JS_FRIEND_API(JSBool)
 js::obj_defineGetter(JSContext *cx, unsigned argc, Value *vp)
@@ -1514,16 +1518,18 @@ NewPropertyDescriptorObject(JSContext *c
 {
     if (!desc->obj) {
         vp->setUndefined();
         return true;
     }
 
     /* We have our own property, so start creating the descriptor. */
     PropDesc d;
+    PropDesc::StackRoot dRoot(cx, &d);
+
     d.initFromPropertyDescriptor(*desc);
     if (!d.makeObject(cx))
         return false;
     *vp = d.pd();
     return true;
 }
 
 void
@@ -1558,17 +1564,17 @@ PropDesc::initFromPropertyDescriptor(con
     hasConfigurable_ = true;
 }
 
 bool
 PropDesc::makeObject(JSContext *cx)
 {
     MOZ_ASSERT(!isUndefined());
 
-    JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass);
+    RootedVarObject obj(cx, NewBuiltinClassInstance(cx, &ObjectClass));
     if (!obj)
         return false;
 
     const JSAtomState &atomState = cx->runtime->atomState;
     if ((hasConfigurable() &&
          !obj->defineProperty(cx, atomState.configurableAtom,
                               BooleanValue((attrs & JSPROP_PERMANENT) == 0))) ||
         (hasEnumerable() &&
@@ -3073,60 +3079,73 @@ js_InferFlags(JSContext *cx, unsigned de
     if (format & JOF_DECLARING)
         flags |= JSRESOLVE_DECLARING;
     return flags;
 }
 
 JSBool
 JSObject::nonNativeSetProperty(JSContext *cx, jsid id, js::Value *vp, JSBool strict)
 {
+    JSObject *self = this;
     if (JS_UNLIKELY(watched())) {
         id = js_CheckForStringIndex(id);
+
+        RootObject selfRoot(cx, &self);
+        RootId idRoot(cx, &id);
+
         WatchpointMap *wpmap = cx->compartment->watchpointMap;
-        if (wpmap && !wpmap->triggerWatchpoint(cx, this, id, vp))
+        if (wpmap && !wpmap->triggerWatchpoint(cx, selfRoot, idRoot, vp))
             return false;
     }
-    return getOps()->setGeneric(cx, this, id, vp, strict);
+    return self->getOps()->setGeneric(cx, self, id, vp, strict);
 }
 
 JSBool
 JSObject::nonNativeSetElement(JSContext *cx, uint32_t index, js::Value *vp, JSBool strict)
 {
+    JSObject *self = this;
     if (JS_UNLIKELY(watched())) {
+        RootObject selfRoot(cx, &self);
+
         jsid id;
         if (!IndexToId(cx, index, &id))
             return false;
         JS_ASSERT(id == js_CheckForStringIndex(id));
+
+        RootId idRoot(cx, &id);
+
         WatchpointMap *wpmap = cx->compartment->watchpointMap;
-        if (wpmap && !wpmap->triggerWatchpoint(cx, this, id, vp))
+        if (wpmap && !wpmap->triggerWatchpoint(cx, selfRoot, idRoot, vp))
             return false;
     }
-    return getOps()->setElement(cx, this, index, vp, strict);
+    return self->getOps()->setElement(cx, self, index, vp, strict);
 }
 
 bool
 JSObject::deleteByValue(JSContext *cx, const Value &property, Value *rval, bool strict)
 {
     uint32_t index;
     if (IsDefinitelyIndex(property, &index))
         return deleteElement(cx, index, rval, strict);
 
     Value propval = property;
     SpecialId sid;
     if (ValueIsSpecial(this, &propval, &sid, cx))
         return deleteSpecial(cx, sid, rval, strict);
 
+    RootedVarObject self(cx, this);
+
     JSAtom *name;
     if (!js_ValueToAtom(cx, propval, &name))
         return false;
 
     if (name->isIndex(&index))
-        return deleteElement(cx, index, rval, false);
-
-    return deleteProperty(cx, name->asPropertyName(), rval, false);
+        return self->deleteElement(cx, index, rval, false);
+
+    return self->deleteProperty(cx, name->asPropertyName(), rval, false);
 }
 
 JS_FRIEND_API(bool)
 JS_CopyPropertiesFrom(JSContext *cx, JSObject *target, JSObject *obj)
 {
     // If we're not native, then we cannot copy properties.
     JS_ASSERT(target->isNative() == obj->isNative());
     if (!target->isNative())
@@ -3669,18 +3688,17 @@ DefineConstructorAndPrototype(JSContext 
          * inference may need to access these, and js_GetClassPrototype will
          * fail if it tries to do a reentrant reconstruction of the class.
          */
         if (key != JSProto_Null) {
             SetClassObject(obj, key, fun, proto);
             cached = true;
         }
 
-        AutoValueRooter tvr2(cx, ObjectValue(*fun));
-        if (!DefineStandardSlot(cx, obj, key, atom, tvr2.value(), 0, named))
+        if (!DefineStandardSlot(cx, obj, key, atom, ObjectValue(*fun), 0, named))
             goto bad;
 
         /*
          * Optionally construct the prototype object, before the class has
          * been fully initialized.  Allow the ctor to replace proto with a
          * different object, as is done for operator new -- and as at least
          * XML support requires.
          */
@@ -4095,17 +4113,17 @@ static JSObjectOp lazy_prototype_init[JS
 #define JS_PROTO(name,code,init) init,
 #include "jsproto.tbl"
 #undef JS_PROTO
 };
 
 namespace js {
 
 bool
-SetProto(JSContext *cx, JSObject *obj, JSObject *proto, bool checkForCycles)
+SetProto(JSContext *cx, HandleObject obj, HandleObject proto, bool checkForCycles)
 {
     JS_ASSERT_IF(!checkForCycles, obj != proto);
     JS_ASSERT(obj->isExtensible());
 
     if (proto && proto->isXML()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_XML_PROTO_FORBIDDEN);
         return false;
     }
@@ -4124,17 +4142,17 @@ SetProto(JSContext *cx, JSObject *obj, J
      * objects, as the uncacheable prototype can inhibit iterator caches on
      * those objects and slow down prototype accesses. Choosing the latter is
      * bad if there are many similar objects to this one which will have their
      * prototype mutated, as the generateOwnShape forces the object into
      * dictionary mode and similar property lineages will be repeatedly cloned.
      *
      * :XXX: bug 707717 make this code less brittle.
      */
-    JSObject *oldproto = obj;
+    RootedVarObject oldproto(cx, obj);
     while (oldproto && oldproto->isNative()) {
         if (oldproto->hasSingletonType()) {
             if (!oldproto->generateOwnShape(cx))
                 return false;
         } else {
             if (!oldproto->setUncacheableProto(cx))
                 return false;
         }
@@ -4394,20 +4412,22 @@ js_PurgeScopeChainHelper(JSContext *cx, 
                 return false;
         }
     }
 
     return true;
 }
 
 Shape *
-js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
+js_AddNativeProperty(JSContext *cx, HandleObject obj, jsid id_,
                      PropertyOp getter, StrictPropertyOp setter, uint32_t slot,
                      unsigned attrs, unsigned flags, int shortid)
 {
+    RootedVarId id(cx, id_);
+
     /* Convert string indices to integers if appropriate. */
     id = js_CheckForStringIndex(id);
 
     /*
      * Purge the property cache of now-shadowed id in obj's scope chain. Do
      * this optimistically (assuming no failure below) before locking obj, so
      * we can lock the shadowed scope.
      */
@@ -4421,24 +4441,33 @@ JSBool
 js_DefineProperty(JSContext *cx, JSObject *obj_, jsid id, const Value *value,
                   PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
     RootObject obj(cx, &obj_);
     return !!DefineNativeProperty(cx, obj, id, *value, getter, setter, attrs, 0, 0);
 }
 
 JSBool
-js_DefineElement(JSContext *cx, JSObject *obj_, uint32_t index, const Value *value,
+js_DefineElement(JSContext *cx, JSObject *obj, uint32_t index, const Value *value,
                  PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
-    RootObject obj(cx, &obj_);
+    if (index <= JSID_INT_MAX) {
+        return !!DefineNativeProperty(cx, RootedVarObject(cx, obj), INT_TO_JSID(index), *value,
+                                      getter, setter, attrs, 0, 0);
+    }
+
+    RootObject objRoot(cx, &obj);
+    RootGetterSetter gsRoot(cx, attrs, &getter, &setter);
+    RootValue valueRoot(cx, value);
+
     jsid id;
     if (!IndexToId(cx, index, &id))
         return false;
-    return !!DefineNativeProperty(cx, obj, id, *value, getter, setter, attrs, 0, 0);
+
+    return !!DefineNativeProperty(cx, objRoot, id, *value, getter, setter, attrs, 0, 0);
 }
 
 /*
  * Backward compatibility requires allowing addProperty hooks to mutate the
  * nominal initial value of a slotful property, while GC safety wants that
  * value to be stored before the call-out through the hook.  Optimize to do
  * both while saving cycles for classes that stub their addProperty hook.
  */
@@ -4465,16 +4494,17 @@ DefineNativeProperty(JSContext *cx, Hand
                      PropertyOp getter, StrictPropertyOp setter, unsigned attrs,
                      unsigned flags, int shortid, unsigned defineHow /* = 0 */)
 {
     JS_ASSERT((defineHow & ~(DNP_CACHE_RESULT | DNP_DONT_PURGE |
                              DNP_SKIP_TYPE)) == 0);
     JS_ASSERT(!(attrs & JSPROP_NATIVE_ACCESSORS));
 
     RootId idRoot(cx, &id);
+    RootGetterSetter gsRoot(cx, attrs, &getter, &setter);
 
     /* Make a local copy of value so addProperty can mutate its inout parameter. */
     RootedVarValue value(cx);
     value = value_;
 
     /* Convert string indices to integers if appropriate. */
     id = js_CheckForStringIndex(id);
 
@@ -4846,25 +4876,25 @@ js::FindPropertyHelper(JSContext *cx,
 bool
 js::FindProperty(JSContext *cx, HandlePropertyName name, HandleObject scopeChain,
                  JSObject **objp, JSObject **pobjp, JSProperty **propp)
 {
     return !!FindPropertyHelper(cx, name, false, scopeChain, objp, pobjp, propp);
 }
 
 JSObject *
-js::FindIdentifierBase(JSContext *cx, JSObject *scopeChain, PropertyName *name)
+js::FindIdentifierBase(JSContext *cx, HandleObject scopeChain, HandlePropertyName name)
 {
     /*
      * This function should not be called for a global object or from the
      * trace and should have a valid cache entry for native scopeChain.
      */
     JS_ASSERT(scopeChain->enclosingScope() != NULL);
 
-    JSObject *obj = scopeChain;
+    RootedVarObject obj(cx, scopeChain);
 
     /*
      * Loop over cacheable objects on the scope chain until we find a
      * property. We also stop when we reach the global object skipping any
      * farther checks or lookups. For details see the JSOP_BINDNAME case of
      * js_Interpret.
      *
      * The test order here matters because IsCacheableNonGlobalScope
@@ -5232,17 +5262,17 @@ js_SetPropertyHelper(JSContext *cx, Hand
     RootId idRoot(cx, &id);
 
     /* Convert string indices to integers if appropriate. */
     id = js_CheckForStringIndex(id);
 
     if (JS_UNLIKELY(obj->watched())) {
         /* Fire watchpoints, if any. */
         WatchpointMap *wpmap = cx->compartment->watchpointMap;
-        if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, vp))
+        if (wpmap && !wpmap->triggerWatchpoint(cx, obj, idRoot, vp))
             return false;
     }
 
     if (!LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags, &pobj, &prop))
         return false;
     if (prop) {
         if (!pobj->isNative()) {
             if (pobj->isProxy()) {
@@ -5472,24 +5502,27 @@ js_SetElementAttributes(JSContext *cx, J
     if (!prop)
         return true;
     return obj->isNative()
            ? obj->changePropertyAttributes(cx, (Shape *) prop, *attrsp)
            : obj->setElementAttributes(cx, index, attrsp);
 }
 
 JSBool
-js_DeleteGeneric(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict)
+js_DeleteGeneric(JSContext *cx, JSObject *obj_, jsid id_, Value *rval, JSBool strict)
 {
     JSObject *proto;
     JSProperty *prop;
     const Shape *shape;
 
     rval->setBoolean(true);
 
+    RootedVarObject obj(cx, obj_);
+    RootedVarId id(cx, id_);
+
     /* Convert string indices to integers if appropriate. */
     id = js_CheckForStringIndex(id);
 
     if (!js_LookupProperty(cx, obj, id, &proto, &prop))
         return false;
     if (!prop || proto != obj) {
         /*
          * If no property, or the property comes from a prototype, call the
@@ -5521,18 +5554,20 @@ js_DeleteGeneric(JSContext *cx, JSObject
 
 JSBool
 js_DeleteProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *rval, JSBool strict)
 {
     return js_DeleteGeneric(cx, obj, ATOM_TO_JSID(name), rval, strict);
 }
 
 JSBool
-js_DeleteElement(JSContext *cx, JSObject *obj, uint32_t index, Value *rval, JSBool strict)
-{
+js_DeleteElement(JSContext *cx, JSObject *obj_, uint32_t index, Value *rval, JSBool strict)
+{
+    RootedVarObject obj(cx, obj_);
+
     jsid id;
     if (!IndexToId(cx, index, &id))
         return false;
     return js_DeleteGeneric(cx, obj, id, rval, strict);
 }
 
 JSBool
 js_DeleteSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *rval, JSBool strict)
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -516,17 +516,17 @@ struct JSObject : public js::ObjectImpl
      * objects with a global object at the root as the scope of the outermost
      * non-function script. In non-compileAndGo code, the scope of the
      * outermost non-function script might not be a global object, and can have
      * a mix of other objects above it before the global object is reached.
      */
 
     /* Access the parent link of an object. */
     inline JSObject *getParent() const;
-    bool setParent(JSContext *cx, JSObject *newParent);
+    static bool setParent(JSContext *cx, js::HandleObject obj, js::HandleObject newParent);
 
     /*
      * Get the enclosing scope of an object. When called on non-scope object,
      * this will just be the global (the name "enclosing scope" still applies
      * in this situation because non-scope objects can be on the scope chain).
      */
     inline JSObject *enclosingScope();
 
@@ -1034,17 +1034,17 @@ class ValueArray {
     js::Value *array;
     size_t length;
 
     ValueArray(js::Value *v, size_t c) : array(v), length(c) {}
 };
 
 /* For manipulating JSContext::sharpObjectMap. */
 extern bool
-js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, bool *alreadySeen, bool *isSharp);
+js_EnterSharpObject(JSContext *cx, js::HandleObject obj, JSIdArray **idap, bool *alreadySeen, bool *isSharp);
 
 extern void
 js_LeaveSharpObject(JSContext *cx, JSIdArray **idap);
 
 /*
  * Mark objects stored in map if GC happens between js_EnterSharpObject
  * and js_LeaveSharpObject. GC calls this when map->depth > 0.
  */
@@ -1208,17 +1208,17 @@ js_CreateThis(JSContext *cx, js::Class *
 extern jsid
 js_CheckForStringIndex(jsid id);
 
 /*
  * Find or create a property named by id in obj's scope, with the given getter
  * and setter, slot, attributes, and other members.
  */
 extern js::Shape *
-js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
+js_AddNativeProperty(JSContext *cx, js::HandleObject obj, jsid id,
                      JSPropertyOp getter, JSStrictPropertyOp setter, uint32_t slot,
                      unsigned attrs, unsigned flags, int shortid);
 
 extern JSBool
 js_DefineOwnProperty(JSContext *cx, js::HandleObject obj, js::HandleId id,
                      const js::Value &descriptor, JSBool *bp);
 
 namespace js {
@@ -1303,17 +1303,17 @@ FindPropertyHelper(JSContext *cx, Handle
  * Search for name either on the current scope chain or on the scope chain's
  * global object, per the global parameter.
  */
 extern bool
 FindProperty(JSContext *cx, HandlePropertyName name, HandleObject scopeChain,
              JSObject **objp, JSObject **pobjp, JSProperty **propp);
 
 extern JSObject *
-FindIdentifierBase(JSContext *cx, JSObject *scopeChain, PropertyName *name);
+FindIdentifierBase(JSContext *cx, HandleObject scopeChain, HandlePropertyName name);
 
 }
 
 extern JSObject *
 js_FindVariableScope(JSContext *cx, JSFunction **funp);
 
 /* JSGET_CACHE_RESULT is the analogue of DNP_CACHE_RESULT for js_GetMethod. */
 const unsigned JSGET_CACHE_RESULT = 1; // from a caching interpreter opcode
@@ -1459,17 +1459,17 @@ js_Object(JSContext *cx, unsigned argc, 
  */
 extern JS_FRIEND_API(JSBool)
 js_GetClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey,
                      JSObject **protop, js::Class *clasp = NULL);
 
 namespace js {
 
 extern bool
-SetProto(JSContext *cx, JSObject *obj, JSObject *proto, bool checkForCycles);
+SetProto(JSContext *cx, HandleObject obj, HandleObject proto, bool checkForCycles);
 
 extern JSString *
 obj_toStringHelper(JSContext *cx, JSObject *obj);
 
 extern JSBool
 eval(JSContext *cx, unsigned argc, Value *vp);
 
 /*
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -222,23 +222,25 @@ JSObject::deleteProperty(JSContext *cx, 
     js::types::MarkTypePropertyConfigured(cx, this, id);
     js::DeletePropertyOp op = getOps()->deleteProperty;
     return (op ? op : js_DeleteProperty)(cx, this, name, rval, strict);
 }
 
 inline bool
 JSObject::deleteElement(JSContext *cx, uint32_t index, js::Value *rval, bool strict)
 {
+    js::RootedVarObject self(cx, this);
+
     jsid id;
     if (!js::IndexToId(cx, index, &id))
         return false;
-    js::types::AddTypePropertyId(cx, this, id, js::types::Type::UndefinedType());
-    js::types::MarkTypePropertyConfigured(cx, this, id);
-    js::DeleteElementOp op = getOps()->deleteElement;
-    return (op ? op : js_DeleteElement)(cx, this, index, rval, strict);
+    js::types::AddTypePropertyId(cx, self, id, js::types::Type::UndefinedType());
+    js::types::MarkTypePropertyConfigured(cx, self, id);
+    js::DeleteElementOp op = self->getOps()->deleteElement;
+    return (op ? op : js_DeleteElement)(cx, self, index, rval, strict);
 }
 
 inline bool
 JSObject::deleteSpecial(JSContext *cx, js::SpecialId sid, js::Value *rval, bool strict)
 {
     jsid id = SPECIALID_TO_JSID(sid);
     js::types::AddTypePropertyId(cx, this, id, js::types::Type::UndefinedType());
     js::types::MarkTypePropertyConfigured(cx, this, id);
@@ -1079,63 +1081,69 @@ JSObject::lookupElement(JSContext *cx, u
 
 inline JSBool
 JSObject::lookupSpecial(JSContext *cx, js::SpecialId sid, JSObject **objp, JSProperty **propp)
 {
     return lookupGeneric(cx, SPECIALID_TO_JSID(sid), objp, propp);
 }
 
 inline JSBool
-JSObject::getElement(JSContext *cx, JSObject *receiver, uint32_t index, js::Value *vp)
+JSObject::getElement(JSContext *cx, JSObject *receiver_, uint32_t index, js::Value *vp)
 {
     js::ElementIdOp op = getOps()->getElement;
     if (op)
-        return op(cx, this, receiver, index, vp);
+        return op(cx, this, receiver_, index, vp);
+
+    js::RootedVarObject self(cx, this);
+    js::RootedVarObject receiver(cx, receiver_);
 
     jsid id;
     if (!js::IndexToId(cx, index, &id))
         return false;
-    return getGeneric(cx, receiver, id, vp);
+    return self->getGeneric(cx, receiver, id, vp);
 }
 
 inline JSBool
 JSObject::getElement(JSContext *cx, uint32_t index, js::Value *vp)
 {
     return getElement(cx, this, index, vp);
 }
 
 inline JSBool
-JSObject::getElementIfPresent(JSContext *cx, JSObject *receiver, uint32_t index, js::Value *vp,
+JSObject::getElementIfPresent(JSContext *cx, JSObject *receiver_, uint32_t index, js::Value *vp,
                               bool *present)
 {
+    js::RootedVarObject self(cx, this), receiver(cx, receiver_);
+
     js::ElementIfPresentOp op = getOps()->getElementIfPresent;
     if (op)
         return op(cx, this, receiver, index, vp, present);
 
-    /* For now, do the index-to-id conversion just once, then use
+    /*
+     * For now, do the index-to-id conversion just once, then use
      * lookupGeneric/getGeneric.  Once lookupElement and getElement stop both
      * doing index-to-id conversions, we can use those here.
      */
     jsid id;
     if (!js::IndexToId(cx, index, &id))
         return false;
 
     JSObject *obj2;
     JSProperty *prop;
-    if (!lookupGeneric(cx, id, &obj2, &prop))
+    if (!self->lookupGeneric(cx, id, &obj2, &prop))
         return false;
 
     if (!prop) {
         *present = false;
         js::Debug_SetValueRangeToCrashOnTouch(vp, 1);
         return true;
     }
 
     *present = true;
-    return getGeneric(cx, receiver, id, vp);
+    return self->getGeneric(cx, receiver, id, vp);
 }
 
 inline JSBool
 JSObject::getSpecial(JSContext *cx, JSObject *receiver, js::SpecialId sid, js::Value *vp)
 {
     return getGeneric(cx, receiver, SPECIALID_TO_JSID(sid), vp);
 }
 
@@ -1264,17 +1272,17 @@ IsInternalFunctionObject(JSObject *funob
     JSFunction *fun = funobj->toFunction();
     return (fun->flags & JSFUN_LAMBDA) && !funobj->getParent();
 }
 
 class AutoPropDescArrayRooter : private AutoGCRooter
 {
   public:
     AutoPropDescArrayRooter(JSContext *cx)
-      : AutoGCRooter(cx, DESCRIPTORS), descriptors(cx)
+      : AutoGCRooter(cx, DESCRIPTORS), descriptors(cx), skip(cx, &descriptors)
     { }
 
     PropDesc *append() {
         if (!descriptors.append(PropDesc()))
             return NULL;
         return &descriptors.back();
     }
 
@@ -1286,40 +1294,46 @@ class AutoPropDescArrayRooter : private 
         JS_ASSERT(i < descriptors.length());
         return descriptors[i];
     }
 
     friend void AutoGCRooter::trace(JSTracer *trc);
 
   private:
     PropDescArray descriptors;
+    SkipRoot skip;
 };
 
 class AutoPropertyDescriptorRooter : private AutoGCRooter, public PropertyDescriptor
 {
   public:
-    AutoPropertyDescriptorRooter(JSContext *cx) : AutoGCRooter(cx, DESCRIPTOR) {
+    AutoPropertyDescriptorRooter(JSContext *cx)
+      : AutoGCRooter(cx, DESCRIPTOR), skip(cx, this)
+    {
         obj = NULL;
         attrs = 0;
         getter = (PropertyOp) NULL;
         setter = (StrictPropertyOp) NULL;
         value.setUndefined();
     }
 
     AutoPropertyDescriptorRooter(JSContext *cx, PropertyDescriptor *desc)
-      : AutoGCRooter(cx, DESCRIPTOR)
+      : AutoGCRooter(cx, DESCRIPTOR), skip(cx, this)
     {
         obj = desc->obj;
         attrs = desc->attrs;
         getter = desc->getter;
         setter = desc->setter;
         value = desc->value;
     }
 
     friend void AutoGCRooter::trace(JSTracer *trc);
+
+  private:
+    SkipRoot skip;
 };
 
 inline bool
 NewObjectCache::lookup(Class *clasp, gc::Cell *key, gc::AllocKind kind, EntryIndex *pentry)
 {
     uintptr_t hash = (uintptr_t(clasp) ^ uintptr_t(key)) + kind;
     *pentry = hash % js::ArrayLength(entries);
 
@@ -1631,17 +1645,17 @@ inline bool
 DefineConstructorAndPrototype(JSContext *cx, GlobalObject *global,
                               JSProtoKey key, JSObject *ctor, JSObject *proto)
 {
     JS_ASSERT(!global->nativeEmpty()); /* reserved slots already allocated */
     JS_ASSERT(ctor);
     JS_ASSERT(proto);
 
     jsid id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]);
-    JS_ASSERT(!global->nativeLookup(cx, id));
+    JS_ASSERT(!global->nativeLookupNoAllocation(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)) {
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -208,36 +208,36 @@ Quote(JSContext *cx, StringBuffer &sb, J
     /* Steps 3-4. */
     return sb.append('"');
 }
 
 class StringifyContext
 {
   public:
     StringifyContext(JSContext *cx, StringBuffer &sb, const StringBuffer &gap,
-                     JSObject *replacer, const AutoIdVector &propertyList)
+                     HandleObject replacer, const AutoIdVector &propertyList)
       : sb(sb),
         gap(gap),
-        replacer(replacer),
+        replacer(cx, replacer),
         propertyList(propertyList),
         depth(0),
         objectStack(cx)
     {}
 
     bool init() {
         return objectStack.init(16);
     }
 
 #ifdef DEBUG
     ~StringifyContext() { JS_ASSERT(objectStack.empty()); }
 #endif
 
     StringBuffer &sb;
     const StringBuffer &gap;
-    JSObject * const replacer;
+    RootedVarObject replacer;
     const AutoIdVector &propertyList;
     uint32_t depth;
     HashSet<JSObject *> objectStack;
 };
 
 static JSBool Str(JSContext *cx, const Value &v, StringifyContext *scx);
 
 static JSBool
@@ -253,18 +253,18 @@ WriteIndent(JSContext *cx, StringifyCont
     }
 
     return JS_TRUE;
 }
 
 class CycleDetector
 {
   public:
-    CycleDetector(StringifyContext *scx, JSObject *obj)
-      : objectStack(scx->objectStack), obj(obj) {
+    CycleDetector(JSContext *cx, StringifyContext *scx, JSObject *obj)
+      : objectStack(scx->objectStack), obj(cx, obj) {
     }
 
     bool init(JSContext *cx) {
         HashSet<JSObject *>::AddPtr ptr = objectStack.lookupForAdd(obj);
         if (ptr) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CYCLIC_VALUE, js_object_str);
             return false;
         }
@@ -272,17 +272,17 @@ class CycleDetector
     }
 
     ~CycleDetector() {
         objectStack.remove(obj);
     }
 
   private:
     HashSet<JSObject *> &objectStack;
-    JSObject *const obj;
+    RootedVarObject obj;
 };
 
 template<typename KeyType>
 class KeyStringifier {
 };
 
 template<>
 class KeyStringifier<uint32_t> {
@@ -391,30 +391,30 @@ PreprocessValue(JSContext *cx, JSObject 
 static inline bool
 IsFilteredValue(const Value &v)
 {
     return v.isUndefined() || js_IsCallable(v) || (v.isObject() && v.toObject().isXML());
 }
 
 /* ES5 15.12.3 JO. */
 static JSBool
-JO(JSContext *cx, JSObject *obj, StringifyContext *scx)
+JO(JSContext *cx, HandleObject obj, StringifyContext *scx)
 {
     /*
      * This method implements the JO algorithm in ES5 15.12.3, but:
      *
      *   * The algorithm is somewhat reformulated to allow the final string to
      *     be streamed into a single buffer, rather than be created and copied
      *     into place incrementally as the ES5 algorithm specifies it.  This
      *     requires moving portions of the Str call in 8a into this algorithm
      *     (and in JA as well).
      */
 
     /* Steps 1-2, 11. */
-    CycleDetector detect(scx, obj);
+    CycleDetector detect(cx, scx, obj);
     if (!detect.init(cx))
         return JS_FALSE;
 
     if (!scx->sb.append('{'))
         return JS_FALSE;
 
     /* Steps 5-7. */
     Maybe<AutoIdVector> ids;
@@ -476,30 +476,30 @@ JO(JSContext *cx, JSObject *obj, Stringi
     if (wroteMember && !WriteIndent(cx, scx, scx->depth - 1))
         return false;
 
     return scx->sb.append('}');
 }
 
 /* ES5 15.12.3 JA. */
 static JSBool
-JA(JSContext *cx, JSObject *obj, StringifyContext *scx)
+JA(JSContext *cx, HandleObject obj, StringifyContext *scx)
 {
     /*
      * This method implements the JA algorithm in ES5 15.12.3, but:
      *
      *   * The algorithm is somewhat reformulated to allow the final string to
      *     be streamed into a single buffer, rather than be created and copied
      *     into place incrementally as the ES5 algorithm specifies it.  This
      *     requires moving portions of the Str call in 8a into this algorithm
      *     (and in JO as well).
      */
 
     /* Steps 1-2, 11. */
-    CycleDetector detect(scx, obj);
+    CycleDetector detect(cx, scx, obj);
     if (!detect.init(cx))
         return JS_FALSE;
 
     if (!scx->sb.append('['))
         return JS_FALSE;
 
     /* Step 6. */
     uint32_t length;
@@ -594,33 +594,36 @@ Str(JSContext *cx, const Value &v, Strin
         if (!NumberValueToStringBuffer(cx, v, sb))
             return false;
 
         return scx->sb.append(sb.begin(), sb.length());
     }
 
     /* Step 10. */
     JS_ASSERT(v.isObject());
-    JSObject *obj = &v.toObject();
+    RootedVarObject obj(cx, &v.toObject());
 
     scx->depth++;
     JSBool ok;
     if (ObjectClassIs(v.toObject(), ESClass_Array, cx))
         ok = JA(cx, obj, scx);
     else
         ok = JO(cx, obj, scx);
     scx->depth--;
 
     return ok;
 }
 
 /* ES5 15.12.3. */
 JSBool
-js_Stringify(JSContext *cx, Value *vp, JSObject *replacer, Value space, StringBuffer &sb)
+js_Stringify(JSContext *cx, Value *vp, JSObject *replacer_, Value space, StringBuffer &sb)
 {
+    RootedVarObject replacer(cx, replacer_);
+    RootValue spaceRoot(cx, &space);
+
     /* Step 4. */
     AutoIdVector propertyList(cx);
     if (replacer) {
         if (replacer->isCallable()) {
             /* Step 4a(i): use replacer to transform values.  */
         } else if (ObjectClassIs(*replacer, ESClass_Array, cx)) {
             /*
              * Step 4b: The spec algorithm is unhelpfully vague about the exact
@@ -923,19 +926,21 @@ static JSFunctionSpec json_static_method
     JS_FN(js_toSource_str,  json_toSource,      0, 0),
 #endif
     JS_FN("parse",          js_json_parse,      2, 0),
     JS_FN("stringify",      js_json_stringify,  3, 0),
     JS_FS_END
 };
 
 JSObject *
-js_InitJSONClass(JSContext *cx, JSObject *obj)
+js_InitJSONClass(JSContext *cx, JSObject *obj_)
 {
-    JSObject *JSON = NewObjectWithClassProto(cx, &JSONClass, NULL, obj);
+    RootedVarObject obj(cx, obj_);
+
+    RootedVarObject JSON(cx, NewObjectWithClassProto(cx, &JSONClass, NULL, obj));
     if (!JSON || !JSON->setSingletonType(cx))
         return NULL;
 
     if (!JS_DefineProperty(cx, obj, js_JSON_str, OBJECT_TO_JSVAL(JSON),
                            JS_PropertyStub, JS_StrictPropertyStub, 0))
         return NULL;
 
     if (!JS_DefineFunctions(cx, JSON, json_static_methods))
--- a/js/src/jsonparser.h
+++ b/js/src/jsonparser.h
@@ -61,16 +61,19 @@ class JSONParser
 
   private:
     /* Data members */
 
     JSContext * const cx;
     mozilla::RangedPtr<const jschar> current;
     const mozilla::RangedPtr<const jschar> end;
 
+    /* For current/end as cursors into a string. */
+    js::SkipRoot root;
+
     js::Value v;
 
     const ParsingMode parsingMode;
     const ErrorHandling errorHandling;
 
     enum Token { String, Number, True, False, Null,
                  ArrayOpen, ArrayClose,
                  ObjectOpen, ObjectClose,
@@ -90,16 +93,17 @@ class JSONParser
      * use strict JSON parsing.
      */
     JSONParser(JSContext *cx, const jschar *data, size_t length,
                ParsingMode parsingMode = StrictJSON,
                ErrorHandling errorHandling = RaiseError)
       : cx(cx),
         current(data, length),
         end(data + length, data, length),
+        root(cx, this),
         parsingMode(parsingMode),
         errorHandling(errorHandling)
 #ifdef DEBUG
       , lastToken(Error)
 #endif
     {
         JS_ASSERT(current <= end);
     }
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -349,18 +349,20 @@ js_DumpPCCounts(JSContext *cx, JSScript 
     }
 }
 
 /*
  * If pc != NULL, include a prefix indicating whether the PC is at the current line.
  * If counts != NULL, include a counter of the number of times each op was executed.
  */
 JS_FRIEND_API(JSBool)
-js_DisassembleAtPC(JSContext *cx, JSScript *script, JSBool lines, jsbytecode *pc, Sprinter *sp)
+js_DisassembleAtPC(JSContext *cx, JSScript *script_, JSBool lines, jsbytecode *pc, Sprinter *sp)
 {
+    RootedVar<JSScript*> script(cx, script_);
+
     jsbytecode *next, *end;
     unsigned len;
 
     sp->put("loc   ");
     if (lines)
         sp->put("line");
     sp->put("  op\n");
     sp->put("----- ");
@@ -446,29 +448,31 @@ ToDisassemblySource(JSContext *cx, jsval
     if (!JSVAL_IS_PRIMITIVE(v)) {
         JSObject *obj = JSVAL_TO_OBJECT(v);
         if (obj->isBlock()) {
             char *source = JS_sprintf_append(NULL, "depth %d {", obj->asBlock().stackDepth());
             if (!source)
                 return false;
 
             Shape::Range r = obj->lastProperty()->all();
+            Shape::Range::Root root(cx, &r);
+
             while (!r.empty()) {
-                const Shape &shape = r.front();
-                JSAtom *atom = JSID_IS_INT(shape.propid())
+                RootedVar<const Shape*> shape(cx, &r.front());
+                JSAtom *atom = JSID_IS_INT(shape->propid())
                                ? cx->runtime->atomState.emptyAtom
-                               : JSID_TO_ATOM(shape.propid());
+                               : JSID_TO_ATOM(shape->propid());
 
                 JSAutoByteString bytes;
                 if (!js_AtomToPrintableString(cx, atom, &bytes))
                     return false;
 
                 r.popFront();
                 source = JS_sprintf_append(source, "%s: %d%s",
-                                           bytes.ptr(), shape.shortid(),
+                                           bytes.ptr(), shape->shortid(),
                                            !r.empty() ? ", " : "");
                 if (!source)
                     return false;
             }
 
             source = JS_sprintf_append(source, "}");
             if (!source)
                 return false;
@@ -5502,17 +5506,19 @@ js_DecompileFunctionBody(JSPrinter *jp)
 
     script = jp->fun->script();
     return DecompileBody(jp, script, script->code);
 }
 
 JSBool
 js_DecompileFunction(JSPrinter *jp)
 {
-    JSFunction *fun = jp->fun;
+    JSContext *cx = jp->sprinter.context;
+
+    RootedVarFunction fun(cx, jp->fun);
     JS_ASSERT(fun);
     JS_ASSERT(!jp->script);
 
     /*
      * If pretty, conform to ECMA-262 Edition 3, 15.3.4.2, by decompiling a
      * FunctionDeclaration.  Otherwise, check the JSFUN_LAMBDA flag and force
      * an expression by parenthesizing.
      */
@@ -5532,17 +5538,17 @@ js_DecompileFunction(JSPrinter *jp)
         js_printf(jp, ") {\n");
         jp->indent += 4;
         js_printf(jp, native_code_str);
         jp->indent -= 4;
         js_printf(jp, "\t}");
     } else {
         JSScript *script = fun->script();
 #if JS_HAS_DESTRUCTURING
-        SprintStack ss(jp->sprinter.context);
+        SprintStack ss(cx);
 #endif
 
         /* Print the parameters. */
         jsbytecode *pc = script->main();
         jsbytecode *endpc = pc + script->length;
         JSBool ok = JS_TRUE;
 
 #if JS_HAS_DESTRUCTURING
@@ -5562,17 +5568,17 @@ js_DecompileFunction(JSPrinter *jp)
             if (!param) {
                 ptrdiff_t todo;
                 const char *lval;
 
                 LOCAL_ASSERT(*pc == JSOP_GETARG || *pc == JSOP_GETALIASEDVAR);
                 pc += js_CodeSpec[*pc].length;
                 LOCAL_ASSERT(*pc == JSOP_DUP);
                 if (!ss.printer) {
-                    ok = InitSprintStack(jp->sprinter.context, &ss, jp, StackDepth(script));
+                    ok = InitSprintStack(cx, &ss, jp, StackDepth(script));
                     if (!ok)
                         break;
                 }
                 pc = DecompileDestructuring(&ss, pc, endpc);
                 if (!pc) {
                     ok = JS_FALSE;
                     break;
                 }
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -149,18 +149,21 @@ ProxyHandler::get(JSContext *cx, JSObjec
     else
         vp->setUndefined();
     if (desc.attrs & JSPROP_SHORTID)
         id = INT_TO_JSID(desc.shortid);
     return CallJSPropertyOp(cx, desc.getter, receiver, id, vp);
 }
 
 bool
-ProxyHandler::getElementIfPresent(JSContext *cx, JSObject *proxy, JSObject *receiver, uint32_t index, Value *vp, bool *present)
+ProxyHandler::getElementIfPresent(JSContext *cx, JSObject *proxy_, JSObject *receiver_, uint32_t index, Value *vp, bool *present)
 {
+    RootedVarObject proxy(cx, proxy_);
+    RootedVarObject receiver(cx, receiver_);
+
     jsid id;
     if (!IndexToId(cx, index, &id))
         return false;
 
     if (!has(cx, proxy, id, present))
         return false;
 
     if (!*present) {
@@ -328,20 +331,20 @@ ProxyHandler::iteratorNext(JSContext *cx
     vp->setMagic(JS_NO_ITER_VALUE);
     return true;
 }
 
 bool
 ProxyHandler::call(JSContext *cx, JSObject *proxy, unsigned argc, Value *vp)
 {
     JS_ASSERT(OperationInProgress(cx, proxy));
-    AutoValueRooter rval(cx);
-    JSBool ok = Invoke(cx, vp[1], GetCall(proxy), argc, JS_ARGV(cx, vp), rval.addr());
+    RootedVarValue rval(cx);
+    JSBool ok = Invoke(cx, vp[1], GetCall(proxy), argc, JS_ARGV(cx, vp), rval.address());
     if (ok)
-        JS_SET_RVAL(cx, vp, rval.value());
+        JS_SET_RVAL(cx, vp, rval);
     return ok;
 }
 
 bool
 ProxyHandler::construct(JSContext *cx, JSObject *proxy,
                         unsigned argc, Value *argv, Value *rval)
 {
     JS_ASSERT(OperationInProgress(cx, proxy));
@@ -425,33 +428,33 @@ GetDerivedTrap(JSContext *cx, JSObject *
               atom == ATOM(set) ||
               atom == ATOM(keys) ||
               atom == ATOM(iterate));
 
     return GetTrap(cx, handler, atom, fvalp);
 }
 
 static bool
-Trap(JSContext *cx, JSObject *handler, Value fval, unsigned argc, Value* argv, Value *rval)
+Trap(JSContext *cx, HandleObject handler, HandleValue fval, unsigned argc, Value* argv, Value *rval)
 {
     return Invoke(cx, ObjectValue(*handler), fval, argc, argv, rval);
 }
 
 static bool
-Trap1(JSContext *cx, JSObject *handler, Value fval, jsid id, Value *rval)
+Trap1(JSContext *cx, HandleObject handler, HandleValue fval, jsid id, Value *rval)
 {
     JSString *str = ToString(cx, IdToValue(id));
     if (!str)
         return false;
     rval->setString(str);
     return Trap(cx, handler, fval, 1, rval, rval);
 }
 
 static bool
-Trap2(JSContext *cx, JSObject *handler, Value fval, jsid id, Value v, Value *rval)
+Trap2(JSContext *cx, HandleObject handler, HandleValue fval, jsid id, Value v, Value *rval)
 {
     JSString *str = ToString(cx, IdToValue(id));
     if (!str)
         return false;
     rval->setString(str);
     Value argv[2] = { *rval, v };
     return Trap(cx, handler, fval, 2, argv, rval);
 }
@@ -573,179 +576,192 @@ ReturnedValueMustNotBePrimitive(JSContex
 static JSObject *
 GetProxyHandlerObject(JSContext *cx, JSObject *proxy)
 {
     JS_ASSERT(OperationInProgress(cx, proxy));
     return GetProxyPrivate(proxy).toObjectOrNull();
 }
 
 bool
-ScriptedProxyHandler::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
+ScriptedProxyHandler::getPropertyDescriptor(JSContext *cx, JSObject *proxy_, jsid id_, bool set,
                                             PropertyDescriptor *desc)
 {
-    JSObject *handler = GetProxyHandlerObject(cx, proxy);
-    AutoValueRooter tvr(cx);
-    return GetFundamentalTrap(cx, handler, ATOM(getPropertyDescriptor), tvr.addr()) &&
-           Trap1(cx, handler, tvr.value(), id, tvr.addr()) &&
-           ((tvr.value().isUndefined() && IndicatePropertyNotFound(cx, desc)) ||
-            (ReturnedValueMustNotBePrimitive(cx, proxy, ATOM(getPropertyDescriptor), tvr.value()) &&
-             ParsePropertyDescriptorObject(cx, proxy, id, tvr.value(), desc)));
+    RootedVarId id(cx, id_);
+    RootedVarObject proxy(cx, proxy_);
+    RootedVarObject handler(cx, GetProxyHandlerObject(cx, proxy));
+    RootedVarValue fval(cx), value(cx);
+    return GetFundamentalTrap(cx, handler, ATOM(getPropertyDescriptor), fval.address()) &&
+           Trap1(cx, handler, fval, id, value.address()) &&
+           ((value.reference().isUndefined() && IndicatePropertyNotFound(cx, desc)) ||
+            (ReturnedValueMustNotBePrimitive(cx, proxy, ATOM(getPropertyDescriptor), value) &&
+             ParsePropertyDescriptorObject(cx, proxy, id, value, desc)));
 }
 
 bool
-ScriptedProxyHandler::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
+ScriptedProxyHandler::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy_, jsid id_, bool set,
                                                PropertyDescriptor *desc)
 {
-    JSObject *handler = GetProxyHandlerObject(cx, proxy);
-    AutoValueRooter tvr(cx);
-    return GetFundamentalTrap(cx, handler, ATOM(getOwnPropertyDescriptor), tvr.addr()) &&
-           Trap1(cx, handler, tvr.value(), id, tvr.addr()) &&
-           ((tvr.value().isUndefined() && IndicatePropertyNotFound(cx, desc)) ||
-            (ReturnedValueMustNotBePrimitive(cx, proxy, ATOM(getPropertyDescriptor), tvr.value()) &&
-             ParsePropertyDescriptorObject(cx, proxy, id, tvr.value(), desc)));
+    RootedVarId id(cx, id_);
+    RootedVarObject proxy(cx, proxy_);
+    RootedVarObject handler(cx, GetProxyHandlerObject(cx, proxy));
+    RootedVarValue fval(cx), value(cx);
+    return GetFundamentalTrap(cx, handler, ATOM(getOwnPropertyDescriptor), fval.address()) &&
+           Trap1(cx, handler, fval, id, value.address()) &&
+           ((value.reference().isUndefined() && IndicatePropertyNotFound(cx, desc)) ||
+            (ReturnedValueMustNotBePrimitive(cx, proxy, ATOM(getPropertyDescriptor), value) &&
+             ParsePropertyDescriptorObject(cx, proxy, id, value, desc)));
 }
 
 bool
-ScriptedProxyHandler::defineProperty(JSContext *cx, JSObject *proxy, jsid id,
+ScriptedProxyHandler::defineProperty(JSContext *cx, JSObject *proxy, jsid id_,
                                      PropertyDescriptor *desc)
 {
-    JSObject *handler = GetProxyHandlerObject(cx, proxy);
-    AutoValueRooter tvr(cx);
-    AutoValueRooter fval(cx);
-    return GetFundamentalTrap(cx, handler, ATOM(defineProperty), fval.addr()) &&
-           NewPropertyDescriptorObject(cx, desc, tvr.addr()) &&
-           Trap2(cx, handler, fval.value(), id, tvr.value(), tvr.addr());
+    RootedVarObject handler(cx, GetProxyHandlerObject(cx, proxy));
+    RootedVarValue fval(cx), value(cx);
+    RootedVarId id(cx, id_);
+    return GetFundamentalTrap(cx, handler, ATOM(defineProperty), fval.address()) &&
+           NewPropertyDescriptorObject(cx, desc, value.address()) &&
+           Trap2(cx, handler, fval, id, value, value.address());
 }
 
 bool
 ScriptedProxyHandler::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
-    JSObject *handler = GetProxyHandlerObject(cx, proxy);
-    AutoValueRooter tvr(cx);
-    return GetFundamentalTrap(cx, handler, ATOM(getOwnPropertyNames), tvr.addr()) &&
-           Trap(cx, handler, tvr.value(), 0, NULL, tvr.addr()) &&
-           ArrayToIdVector(cx, tvr.value(), props);
+    RootedVarObject handler(cx, GetProxyHandlerObject(cx, proxy));
+    RootedVarValue fval(cx), value(cx);
+    return GetFundamentalTrap(cx, handler, ATOM(getOwnPropertyNames), fval.address()) &&
+           Trap(cx, handler, fval, 0, NULL, value.address()) &&
+           ArrayToIdVector(cx, value, props);
 }
 
 bool
 ScriptedProxyHandler::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
 {
-    JSObject *handler = GetProxyHandlerObject(cx, proxy);
-    AutoValueRooter tvr(cx);
-    return GetFundamentalTrap(cx, handler, ATOM(delete), tvr.addr()) &&
-           Trap1(cx, handler, tvr.value(), id, tvr.addr()) &&
-           ValueToBool(cx, tvr.value(), bp);
+    RootedVarObject handler(cx, GetProxyHandlerObject(cx, proxy));
+    RootedVarValue fval(cx), value(cx);
+    return GetFundamentalTrap(cx, handler, ATOM(delete), fval.address()) &&
+           Trap1(cx, handler, fval, id, value.address()) &&
+           ValueToBool(cx, value, bp);
 }
 
 bool
 ScriptedProxyHandler::enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
-    JSObject *handler = GetProxyHandlerObject(cx, proxy);
-    AutoValueRooter tvr(cx);
-    return GetFundamentalTrap(cx, handler, ATOM(enumerate), tvr.addr()) &&
-           Trap(cx, handler, tvr.value(), 0, NULL, tvr.addr()) &&
-           ArrayToIdVector(cx, tvr.value(), props);
+    RootedVarObject handler(cx, GetProxyHandlerObject(cx, proxy));
+    RootedVarValue fval(cx), value(cx);
+    return GetFundamentalTrap(cx, handler, ATOM(enumerate), fval.address()) &&
+           Trap(cx, handler, fval, 0, NULL, value.address()) &&
+           ArrayToIdVector(cx, value, props);
 }
 
 bool
-ScriptedProxyHandler::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
+ScriptedProxyHandler::has(JSContext *cx, JSObject *proxy_, jsid id, bool *bp)
 {
-    JSObject *handler = GetProxyHandlerObject(cx, proxy);
-    AutoValueRooter tvr(cx);
-    if (!GetDerivedTrap(cx, handler, ATOM(has), tvr.addr()))
+    RootedVarObject proxy(cx, proxy_);
+    RootedVarObject handler(cx, GetProxyHandlerObject(cx, proxy));
+    RootedVarValue fval(cx), value(cx);
+    if (!GetDerivedTrap(cx, handler, ATOM(has), fval.address()))
         return false;
-    if (!js_IsCallable(tvr.value()))
+    if (!js_IsCallable(fval))
         return ProxyHandler::has(cx, proxy, id, bp);
-    return Trap1(cx, handler, tvr.value(), id, tvr.addr()) &&
-           ValueToBool(cx, tvr.value(), bp);
+    return Trap1(cx, handler, fval, id, value.address()) &&
+           ValueToBool(cx, value, bp);
 }
 
 bool
-ScriptedProxyHandler::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
+ScriptedProxyHandler::hasOwn(JSContext *cx, JSObject *proxy_, jsid id, bool *bp)
 {
-    JSObject *handler = GetProxyHandlerObject(cx, proxy);
-    AutoValueRooter tvr(cx);
-    if (!GetDerivedTrap(cx, handler, ATOM(hasOwn), tvr.addr()))
+    RootedVarObject proxy(cx, proxy_);
+    RootedVarObject handler(cx, GetProxyHandlerObject(cx, proxy));
+    RootedVarValue fval(cx), value(cx);
+    if (!GetDerivedTrap(cx, handler, ATOM(hasOwn), fval.address()))
         return false;
-    if (!js_IsCallable(tvr.value()))
+    if (!js_IsCallable(fval))
         return ProxyHandler::hasOwn(cx, proxy, id, bp);
-    return Trap1(cx, handler, tvr.value(), id, tvr.addr()) &&
-           ValueToBool(cx, tvr.value(), bp);
+    return Trap1(cx, handler, fval, id, value.address()) &&
+           ValueToBool(cx, value, bp);
 }
 
 bool
-ScriptedProxyHandler::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp)
+ScriptedProxyHandler::get(JSContext *cx, JSObject *proxy_, JSObject *receiver, jsid id_, Value *vp)
 {
-    JSObject *handler = GetProxyHandlerObject(cx, proxy);
+    RootedVarId id(cx, id_);
+    RootedVarObject proxy(cx, proxy_);
+    RootedVarObject handler(cx, GetProxyHandlerObject(cx, proxy));
     JSString *str = ToString(cx, IdToValue(id));
     if (!str)
         return false;
-    AutoValueRooter tvr(cx, StringValue(str));
-    Value argv[] = { ObjectOrNullValue(receiver), tvr.value() };
-    AutoValueRooter fval(cx);
-    if (!GetDerivedTrap(cx, handler, ATOM(get), fval.addr()))
+    RootedVarValue value(cx, StringValue(str));
+    Value argv[] = { ObjectOrNullValue(receiver), value };
+    RootedVarValue fval(cx);
+    if (!GetDerivedTrap(cx, handler, ATOM(get), fval.address()))
         return false;
-    if (!js_IsCallable(fval.value()))
+    if (!js_IsCallable(fval))
         return ProxyHandler::get(cx, proxy, receiver, id, vp);
-    return Trap(cx, handler, fval.value(), 2, argv, vp);
+    return Trap(cx, handler, fval, 2, argv, vp);
 }
 
 bool
-ScriptedProxyHandler::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, bool strict,
+ScriptedProxyHandler::set(JSContext *cx, JSObject *proxy_, JSObject *receiver, jsid id_, bool strict,
                           Value *vp)
 {
-    JSObject *handler = GetProxyHandlerObject(cx, proxy);
+    RootedVarId id(cx, id_);
+    RootedVarObject proxy(cx, proxy_);
+    RootedVarObject handler(cx, GetProxyHandlerObject(cx, proxy));
     JSString *str = ToString(cx, IdToValue(id));
     if (!str)
         return false;
-    AutoValueRooter tvr(cx, StringValue(str));
-    Value argv[] = { ObjectOrNullValue(receiver), tvr.value(), *vp };
-    AutoValueRooter fval(cx);
-    if (!GetDerivedTrap(cx, handler, ATOM(set), fval.addr()))
+    RootedVarValue value(cx, StringValue(str));
+    Value argv[] = { ObjectOrNullValue(receiver), value, *vp };
+    RootedVarValue fval(cx);
+    if (!GetDerivedTrap(cx, handler, ATOM(set), fval.address()))
         return false;
-    if (!js_IsCallable(fval.value()))
+    if (!js_IsCallable(fval))
         return ProxyHandler::set(cx, proxy, receiver, id, strict, vp);
-    return Trap(cx, handler, fval.value(), 3, argv, tvr.addr());
+    return Trap(cx, handler, fval, 3, argv, value.address());
 }
 
 bool
-ScriptedProxyHandler::keys(JSContext *cx, JSObject *proxy, AutoIdVector &props)
+ScriptedProxyHandler::keys(JSContext *cx, JSObject *proxy_, AutoIdVector &props)
 {
-    JSObject *handler = GetProxyHandlerObject(cx, proxy);
-    AutoValueRooter tvr(cx);
-    if (!GetDerivedTrap(cx, handler, ATOM(keys), tvr.addr()))
+    RootedVarObject proxy(cx, proxy_);
+    RootedVarObject handler(cx, GetProxyHandlerObject(cx, proxy));
+    RootedVarValue value(cx);
+    if (!GetDerivedTrap(cx, handler, ATOM(keys), value.address()))
         return false;
-    if (!js_IsCallable(tvr.value()))
+    if (!js_IsCallable(value))
         return ProxyHandler::keys(cx, proxy, props);
-    return Trap(cx, handler, tvr.value(), 0, NULL, tvr.addr()) &&
-           ArrayToIdVector(cx, tvr.value(), props);
+    return Trap(cx, handler, value, 0, NULL, value.address()) &&
+           ArrayToIdVector(cx, value, props);
 }
 
 bool
-ScriptedProxyHandler::iterate(JSContext *cx, JSObject *proxy, unsigned flags, Value *vp)
+ScriptedProxyHandler::iterate(JSContext *cx, JSObject *proxy_, unsigned flags, Value *vp)
 {
-    JSObject *handler = GetProxyHandlerObject(cx, proxy);
-    AutoValueRooter tvr(cx);
-    if (!GetDerivedTrap(cx, handler, ATOM(iterate), tvr.addr()))
+    RootedVarObject proxy(cx, proxy_);
+    RootedVarObject handler(cx, GetProxyHandlerObject(cx, proxy));
+    RootedVarValue value(cx);
+    if (!GetDerivedTrap(cx, handler, ATOM(iterate), value.address()))
         return false;
-    if (!js_IsCallable(tvr.value()))
+    if (!js_IsCallable(value))
         return ProxyHandler::iterate(cx, proxy, flags, vp);
-    return Trap(cx, handler, tvr.value(), 0, NULL, vp) &&
+    return Trap(cx, handler, value, 0, NULL, vp) &&
            ReturnedValueMustNotBePrimitive(cx, proxy, ATOM(iterate), *vp);
 }
 
 ScriptedProxyHandler ScriptedProxyHandler::singleton;
 
 class AutoPendingProxyOperation {
     JSRuntime               *rt;
     PendingProxyOperation   op;
   public:
-    AutoPendingProxyOperation(JSContext *cx, JSObject *proxy) : rt(cx->runtime) {
+    AutoPendingProxyOperation(JSContext *cx, JSObject *proxy)
+        : rt(cx->runtime), op(cx, proxy)
+    {
         op.next = rt->pendingProxyOperation;
-        op.object = proxy;
         rt->pendingProxyOperation = &op;
     }
 
     ~AutoPendingProxyOperation() {
         JS_ASSERT(rt->pendingProxyOperation == &op);
         rt->pendingProxyOperation = op.next;
     }
 };
@@ -977,19 +993,21 @@ Proxy::iteratorNext(JSContext *cx, JSObj
 
 static JSObject *
 proxy_innerObject(JSContext *cx, JSObject *obj)
 {
     return GetProxyPrivate(obj).toObjectOrNull();
 }
 
 static JSBool
-proxy_LookupGeneric(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
+proxy_LookupGeneric(JSContext *cx, JSObject *obj_, jsid id, JSObject **objp,
                     JSProperty **propp)
 {
+    RootedVarObject obj(cx, obj_);
+
     id = js_CheckForStringIndex(id);
 
     bool found;
     if (!Proxy::has(cx, obj, id, &found))
         return false;
 
     if (found) {
         *propp = (JSProperty *)0x1;
@@ -1004,19 +1022,21 @@ proxy_LookupGeneric(JSContext *cx, JSObj
 static JSBool
 proxy_LookupProperty(JSContext *cx, JSObject *obj, PropertyName *name, JSObject **objp,
                      JSProperty **propp)
 {
     return proxy_LookupGeneric(cx, obj, ATOM_TO_JSID(name), objp, propp);
 }
 
 static JSBool
-proxy_LookupElement(JSContext *cx, JSObject *obj, uint32_t index, JSObject **objp,
+proxy_LookupElement(JSContext *cx, JSObject *obj_, uint32_t index, JSObject **objp,
                     JSProperty **propp)
 {
+    RootedVarObject obj(cx, obj_);
+
     jsid id;
     if (!IndexToId(cx, index, &id))
         return false;
     return proxy_LookupGeneric(cx, obj, id, objp, propp);
 }
 
 static JSBool
 proxy_LookupSpecial(JSContext *cx, JSObject *obj, SpecialId sid, JSObject **objp, JSProperty **propp)
@@ -1043,19 +1063,21 @@ proxy_DefineGeneric(JSContext *cx, JSObj
 static JSBool
 proxy_DefineProperty(JSContext *cx, JSObject *obj, PropertyName *name, const Value *value,
                      PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
     return proxy_DefineGeneric(cx, obj, ATOM_TO_JSID(name), value, getter, setter, attrs);
 }
 
 static JSBool
-proxy_DefineElement(JSContext *cx, JSObject *obj, uint32_t index, const Value *value,
+proxy_DefineElement(JSContext *cx, JSObject *obj_, uint32_t index, const Value *value,
                     PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
+    RootedVarObject obj(cx, obj_);
+
     jsid id;
     if (!IndexToId(cx, index, &id))
         return false;
     return proxy_DefineGeneric(cx, obj, id, value, getter, setter, attrs);
 }
 
 static JSBool
 proxy_DefineSpecial(JSContext *cx, JSObject *obj, SpecialId sid, const Value *value,
@@ -1074,18 +1096,21 @@ proxy_GetGeneric(JSContext *cx, JSObject
 
 static JSBool
 proxy_GetProperty(JSContext *cx, JSObject *obj, JSObject *receiver, PropertyName *name, Value *vp)
 {
     return proxy_GetGeneric(cx, obj, receiver, ATOM_TO_JSID(name), vp);
 }
 
 static JSBool
-proxy_GetElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp)
+proxy_GetElement(JSContext *cx, JSObject *obj_, JSObject *receiver_, uint32_t index, Value *vp)
 {
+    RootedVarObject obj(cx, obj_);
+    RootedVarObject receiver(cx, receiver_);
+
     jsid id;
     if (!IndexToId(cx, index, &id))
         return false;
     return proxy_GetGeneric(cx, obj, receiver, id, vp);
 }
 
 static JSBool
 proxy_GetElementIfPresent(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index,
@@ -1110,18 +1135,20 @@ proxy_SetGeneric(JSContext *cx, JSObject
 
 static JSBool
 proxy_SetProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *vp, JSBool strict)
 {
     return proxy_SetGeneric(cx, obj, ATOM_TO_JSID(name), vp, strict);
 }
 
 static JSBool
-proxy_SetElement(JSContext *cx, JSObject *obj, uint32_t index, Value *vp, JSBool strict)
+proxy_SetElement(JSContext *cx, JSObject *obj_, uint32_t index, Value *vp, JSBool strict)
 {
+    RootedVarObject obj(cx, obj_);
+
     jsid id;
     if (!IndexToId(cx, index, &id))
         return false;
     return proxy_SetGeneric(cx, obj, id, vp, strict);
 }
 
 static JSBool
 proxy_SetSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *vp, JSBool strict)
@@ -1143,18 +1170,20 @@ proxy_GetGenericAttributes(JSContext *cx
 
 static JSBool
 proxy_GetPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, unsigned *attrsp)
 {
     return proxy_GetGenericAttributes(cx, obj, ATOM_TO_JSID(name), attrsp);
 }
 
 static JSBool
-proxy_GetElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, unsigned *attrsp)
+proxy_GetElementAttributes(JSContext *cx, JSObject *obj_, uint32_t index, unsigned *attrsp)
 {
+    RootedVarObject obj(cx, obj_);
+
     jsid id;
     if (!IndexToId(cx, index, &id))
         return false;
     return proxy_GetGenericAttributes(cx, obj, id, attrsp);
 }
 
 static JSBool
 proxy_GetSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, unsigned *attrsp)
@@ -1177,52 +1206,58 @@ proxy_SetGenericAttributes(JSContext *cx
 
 static JSBool
 proxy_SetPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, unsigned *attrsp)
 {
     return proxy_SetGenericAttributes(cx, obj, ATOM_TO_JSID(name), attrsp);
 }
 
 static JSBool
-proxy_SetElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, unsigned *attrsp)
+proxy_SetElementAttributes(JSContext *cx, JSObject *obj_, uint32_t index, unsigned *attrsp)
 {
+    RootedVarObject obj(cx, obj_);
+
     jsid id;
     if (!IndexToId(cx, index, &id))
         return false;
     return proxy_SetGenericAttributes(cx, obj, id, attrsp);
 }
 
 static JSBool
 proxy_SetSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, unsigned *attrsp)
 {
     return proxy_SetGenericAttributes(cx, obj, SPECIALID_TO_JSID(sid), attrsp);
 }
 
 static JSBool
-proxy_DeleteGeneric(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict)
+proxy_DeleteGeneric(JSContext *cx, JSObject *obj_, jsid id, Value *rval, JSBool strict)
 {
     JS_ASSERT(id == js_CheckForStringIndex(id));
 
+    RootedVarObject obj(cx, obj_);
+
     // TODO: throwing away strict
     bool deleted;
     if (!Proxy::delete_(cx, obj, id, &deleted) || !js_SuppressDeletedProperty(cx, obj, id))
         return false;
     rval->setBoolean(deleted);
     return true;
 }
 
 static JSBool
 proxy_DeleteProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *rval, JSBool strict)
 {
     return proxy_DeleteGeneric(cx, obj, js_CheckForStringIndex(ATOM_TO_JSID(name)), rval, strict);
 }
 
 static JSBool
-proxy_DeleteElement(JSContext *cx, JSObject *obj, uint32_t index, Value *rval, JSBool strict)
+proxy_DeleteElement(JSContext *cx, JSObject *obj_, uint32_t index, Value *rval, JSBool strict)
 {
+    RootedVarObject obj(cx, obj_);
+
     jsid id;
     if (!IndexToId(cx, index, &id))
         return false;
     return proxy_DeleteGeneric(cx, obj, id, rval, strict);
 }
 
 static JSBool
 proxy_DeleteSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *rval, JSBool strict)
@@ -1461,19 +1496,22 @@ JS_FRIEND_DATA(Class) js::FunctionProxyC
         NULL,                /* enumerate       */
         proxy_TypeOf,
         NULL,                /* thisObject      */
         NULL,                /* clear           */
     }
 };
 
 JS_FRIEND_API(JSObject *)
-js::NewProxyObject(JSContext *cx, ProxyHandler *handler, const Value &priv, JSObject *proto,
-                   JSObject *parent, JSObject *call, JSObject *construct)
+js::NewProxyObject(JSContext *cx, ProxyHandler *handler, const Value &priv_, JSObject *proto_,
+                   JSObject *parent_, JSObject *call_, JSObject *construct_)
 {
+    RootedVarValue priv(cx, priv_);
+    RootedVarObject proto(cx, proto_), parent(cx, parent_), call(cx, call_), construct(cx, construct_);
+
     JS_ASSERT_IF(proto, cx->compartment == proto->compartment());
     JS_ASSERT_IF(parent, cx->compartment == parent->compartment());
     bool fun = call || construct;
     Class *clasp;
     if (fun)
         clasp = &FunctionProxyClass;
     else
         clasp = handler->isOuterWindow() ? &OuterWindowProxyClass : &ObjectProxyClass;
@@ -1602,19 +1640,21 @@ Class js::ProxyClass = {
     JS_PropertyStub,         /* getProperty */
     JS_StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub
 };
 
 JS_FRIEND_API(JSObject *)
-js_InitProxyClass(JSContext *cx, JSObject *obj)
+js_InitProxyClass(JSContext *cx, JSObject *obj_)
 {
-    JSObject *module = NewObjectWithClassProto(cx, &ProxyClass, NULL, obj);
+    RootedVarObject obj(cx, obj_);
+
+    RootedVarObject module(cx, NewObjectWithClassProto(cx, &ProxyClass, NULL, obj));
     if (!module || !module->setSingletonType(cx))
         return NULL;
 
     if (!JS_DefineProperty(cx, obj, "Proxy", OBJECT_TO_JSVAL(module),
                            JS_PropertyStub, JS_StrictPropertyStub, 0)) {
         return NULL;
     }
     if (!JS_DefineFunctions(cx, module, static_methods))
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -256,16 +256,17 @@ class SkipRoot;
 enum ThingRootKind
 {
     THING_ROOT_OBJECT,
     THING_ROOT_SHAPE,
     THING_ROOT_BASE_SHAPE,
     THING_ROOT_TYPE_OBJECT,
     THING_ROOT_STRING,
     THING_ROOT_SCRIPT,
+    THING_ROOT_XML,
     THING_ROOT_ID,
     THING_ROOT_VALUE,
     THING_ROOT_LIMIT
 };
 
 struct ContextFriendFields {
     JSRuntime *const    runtime;
 
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -3097,16 +3097,18 @@ ASTSerializer::functionBody(ParseNode *p
     return builder.blockStatement(elts, pos, dst);
 }
 
 } /* namespace js */
 
 static JSBool
 reflect_parse(JSContext *cx, uint32_t argc, jsval *vp)
 {
+    cx->runtime->gcExactScanningEnabled = false;
+
     if (argc < 1) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
                              "Reflect.parse", "0", "s");
         return JS_FALSE;
     }
 
     JSString *src = ToString(cx, JS_ARGV(cx, vp)[0]);
     if (!src)
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -537,16 +537,18 @@ JSObject::addPropertyInternal(JSContext 
                               unsigned flags, int shortid, Shape **spp,
                               bool allowDictionary)
 {
     JS_ASSERT_IF(!allowDictionary, !inDictionaryMode());
 
     RootId idRoot(cx, &id);
     RootedVarObject self(cx, this);
 
+    RootGetterSetter gsRoot(cx, attrs, &getter, &setter);
+
     PropertyTable *table = NULL;
     if (!inDictionaryMode()) {
         bool stableSlot =
             (slot == SHAPE_INVALID_SLOT) ||
             lastProperty()->hasMissingSlot() ||
             (slot == lastProperty()->maybeSlot() + 1);
         JS_ASSERT_IF(!allowDictionary, stableSlot);
         if (allowDictionary &&
@@ -645,16 +647,17 @@ JSObject::putProperty(JSContext *cx, jsi
 {
     JS_ASSERT(!JSID_IS_VOID(id));
 
     RootId idRoot(cx, &id);
 
     NormalizeGetterAndSetter(cx, this, id, attrs, flags, getter, setter);
 
     RootedVarObject self(cx, this);
+    RootGetterSetter gsRoot(cx, attrs, &getter, &setter);
 
     /* Search for id in order to claim its entry if table has been allocated. */
     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.
@@ -856,31 +859,31 @@ JSObject::removeProperty(JSContext *cx, 
 
     /*
      * 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;
+    RootedVarShape spare(cx);
     if (self->inDictionaryMode()) {
         spare = js_NewGCShape(cx);
         if (!spare)
             return false;
         new (spare) Shape(shape->base()->unowned(), 0);
-        if (shape == lastProperty()) {
+        if (shape == self->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;
-            StackBaseShape base(lastProperty()->base());
+            RootedVarShape previous(cx, self->lastProperty()->parent);
+            StackBaseShape base(self->lastProperty()->base());
             base.updateGetterSetter(previous->attrs, previous->getter(), previous->setter());
             BaseShape *nbase = BaseShape::getUnowned(cx, base);
             if (!nbase)
                 return false;
             previous->base_ = nbase;
         }
     }
 
@@ -1036,41 +1039,41 @@ bool
 JSObject::shadowingShapeChange(JSContext *cx, const Shape &shape)
 {
     return generateOwnShape(cx);
 }
 
 bool
 JSObject::clearParent(JSContext *cx)
 {
-    return setParent(cx, NULL);
+    return setParent(cx, RootedVarObject(cx, this), RootedVarObject(cx));
 }
 
-bool
-JSObject::setParent(JSContext *cx, JSObject *parent)
+/* static */ bool
+JSObject::setParent(JSContext *cx, HandleObject obj, HandleObject parent)
 {
     if (parent && !parent->setDelegate(cx))
         return false;
 
-    if (inDictionaryMode()) {
-        StackBaseShape base(lastProperty());
+    if (obj->inDictionaryMode()) {
+        StackBaseShape base(obj->lastProperty());
         base.parent = parent;
         UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
         if (!nbase)
             return false;
 
-        lastProperty()->base()->adoptUnowned(nbase);
+        obj->lastProperty()->base()->adoptUnowned(nbase);
         return true;
     }
 
-    Shape *newShape = Shape::setObjectParent(cx, parent, getProto(), shape_);
+    Shape *newShape = Shape::setObjectParent(cx, parent, obj->getProto(), obj->shape_);
     if (!newShape)
         return false;
 
-    shape_ = newShape;
+    obj->shape_ = newShape;
     return true;
 }
 
 /* static */ Shape *
 Shape::setObjectParent(JSContext *cx, JSObject *parent, JSObject *proto, Shape *last)
 {
     if (last->getObjectParent() == parent)
         return last;
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -892,16 +892,31 @@ struct Shape : public js::gc::Cell
   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);
     }
 };
 
+class RootGetterSetter
+{
+    mozilla::Maybe<RootObject> getterRoot;
+    mozilla::Maybe<RootObject> setterRoot;
+
+  public:
+    RootGetterSetter(JSContext *cx, uint8_t attrs, PropertyOp *pgetter, StrictPropertyOp *psetter)
+    {
+        if (attrs & JSPROP_GETTER)
+            getterRoot.construct(cx, (JSObject **) pgetter);
+        if (attrs & JSPROP_SETTER)
+            setterRoot.construct(cx, (JSObject **) psetter);
+    }
+};
+
 struct EmptyShape : public js::Shape
 {
     EmptyShape(UnownedBaseShape *base, uint32_t nfixed);
 
     /*
      * Lookup an initial shape matching the given parameters, creating an empty
      * shape if none was found.
      */
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -140,17 +140,17 @@ Bindings::add(JSContext *cx, HandleAtom 
         indexp = &nvars;
         getter = CallObject::getVarOp;
         setter = CallObject::setVarOp;
         if (kind == CONSTANT)
             attrs |= JSPROP_READONLY;
         slot += nargs + nvars;
     }
 
-    jsid id;
+    RootedVarId id(cx);
     if (!name) {
         JS_ASSERT(kind == ARGUMENT); /* destructuring */
         id = INT_TO_JSID(nargs);
     } else {
         id = ATOM_TO_JSID(name);
     }
 
     StackBaseShape base(&CallClass, NULL, BaseShape::VAROBJ);
@@ -439,16 +439,18 @@ js::XDRScript(XDRState<mode> *xdr, JSScr
     if (mode == XDR_DECODE) {
         nargs = argsVars >> 16;
         nvars = argsVars & 0xFFFF;
     }
     JS_ASSERT(nargs != Bindings::BINDING_COUNT_LIMIT);
     JS_ASSERT(nvars != Bindings::BINDING_COUNT_LIMIT);
 
     Bindings bindings(cx);
+    Bindings::StackRoot bindingsRoot(cx, &bindings);
+
     uint32_t nameCount = nargs + nvars;
     if (nameCount > 0) {
         LifoAllocScope las(&cx->tempLifoAlloc());
 
         /*
          * To xdr the names we prefix the names with a bitmap descriptor and
          * then xdr the names as strings. For argument names (indexes below
          * nargs) the corresponding bit in the bitmap is unset when the name
@@ -1277,17 +1279,17 @@ JSScript::NewScript(JSContext *cx, uint3
     JS_ASSERT(script->getVersion() == version);
     return script;
 }
 
 JSScript *
 JSScript::NewScriptFromEmitter(JSContext *cx, BytecodeEmitter *bce)
 {
     uint32_t mainLength, prologLength, nfixed;
-    JSScript *script;
+    RootedVar<JSScript*> script(cx);
     const char *filename;
     JSFunction *fun;
 
     /* The counts of indexed things must be checked during code generation. */
     JS_ASSERT(bce->atomIndices->count() <= INDEX_LIMIT);
     JS_ASSERT(bce->objectList.length <= INDEX_LIMIT);
     JS_ASSERT(bce->regexpList.length <= INDEX_LIMIT);
 
@@ -2041,79 +2043,81 @@ void
 JSScript::setNeedsArgsObj(bool needsArgsObj)
 {
     JS_ASSERT(!analyzedArgsUsage());
     JS_ASSERT_IF(needsArgsObj, argumentsHasLocalBinding());
     needsArgsAnalysis_ = false;
     needsArgsObj_ = needsArgsObj;
 }
 
-bool
-JSScript::applySpeculationFailed(JSContext *cx)
+/* static */ bool
+JSScript::applySpeculationFailed(JSContext *cx, JSScript *script_)
 {
-    JS_ASSERT(analyzedArgsUsage());
-    JS_ASSERT(argumentsHasLocalBinding());
+    RootedVar<JSScript*> script(cx, script_);
+
+    JS_ASSERT(script->analyzedArgsUsage());
+    JS_ASSERT(script->argumentsHasLocalBinding());
 
     /*
      * It is possible that the apply speculation has already failed, everything
      * has been fixed up, but there was an outstanding magic value on the
      * stack that has just now flowed into an apply. In this case, there is
      * nothing to do; GuardFunApplySpeculation will patch in the real argsobj.
      */
-    if (needsArgsObj())
+    if (script->needsArgsObj())
         return true;
 
-    needsArgsObj_ = true;
+    script->needsArgsObj_ = true;
 
-    const unsigned slot = argumentsLocalSlot();
+    const unsigned slot = script->argumentsLocalSlot();
 
     /*
      * By design, the apply-arguments optimization is only made when there
      * are no outstanding cases of MagicValue(JS_OPTIMIZED_ARGUMENTS) other
      * than this particular invocation of 'f.apply(x, arguments)'. Thus, there
      * are no outstanding values of MagicValue(JS_OPTIMIZED_ARGUMENTS) on the
      * stack. However, there are three things that need fixup:
      *  - there may be any number of activations of this script that don't have
      *    an argsObj that now need one.
      *  - jit code compiled (and possible active on the stack) with the static
      *    assumption of !script->needsArgsObj();
      *  - type inference data for the script assuming script->needsArgsObj; and
      */
     for (AllFramesIter i(cx->stack.space()); !i.done(); ++i) {
         StackFrame *fp = i.fp();
-        if (fp->isFunctionFrame() && fp->script() == this) {
+        if (fp->isFunctionFrame() && fp->script() == script) {
             if (!fp->hasArgsObj()) {
                 ArgumentsObject *obj = ArgumentsObject::create(cx, fp);
                 if (!obj) {
                     /*
                      * We can't leave stack frames where script->needsArgsObj
                      * and !fp->hasArgsObj. It is, however, safe to leave frames
                      * where fp->hasArgsObj and !fp->script->needsArgsObj.
                      */
-                    needsArgsObj_ = false;
+                    script->needsArgsObj_ = false;
                     return false;
                 }
 
                 /* Note: 'arguments' may have already been overwritten. */
                 if (fp->localSlot(slot).isMagic(JS_OPTIMIZED_ARGUMENTS))
                     fp->localSlot(slot) = ObjectValue(*obj);
             }
         }
     }
 
 #ifdef JS_METHODJIT
-    if (hasJITCode()) {
-        mjit::Recompiler::clearStackReferences(cx->runtime->defaultFreeOp(), this);
-        mjit::ReleaseScriptCode(cx->runtime->defaultFreeOp(), this);
+    if (script->hasJITCode()) {
+        mjit::Recompiler::clearStackReferences(cx->runtime->defaultFreeOp(), script);
+        mjit::ReleaseScriptCode(cx->runtime->defaultFreeOp(), script);
     }
 #endif
 
-    if (hasAnalysis() && analysis()->ranInference()) {
+    if (script->hasAnalysis() && script->analysis()->ranInference()) {
         types::AutoEnterTypeInference enter(cx);
-        types::TypeScript::MonitorUnknown(cx, this, argumentsBytecode());
+        types::TypeScript::MonitorUnknown(cx, script, script->argumentsBytecode());
     }
 
     return true;
 }
 
 #ifdef DEBUG
 bool
 JSScript::varIsAliased(unsigned varSlot)
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -599,17 +599,17 @@ struct JSScript : public js::gc::Cell
      * write MagicValue(JS_OPTIMIZED_ARGUMENTS) to 'arguments's slot and any
      * uses of 'arguments' will be guaranteed to handle this magic value.
      * So avoid spurious arguments object creation, we maintain the invariant
      * that needsArgsObj is only called after the script has been analyzed.
      */
     bool analyzedArgsUsage() const { return !needsArgsAnalysis_; }
     bool needsArgsObj() const { JS_ASSERT(analyzedArgsUsage()); return needsArgsObj_; }
     void setNeedsArgsObj(bool needsArgsObj);
-    bool applySpeculationFailed(JSContext *cx);
+    static bool applySpeculationFailed(JSContext *cx, JSScript *script);
 
     /* Hash table chaining for JSCompartment::evalCache. */
     JSScript *&evalHashLink() { return *globalObject.unsafeGetUnioned(); }
 
     /*
      * Original compiled function for the script, if it has a function.
      * NULL for global and eval scripts.
      */
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -397,40 +397,43 @@ static JSFunctionSpec string_functions[]
 };
 
 jschar      js_empty_ucstr[]  = {0};
 JSSubString js_EmptySubString = {0, js_empty_ucstr};
 
 static const unsigned STRING_ELEMENT_ATTRS = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT;
 
 static JSBool
-str_enumerate(JSContext *cx, JSObject *obj)
+str_enumerate(JSContext *cx, JSObject *obj_)
 {
-    JSString *str = obj->asString().unbox();
+    RootedVarObject obj(cx, obj_);
+    RootedVarString str(cx, obj->asString().unbox());
     for (size_t i = 0, length = str->length(); i < length; i++) {
         JSString *str1 = js_NewDependentString(cx, str, i, 1);
         if (!str1)
             return false;
         if (!obj->defineElement(cx, i, StringValue(str1),
                                 JS_PropertyStub, JS_StrictPropertyStub,
                                 STRING_ELEMENT_ATTRS)) {
             return false;
         }
     }
 
     return true;
 }
 
 static JSBool
-str_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
+str_resolve(JSContext *cx, JSObject *obj_, jsid id, unsigned flags,
             JSObject **objp)
 {
     if (!JSID_IS_INT(id))
         return JS_TRUE;
 
+    RootedVarObject obj(cx, obj_);
+
     JSString *str = obj->asString().unbox();
 
     int32_t slot = JSID_TO_INT(id);
     if ((size_t)slot < str->length()) {
         JSString *str1 = cx->runtime->staticStrings.getUnitStringForElement(cx, str, size_t(slot));
         if (!str1)
             return JS_FALSE;
         if (!obj->defineElement(cx, uint32_t(slot), StringValue(str1), NULL, NULL,
@@ -1333,25 +1336,25 @@ str_trimRight(JSContext *cx, unsigned ar
 
 /*
  * Perl-inspired string functions.
  */
 
 /* Result of a successfully performed flat match. */
 class FlatMatch
 {
-    JSAtom       *patstr;
+    RootedVarAtom patstr;
     const jschar *pat;
     size_t       patlen;
     int32_t      match_;
 
     friend class StringRegExpGuard;
 
   public:
-    FlatMatch() : patstr(NULL) {} /* Old GCC wants this initialization. */
+    FlatMatch(JSContext *cx) : patstr(cx) {}
     JSLinearString *pattern() const { return patstr; }
     size_t patternLength() const { return patlen; }
 
     /*
      * Note: The match is -1 when the match is performed successfully,
      * but no match is found.
      */
     int32_t match() const { return match_; }
@@ -1419,17 +1422,17 @@ class StringRegExpGuard
                 if (!sb.append(*it))
                     return NULL;
             }
         }
         return sb.finishAtom();
     }
 
   public:
-    StringRegExpGuard() {}
+    StringRegExpGuard(JSContext *cx) : fm(cx) {}
 
     /* init must succeed in order to call tryFlatMatch or normalizeRegExp. */
     bool init(JSContext *cx, CallArgs args, bool convertVoid = false)
     {
         if (args.length() != 0 && IsObjectWithClass(args[0], ESClass_RegExp, cx)) {
             if (!RegExpToShared(cx, args[0].toObject(), &re_))
                 return false;
         } else {
@@ -1585,25 +1588,25 @@ DoMatch(JSContext *cx, RegExpStatics *re
             return false;
         if (callbackOnSingle && Matched(type, *rval) && !callback(cx, res, 0, data))
             return false;
     }
     return true;
 }
 
 static bool
-BuildFlatMatchArray(JSContext *cx, JSString *textstr, const FlatMatch &fm, CallArgs *args)
+BuildFlatMatchArray(JSContext *cx, HandleString textstr, const FlatMatch &fm, CallArgs *args)
 {
     if (fm.match() < 0) {
         args->rval() = NullValue();
         return true;
     }
 
     /* For this non-global match, produce a RegExp.exec-style array. */
-    JSObject *obj = NewSlowEmptyArray(cx);
+    RootedVarObject obj(cx, NewSlowEmptyArray(cx));
     if (!obj)
         return false;
 
     if (!obj->defineElement(cx, 0, StringValue(fm.pattern())) ||
         !obj->defineProperty(cx, cx->runtime->atomState.indexAtom, Int32Value(fm.match())) ||
         !obj->defineProperty(cx, cx->runtime->atomState.inputAtom, StringValue(textstr)))
     {
         return false;
@@ -1634,21 +1637,21 @@ MatchCallback(JSContext *cx, RegExpStati
     Value v;
     return res->createLastMatch(cx, &v) && arrayobj->defineElement(cx, count, v);
 }
 
 JSBool
 js::str_match(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    JSString *str = ThisToStringForStringProto(cx, args);
+    RootedVarString str(cx, ThisToStringForStringProto(cx, args));
     if (!str)
         return false;
 
-    StringRegExpGuard g;
+    StringRegExpGuard g(cx);
     if (!g.init(cx, args, true))
         return false;
 
     if (const FlatMatch *fm = g.tryFlatMatch(cx, str, 1, args.length()))
         return BuildFlatMatchArray(cx, str, *fm, &args);
 
     /* Return if there was an error in tryFlatMatch. */
     if (cx->isExceptionPending())
@@ -1670,21 +1673,21 @@ js::str_match(JSContext *cx, unsigned ar
         args.rval() = rval;
     return true;
 }
 
 JSBool
 js::str_search(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    JSString *str = ThisToStringForStringProto(cx, args);
+    RootedVarString str(cx, ThisToStringForStringProto(cx, args));
     if (!str)
         return false;
 
-    StringRegExpGuard g;
+    StringRegExpGuard g(cx);
     if (!g.init(cx, args, true))
         return false;
     if (const FlatMatch *fm = g.tryFlatMatch(cx, str, 1, args.length())) {
         args.rval() = Int32Value(fm->match());
         return true;
     }
 
     if (cx->isExceptionPending())  /* from tryFlatMatch */
@@ -1712,26 +1715,29 @@ js::str_search(JSContext *cx, unsigned a
     else
         args.rval() = Int32Value(-1);
     return true;
 }
 
 struct ReplaceData
 {
     ReplaceData(JSContext *cx)
-      : str(cx), lambda(cx), elembase(cx), repstr(cx), sb(cx)
+      : str(cx), g(cx), lambda(cx), elembase(cx), repstr(cx),
+        dollarRoot(cx, &dollar), dollarEndRoot(cx, &dollarEnd), sb(cx)
     {}
 
     RootedVarString    str;            /* 'this' parameter object as a string */
     StringRegExpGuard  g;              /* regexp parameter object and private data */
     RootedVarObject    lambda;         /* replacement function object or null */
     RootedVarObject    elembase;       /* object for function(a){return b[a]} replace */
     RootedVar<JSLinearString*> repstr; /* replacement string */
     const jschar       *dollar;        /* null or pointer to first $ in repstr */
     const jschar       *dollarEnd;     /* limit pointer for js_strchr_limit */
+    SkipRoot           dollarRoot;     /* XXX prevent dollar from being relocated */
+    SkipRoot           dollarEndRoot;  /* ditto */
     int                leftIndex;      /* left context index in str->chars */
     JSSubString        dollarStr;      /* for "$$" InterpretDollar result */
     bool               calledBack;     /* record whether callback has been called */
     InvokeArgsGuard    args;           /* arguments for lambda call */
     StringBuffer       sb;             /* buffer built during DoMatch */
 };
 
 static bool
@@ -1839,17 +1845,17 @@ FindReplaceLength(JSContext *cx, RegExpS
         /*
          * Couldn't handle this property, fall through and despecialize to the
          * general lambda case.
          */
         rdata.elembase = NULL;
     }
 
     if (JSObject *lambda = rdata.lambda) {
-        PreserveRegExpStatics staticsGuard(res);
+        PreserveRegExpStatics staticsGuard(cx, res);
         if (!staticsGuard.init(cx))
             return false;
 
         /*
          * In the lambda case, not only do we find the replacement string's
          * length, we compute repstr and return it via rdata for use within
          * DoReplace.  The lambda is called with arguments ($&, $1, $2, ...,
          * index, input), i.e., all the properties of a regexp match array.
@@ -1983,17 +1989,17 @@ BuildFlatReplacement(JSContext *cx, Hand
          * If we are replacing over a rope, avoid flattening it by iterating
          * through it, building a new rope.
          */
         StringSegmentRange r(cx);
         if (!r.init(textstr))
             return false;
         size_t pos = 0;
         while (!r.empty()) {
-            JSString *str = r.front();
+            RootedVarString str(cx, r.front());
             size_t len = str->length();
             size_t strEnd = pos + len;
             if (pos < matchEnd && strEnd > match) {
                 /*
                  * We need to special-case any part of the rope that overlaps
                  * with the replacement string.
                  */
                 if (match >= pos) {
@@ -2056,17 +2062,17 @@ BuildFlatReplacement(JSContext *cx, Hand
  * constructing a result string that looks like:
  *
  *      newstring = string[:matchStart] + dollarSub(replaceValue) + string[matchLimit:]
  */
 static inline bool
 BuildDollarReplacement(JSContext *cx, JSString *textstrArg, JSLinearString *repstr,
                        const jschar *firstDollar, const FlatMatch &fm, CallArgs *args)
 {
-    JSLinearString *textstr = textstrArg->ensureLinear(cx);
+    RootedVar<JSLinearString*> textstr(cx, textstrArg->ensureLinear(cx));
     if (!textstr)
         return NULL;
 
     JS_ASSERT(repstr->chars() <= firstDollar && firstDollar < repstr->chars() + repstr->length());
     size_t matchStart = fm.match();
     size_t matchLimit = matchStart + fm.patternLength();
 
     /*
@@ -2584,17 +2590,17 @@ class SplitStringMatcher
 
 /* ES5 15.5.4.14 */
 JSBool
 js::str_split(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* Steps 1-2. */
-    JSString *str = ThisToStringForStringProto(cx, args);
+    RootedVarString str(cx, ThisToStringForStringProto(cx, args));
     if (!str)
         return false;
 
     RootedVarTypeObject type(cx, GetTypeCallerInitObject(cx, JSProto_Array));
     if (!type)
         return false;
     AddTypeProperty(cx, type, NULL, Type::StringType());
 
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -265,30 +265,32 @@ ArrayBufferObject::allocateSlots(JSConte
     header->initializedLength = 0;
     header->length = size;
     header->unused = 0;
 
     return true;
 }
 
 static JSObject *
-DelegateObject(JSContext *cx, JSObject *obj)
+DelegateObject(JSContext *cx, HandleObject obj)
 {
     if (!obj->getPrivate()) {
         JSObject *delegate = NewObjectWithGivenProto(cx, &ObjectClass, obj->getProto(), NULL);
         obj->setPrivate(delegate);
         return delegate;
     }
     return static_cast<JSObject*>(obj->getPrivate());
 }
 
 JSObject *
 ArrayBufferObject::create(JSContext *cx, uint32_t nbytes, uint8_t *contents)
 {
-    JSObject *obj = NewBuiltinClassInstance(cx, &ArrayBufferObject::protoClass);
+    SkipRoot skip(cx, &contents);
+
+    RootedVarObject obj(cx, NewBuiltinClassInstance(cx, &ArrayBufferObject::protoClass));
     if (!obj)
         return NULL;
 #ifdef JS_THREADSAFE
     JS_ASSERT(obj->getAllocKind() == gc::FINALIZE_OBJECT16_BACKGROUND);
 #else
     JS_ASSERT(obj->getAllocKind() == gc::FINALIZE_OBJECT16);
 #endif
 
@@ -338,26 +340,29 @@ ArrayBufferObject::obj_trace(JSTracer *t
         MarkObjectUnbarriered(trc, &delegate, "arraybuffer.delegate");
         obj->setPrivateUnbarriered(delegate);
     }
 }
 
 static JSProperty * const PROPERTY_FOUND = reinterpret_cast<JSProperty *>(1);
 
 JSBool
-ArrayBufferObject::obj_lookupGeneric(JSContext *cx, JSObject *obj, jsid id,
+ArrayBufferObject::obj_lookupGeneric(JSContext *cx, JSObject *obj_, jsid id_,
                                      JSObject **objp, JSProperty **propp)
 {
+    RootedVarObject obj(cx, obj_);
+    RootedVarId id(cx, id_);
+
     if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom)) {
         *propp = PROPERTY_FOUND;
         *objp = getArrayBuffer(obj);
         return true;
     }
 
-    JSObject *delegate = DelegateObject(cx, obj);
+    RootedVarObject delegate(cx, DelegateObject(cx, obj));
     if (!delegate)
         return false;
 
     JSBool delegateResult = delegate->lookupGeneric(cx, id, objp, propp);
 
     /* If false, there was an error, so propagate it.
      * Otherwise, if propp is non-null, the property
      * was found. Otherwise it was not
@@ -385,20 +390,22 @@ ArrayBufferObject::obj_lookupGeneric(JSC
 JSBool
 ArrayBufferObject::obj_lookupProperty(JSContext *cx, JSObject *obj, PropertyName *name,
                                       JSObject **objp, JSProperty **propp)
 {
     return obj_lookupGeneric(cx, obj, ATOM_TO_JSID(name), objp, propp);
 }
 
 JSBool
-ArrayBufferObject::obj_lookupElement(JSContext *cx, JSObject *obj, uint32_t index,
+ArrayBufferObject::obj_lookupElement(JSContext *cx, JSObject *obj_, uint32_t index,
                                      JSObject **objp, JSProperty **propp)
 {
-    JSObject *delegate = DelegateObject(cx, obj);
+    RootedVarObject obj(cx, obj_);
+
+    RootedVarObject delegate(cx, DelegateObject(cx, obj));
     if (!delegate)
         return false;
 
     /*
      * If false, there was an error, so propagate it.
      * Otherwise, if propp is non-null, the property
      * was found. Otherwise it was not
      * found so look in the prototype chain.
@@ -423,115 +430,135 @@ ArrayBufferObject::obj_lookupElement(JSC
 JSBool
 ArrayBufferObject::obj_lookupSpecial(JSContext *cx, JSObject *obj, SpecialId sid,
                                      JSObject **objp, JSProperty **propp)
 {
     return obj_lookupGeneric(cx, obj, SPECIALID_TO_JSID(sid), objp, propp);
 }
 
 JSBool
-ArrayBufferObject::obj_defineGeneric(JSContext *cx, JSObject *obj, jsid id, const Value *v,
+ArrayBufferObject::obj_defineGeneric(JSContext *cx, JSObject *obj_, jsid id_, const Value *v,
                                      PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
-    if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom))
+    if (JSID_IS_ATOM(id_, cx->runtime->atomState.byteLengthAtom))
         return true;
 
+    RootedVarObject obj(cx, obj_);
+    RootedVarId id(cx, id_);
+    RootGetterSetter gsRoot(cx, attrs, &getter, &setter);
+
     JSObject *delegate = DelegateObject(cx, obj);
     if (!delegate)
         return false;
     return js_DefineProperty(cx, delegate, id, v, getter, setter, attrs);
 }
 
 JSBool
 ArrayBufferObject::obj_defineProperty(JSContext *cx, JSObject *obj,
                                       PropertyName *name, const Value *v,
                                       PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
     return obj_defineGeneric(cx, obj, ATOM_TO_JSID(name), v, getter, setter, attrs);
 }
 
 JSBool
-ArrayBufferObject::obj_defineElement(JSContext *cx, JSObject *obj, uint32_t index, const Value *v,
+ArrayBufferObject::obj_defineElement(JSContext *cx, JSObject *obj_, uint32_t index, const Value *v,
                                      PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
+    RootedVarObject obj(cx, obj_);
+    RootGetterSetter gsRoot(cx, attrs, &getter, &setter);
+
     JSObject *delegate = DelegateObject(cx, obj);
     if (!delegate)
         return false;
     return js_DefineElement(cx, delegate, index, v, getter, setter, attrs);
 }
 
 JSBool
 ArrayBufferObject::obj_defineSpecial(JSContext *cx, JSObject *obj, SpecialId sid, const Value *v,
                                      PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
     return obj_defineGeneric(cx, obj, SPECIALID_TO_JSID(sid), v, getter, setter, attrs);
 }
 
 JSBool
-ArrayBufferObject::obj_getGeneric(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
+ArrayBufferObject::obj_getGeneric(JSContext *cx, JSObject *obj_, JSObject *receiver_, jsid id_, Value *vp)
 {
+    RootedVarObject obj(cx, obj_), receiver(cx, receiver_);
+    RootedVarId id(cx, id_);
+
     obj = getArrayBuffer(obj);
     JS_ASSERT(obj);
     if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom)) {
         vp->setInt32(obj->asArrayBuffer().byteLength());
         return true;
     }
 
     RootedVarObject delegate(cx, DelegateObject(cx, obj));
     if (!delegate)
         return false;
-    return js_GetProperty(cx, delegate, RootedVarObject(cx, receiver), id, vp);
+    return js_GetProperty(cx, delegate, receiver, id, vp);
 }
 
 JSBool
-ArrayBufferObject::obj_getProperty(JSContext *cx, JSObject *obj,
-                                   JSObject *receiver, PropertyName *name, Value *vp)
+ArrayBufferObject::obj_getProperty(JSContext *cx, JSObject *obj_,
+                                   JSObject *receiver_, PropertyName *name_, Value *vp)
 {
+    RootedVarObject obj(cx, obj_), receiver(cx, receiver_);
+    RootedVarPropertyName name(cx, name_);
+
     obj = getArrayBuffer(obj);
     if (name == cx->runtime->atomState.byteLengthAtom) {
         vp->setInt32(obj->asArrayBuffer().byteLength());
         return true;
     }
 
     RootedVarObject delegate(cx, DelegateObject(cx, obj));
     if (!delegate)
         return false;
-    return js_GetProperty(cx, delegate, RootedVarObject(cx, receiver), ATOM_TO_JSID(name), vp);
+    return js_GetProperty(cx, delegate, receiver, ATOM_TO_JSID(name), vp);
 }
 
 JSBool
 ArrayBufferObject::obj_getElement(JSContext *cx, JSObject *obj,
-                                  JSObject *receiver, uint32_t index, Value *vp)
+                                  JSObject *receiver_, uint32_t index, Value *vp)
 {
-    RootedVarObject delegate(cx, DelegateObject(cx, getArrayBuffer(obj)));
+    RootedVarObject receiver(cx, receiver_);
+
+    RootedVarObject delegate(cx, DelegateObject(cx, RootedVarObject(cx, getArrayBuffer(obj))));
     if (!delegate)
         return false;
-    return js_GetElement(cx, delegate, RootedVarObject(cx, receiver), index, vp);
+    return js_GetElement(cx, delegate, receiver, index, vp);
 }
 
 JSBool
-ArrayBufferObject::obj_getElementIfPresent(JSContext *cx, JSObject *obj, JSObject *receiver,
+ArrayBufferObject::obj_getElementIfPresent(JSContext *cx, JSObject *obj, JSObject *receiver_,
                                            uint32_t index, Value *vp, bool *present)
 {
-    JSObject *delegate = DelegateObject(cx, getArrayBuffer(obj));
+    RootedVarObject receiver(cx, receiver_);
+
+    JSObject *delegate = DelegateObject(cx, RootedVarObject(cx, getArrayBuffer(obj)));
     if (!delegate)
         return false;
     return delegate->getElementIfPresent(cx, receiver, index, vp, present);
 }
 
 JSBool
 ArrayBufferObject::obj_getSpecial(JSContext *cx, JSObject *obj,
                                   JSObject *receiver, SpecialId sid, Value *vp)
 {
     return obj_getGeneric(cx, obj, receiver, SPECIALID_TO_JSID(sid), vp);
 }
 
 JSBool
-ArrayBufferObject::obj_setGeneric(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
+ArrayBufferObject::obj_setGeneric(JSContext *cx, JSObject *obj_, jsid id_, Value *vp, JSBool strict)
 {
+    RootedVarObject obj(cx, obj_);
+    RootedVarId id(cx, id_);
+
     if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom))
         return true;
 
     RootedVarObject delegate(cx, DelegateObject(cx, obj));
     if (!delegate)
         return false;
 
     if (JSID_IS_ATOM(id, cx->runtime->atomState.protoAtom)) {
@@ -549,29 +576,29 @@ ArrayBufferObject::obj_setGeneric(JSCont
         // if the delegate's prototype has now changed
         // then we change our prototype too.
         //
         // otherwise __proto__ was a plain property
         // and we don't modify our prototype chain
         // since obj_getProperty will fetch it as a plain
         // property from the delegate.
 
-        JSObject *oldDelegateProto = delegate->getProto();
+        RootedVarObject oldDelegateProto(cx, delegate->getProto());
 
         if (!js_SetPropertyHelper(cx, delegate, id, 0, vp, strict))
             return false;
 
         if (delegate->getProto() != oldDelegateProto) {
             // actual __proto__ was set and not a plain property called
             // __proto__
             if (!obj->isExtensible()) {
                 obj->reportNotExtensible(cx);
                 return false;
             }
-            if (!SetProto(cx, obj, vp->toObjectOrNull(), true)) {
+            if (!SetProto(cx, obj, RootedVarObject(cx, vp->toObjectOrNull()), true)) {
                 // this can be caused for example by setting x.__proto__ = x
                 // restore delegate prototype chain
                 SetProto(cx, delegate, oldDelegateProto, true);
                 return false;
             }
         }
         return true;
     }
@@ -582,37 +609,42 @@ ArrayBufferObject::obj_setGeneric(JSCont
 JSBool
 ArrayBufferObject::obj_setProperty(JSContext *cx, JSObject *obj,
                                    PropertyName *name, Value *vp, JSBool strict)
 {
     return obj_setGeneric(cx, obj, ATOM_TO_JSID(name), vp, strict);
 }
 
 JSBool
-ArrayBufferObject::obj_setElement(JSContext *cx, JSObject *obj,
+ArrayBufferObject::obj_setElement(JSContext *cx, JSObject *obj_,
                                   uint32_t index, Value *vp, JSBool strict)
 {
+    RootedVarObject obj(cx, obj_);
+
     RootedVarObject delegate(cx, DelegateObject(cx, obj));
     if (!delegate)
         return false;
 
     return js_SetElementHelper(cx, delegate, index, 0, vp, strict);
 }
 
 JSBool
 ArrayBufferObject::obj_setSpecial(JSContext *cx, JSObject *obj,
                                   SpecialId sid, Value *vp, JSBool strict)
 {
     return obj_setGeneric(cx, obj, SPECIALID_TO_JSID(sid), vp, strict);
 }
 
 JSBool
-ArrayBufferObject::obj_getGenericAttributes(JSContext *cx, JSObject *obj,
-                                            jsid id, unsigned *attrsp)
+ArrayBufferObject::obj_getGenericAttributes(JSContext *cx, JSObject *obj_,
+                                            jsid id_, unsigned *attrsp)
 {
+    RootedVarObject obj(cx, obj_);
+    RootedVarId id(cx, id_);
+
     if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom)) {
         *attrsp = JSPROP_PERMANENT | JSPROP_READONLY;
         return true;
     }
 
     JSObject *delegate = DelegateObject(cx, obj);
     if (!delegate)
         return false;
@@ -622,36 +654,41 @@ ArrayBufferObject::obj_getGenericAttribu
 JSBool
 ArrayBufferObject::obj_getPropertyAttributes(JSContext *cx, JSObject *obj,
                                              PropertyName *name, unsigned *attrsp)
 {
     return obj_getGenericAttributes(cx, obj, ATOM_TO_JSID(name), attrsp);
 }
 
 JSBool
-ArrayBufferObject::obj_getElementAttributes(JSContext *cx, JSObject *obj,
+ArrayBufferObject::obj_getElementAttributes(JSContext *cx, JSObject *obj_,
                                             uint32_t index, unsigned *attrsp)
 {
+    RootedVarObject obj(cx, obj_);
+
     JSObject *delegate = DelegateObject(cx, obj);
     if (!delegate)
         return false;
     return js_GetElementAttributes(cx, delegate, index, attrsp);
 }
 
 JSBool
 ArrayBufferObject::obj_getSpecialAttributes(JSContext *cx, JSObject *obj,
                                             SpecialId sid, unsigned *attrsp)
 {
     return obj_getGenericAttributes(cx, obj, SPECIALID_TO_JSID(sid), attrsp);
 }
 
 JSBool
-ArrayBufferObject::obj_setGenericAttributes(JSContext *cx, JSObject *obj,
-                                            jsid id, unsigned *attrsp)
+ArrayBufferObject::obj_setGenericAttributes(JSContext *cx, JSObject *obj_,
+                                            jsid id_, unsigned *attrsp)
 {
+    RootedVarObject obj(cx, obj_);
+    RootedVarId id(cx, id_);
+
     if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                              JSMSG_CANT_SET_ARRAY_ATTRS);
         return false;
     }
 
     JSObject *delegate = DelegateObject(cx, obj);
     if (!delegate)
@@ -662,61 +699,71 @@ ArrayBufferObject::obj_setGenericAttribu
 JSBool
 ArrayBufferObject::obj_setPropertyAttributes(JSContext *cx, JSObject *obj,
                                              PropertyName *name, unsigned *attrsp)
 {
     return obj_setGenericAttributes(cx, obj, ATOM_TO_JSID(name), attrsp);
 }
 
 JSBool
-ArrayBufferObject::obj_setElementAttributes(JSContext *cx, JSObject *obj,
+ArrayBufferObject::obj_setElementAttributes(JSContext *cx, JSObject *obj_,
                                             uint32_t index, unsigned *attrsp)
 {
+    RootedVarObject obj(cx, obj_);
+
     JSObject *delegate = DelegateObject(cx, obj);
     if (!delegate)
         return false;
     return js_SetElementAttributes(cx, delegate, index, attrsp);
 }
 
 JSBool
 ArrayBufferObject::obj_setSpecialAttributes(JSContext *cx, JSObject *obj,
                                             SpecialId sid, unsigned *attrsp)
 {
     return obj_setGenericAttributes(cx, obj, SPECIALID_TO_JSID(sid), attrsp);
 }
 
 JSBool
-ArrayBufferObject::obj_deleteProperty(JSContext *cx, JSObject *obj,
-                                      PropertyName *name, Value *rval, JSBool strict)
+ArrayBufferObject::obj_deleteProperty(JSContext *cx, JSObject *obj_,
+                                      PropertyName *name_, Value *rval, JSBool strict)
 {
+    RootedVarObject obj(cx, obj_);
+    RootedVarPropertyName name(cx, name_);
+
     if (name == cx->runtime->atomState.byteLengthAtom) {
         rval->setBoolean(false);
         return true;
     }
 
     JSObject *delegate = DelegateObject(cx, obj);
     if (!delegate)
         return false;
     return js_DeleteProperty(cx, delegate, name, rval, strict);
 }
 
 JSBool
-ArrayBufferObject::obj_deleteElement(JSContext *cx, JSObject *obj,
+ArrayBufferObject::obj_deleteElement(JSContext *cx, JSObject *obj_,
                                      uint32_t index, Value *rval, JSBool strict)
 {
+    RootedVarObject obj(cx, obj_);
+
     JSObject *delegate = DelegateObject(cx, obj);
     if (!delegate)
         return false;
     return js_DeleteElement(cx, delegate, index, rval, strict);
 }
 
 JSBool
-ArrayBufferObject::obj_deleteSpecial(JSContext *cx, JSObject *obj,
-                                     SpecialId sid, Value *rval, JSBool strict)
+ArrayBufferObject::obj_deleteSpecial(JSContext *cx, JSObject *obj_,
+                                     SpecialId sid_, Value *rval, JSBool strict)
 {
+    RootedVarObject obj(cx, obj_);
+    RootedVar<SpecialId> sid(cx, sid_);
+
     JSObject *delegate = DelegateObject(cx, obj);
     if (!delegate)
         return false;
     return js_DeleteSpecial(cx, delegate, sid, rval, strict);
 }
 
 JSBool
 ArrayBufferObject::obj_enumerate(JSContext *cx, JSObject *obj,
@@ -1144,17 +1191,17 @@ class TypedArrayTemplate
             vp->setUndefined();
             return true;
         }
 
         return proto->getElementIfPresent(cx, receiver, index, vp, present);
     }
 
     static bool
-    setElementTail(JSContext *cx, JSObject *tarray, uint32_t index, Value *vp, JSBool strict)
+    setElementTail(JSContext *cx, HandleObject tarray, uint32_t index, Value *vp, JSBool strict)
     {
         JS_ASSERT(tarray);
         JS_ASSERT(index < getLength(tarray));
 
         if (vp->isInt32()) {
             setIndex(tarray, index, NativeType(vp->toInt32()));
             return true;
         }
@@ -1200,17 +1247,17 @@ class TypedArrayTemplate
         }
 
         return true;
     }
 
     static JSBool
     obj_setGeneric(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
     {
-        JSObject *tarray = getTypedArray(obj);
+        RootedVarObject tarray(cx, getTypedArray(obj));
         JS_ASSERT(tarray);
 
         if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
             vp->setNumber(getLength(tarray));
             return true;
         }
 
         uint32_t index;
@@ -1232,17 +1279,17 @@ class TypedArrayTemplate
     obj_setProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *vp, JSBool strict)
     {
         return obj_setGeneric(cx, obj, ATOM_TO_JSID(name), vp, strict);
     }
 
     static JSBool
     obj_setElement(JSContext *cx, JSObject *obj, uint32_t index, Value *vp, JSBool strict)
     {
-        JSObject *tarray = getTypedArray(obj);
+        RootedVarObject tarray(cx, getTypedArray(obj));
         JS_ASSERT(tarray);
 
         if (index >= getLength(tarray)) {
             // Silent ignore is better than an exception here, because
             // at some point we may want to support other properties on
             // these objects.  This is especially true when these arrays
             // are used to implement HTML Canvas 2D's PixelArray objects,
             // which used to be plain old arrays.
@@ -1377,19 +1424,19 @@ class TypedArrayTemplate
 
     static JSType
     obj_typeOf(JSContext *cx, JSObject *obj)
     {
         return JSTYPE_OBJECT;
     }
 
     static JSObject *
-    createTypedArray(JSContext *cx, JSObject *bufobj, uint32_t byteOffset, uint32_t len)
+    createTypedArray(JSContext *cx, HandleObject bufobj, uint32_t byteOffset, uint32_t len)
     {
-        JSObject *obj = NewBuiltinClassInstance(cx, protoClass());
+        RootedVarObject obj(cx, NewBuiltinClassInstance(cx, protoClass()));
         if (!obj)
             return NULL;
 #ifdef JS_THREADSAFE
         JS_ASSERT(obj->getAllocKind() == gc::FINALIZE_OBJECT8_BACKGROUND);
 #else
         JS_ASSERT(obj->getAllocKind() == gc::FINALIZE_OBJECT8);
 #endif
 
@@ -1461,39 +1508,39 @@ class TypedArrayTemplate
     static JSObject *
     create(JSContext *cx, unsigned argc, Value *argv)
     {
         /* N.B. there may not be an argv[-2]/argv[-1]. */
 
         /* () or (number) */
         uint32_t len = 0;
         if (argc == 0 || ValueIsLength(cx, argv[0], &len)) {
-            JSObject *bufobj = createBufferWithSizeAndCount(cx, len);
+            RootedVarObject bufobj(cx, createBufferWithSizeAndCount(cx, len));
             if (!bufobj)
                 return NULL;
 
             return createTypedArray(cx, bufobj, 0, len);
         }
 
         /* (not an object) */
         if (!argv[0].isObject()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                  JSMSG_TYPED_ARRAY_BAD_ARGS);
             return NULL;
         }
 
-        JSObject *dataObj = &argv[0].toObject();
+        RootedVarObject dataObj(cx, &argv[0].toObject());
 
         /* (typedArray) */
         if (dataObj->isTypedArray()) {
             JSObject *otherTypedArray = getTypedArray(dataObj);
             JS_ASSERT(otherTypedArray);
 
             uint32_t len = getLength(otherTypedArray);
-            JSObject *bufobj = createBufferWithSizeAndCount(cx, len);
+            RootedVarObject bufobj(cx, createBufferWithSizeAndCount(cx, len));
             if (!bufobj)
                 return NULL;
 
             JSObject *obj = createTypedArray(cx, bufobj, 0, len);
             if (!obj || !copyFromTypedArray(cx, obj, otherTypedArray, 0))
                 return NULL;
             return obj;
         }
@@ -1567,21 +1614,21 @@ class TypedArrayTemplate
 
     /* set(array[, offset]) */
     static JSBool
     fun_set(JSContext *cx, unsigned argc, Value *vp)
     {
         CallArgs args = CallArgsFromVp(argc, vp);
 
         bool ok;
-        JSObject *obj = NonGenericMethodGuard(cx, args, fun_set, fastClass(), &ok);
+        RootedVarObject obj(cx, NonGenericMethodGuard(cx, args, fun_set, fastClass(), &ok));
         if (!obj)
             return ok;
 
-        JSObject *tarray = getTypedArray(obj);
+        RootedVarObject tarray(cx, getTypedArray(obj));
         if (!tarray)
             return true;
 
         // these are the default values
         int32_t off = 0;
 
         if (args.length() > 1) {
             if (!ToInt32(cx, args[1], &off))
@@ -1599,17 +1646,17 @@ class TypedArrayTemplate
 
         // first arg must be either a typed array or a JS array
         if (args.length() == 0 || !args[0].isObject()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                  JSMSG_TYPED_ARRAY_BAD_ARGS);
             return false;
         }
 
-        JSObject *arg0 = args[0].toObjectOrNull();
+        RootedVarObject arg0(cx, args[0].toObjectOrNull());
         if (arg0->isTypedArray()) {
             JSObject *src = TypedArray::getTypedArray(arg0);
             if (!src ||
                 getLength(src) > getLength(tarray) - offset)
             {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                      JSMSG_TYPED_ARRAY_BAD_ARGS);
                 return false;
@@ -1634,17 +1681,17 @@ class TypedArrayTemplate
         }
 
         args.rval().setUndefined();
         return true;
     }
 
   public:
     static JSObject *
-    createTypedArrayWithBuffer(JSContext *cx, JSObject *bufobj,
+    createTypedArrayWithBuffer(JSContext *cx, HandleObject bufobj,
                                int32_t byteOffsetInt, int32_t lengthInt)
     {
         uint32_t boffset = (byteOffsetInt == -1) ? 0 : uint32_t(byteOffsetInt);
 
         ArrayBufferObject &buffer = bufobj->asArrayBuffer();
 
         if (boffset > buffer.byteLength() || boffset % sizeof(NativeType) != 0) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS);
@@ -1673,37 +1720,37 @@ class TypedArrayTemplate
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS);
             return NULL; // boffset + len is too big for the arraybuffer
         }
 
         return createTypedArray(cx, bufobj, boffset, len);
     }
 
     static JSObject *
-    createTypedArrayFromArray(JSContext *cx, JSObject *other)
+    createTypedArrayFromArray(JSContext *cx, HandleObject other)
     {
         uint32_t len;
         if (!js_GetLengthProperty(cx, other, &len))
             return NULL;
 
-        JSObject *bufobj = createBufferWithSizeAndCount(cx, len);
+        RootedVarObject bufobj(cx, createBufferWithSizeAndCount(cx, len));
         if (!bufobj)
             return NULL;
 
-        JSObject *obj = createTypedArray(cx, bufobj, 0, len);
+        RootedVarObject obj(cx, createTypedArray(cx, bufobj, 0, len));
         if (!obj || !copyFromArray(cx, obj, other, len))
             return NULL;
         return obj;
     }
 
     /*
      * Note: the offset and length arguments are ignored if an array is passed in.
      */
     static JSObject *
-    createTypedArrayWithOffsetLength(JSContext *cx, JSObject *other,
+    createTypedArrayWithOffsetLength(JSContext *cx, HandleObject other,
                                      int32_t byteOffsetInt, int32_t lengthInt)
     {
         JS_ASSERT(!other->isTypedArray());
 
         if (other->isArrayBuffer()) {
             /* Handle creation from an ArrayBuffer not ArrayBuffer.prototype. */
             return createTypedArrayWithBuffer(cx, other, byteOffsetInt, lengthInt);
         }
@@ -1732,17 +1779,17 @@ class TypedArrayTemplate
     static JSObject *
     createSubarray(JSContext *cx, JSObject *tarray, uint32_t begin, uint32_t end)
     {
         JS_ASSERT(tarray);
 
         JS_ASSERT(begin <= getLength(tarray));
         JS_ASSERT(end <= getLength(tarray));
 
-        JSObject *bufobj = getBuffer(tarray);
+        RootedVarObject bufobj(cx, getBuffer(tarray));
         JS_ASSERT(bufobj);
 
         JS_ASSERT(begin <= end);
         uint32_t length = end - begin;
 
         JS_ASSERT(begin < UINT32_MAX / sizeof(NativeType));
         JS_ASSERT(UINT32_MAX - begin * sizeof(NativeType) >= getByteOffset(tarray));
         uint32_t byteOffset = getByteOffset(tarray) + begin * sizeof(NativeType);
@@ -1784,24 +1831,25 @@ class TypedArrayTemplate
 
         return ArrayTypeIsFloatingPoint()
                ? NativeType(js_NaN)
                : NativeType(int32_t(0));
     }
 
     static bool
     copyFromArray(JSContext *cx, JSObject *thisTypedArrayObj,
-                  JSObject *ar, uint32_t len, uint32_t offset = 0)
+                  HandleObject ar, uint32_t len, uint32_t offset = 0)
     {
         thisTypedArrayObj = getTypedArray(thisTypedArrayObj);
         JS_ASSERT(thisTypedArrayObj);
 
         JS_ASSERT(offset <= getLength(thisTypedArrayObj));
         JS_ASSERT(len <= getLength(thisTypedArrayObj) - offset);
         NativeType *dest = static_cast<NativeType*>(getDataOffset(thisTypedArrayObj)) + offset;
+        SkipRoot skip(cx, &dest);
 
         if (ar->isDenseArray() && ar->getDenseArrayInitializedLength() >= len) {
             JS_ASSERT(ar->getArrayLength() == len);
 
             const Value *src = ar->getDenseArrayElements();
 
             /*
              * It is valid to skip the hole check here because nativeFromValue
@@ -2243,25 +2291,25 @@ JSFunctionSpec _typedArray::jsfuncs[] = 
     JS_FN("set", _typedArray::fun_set, 2, JSFUN_GENERIC_NATIVE),               \
     JS_FS_END                                                                  \
 }
 
 template<typename ElementType>
 static inline JSObject *
 NewArray(JSContext *cx, uint32_t nelements)
 {
-    JSObject *buffer = TypedArrayTemplate<ElementType>::createBufferWithSizeAndCount(cx, nelements);
+    RootedVarObject buffer(cx, TypedArrayTemplate<ElementType>::createBufferWithSizeAndCount(cx, nelements));
     if (!buffer)
         return NULL;
     return TypedArrayTemplate<ElementType>::createTypedArray(cx, buffer, 0, nelements);
 }
 
 template<typename ElementType>
 static inline JSObject *
-NewArrayWithBuffer(JSContext *cx, JSObject *arrayBuffer, int32_t byteoffset, int32_t intLength)
+NewArrayWithBuffer(JSContext *cx, HandleObject arrayBuffer, int32_t byteoffset, int32_t intLength)
 {
     if (!arrayBuffer->isArrayBuffer()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS);
         return NULL; // must be arrayBuffer
     }
 
     return TypedArrayTemplate<ElementType>::createTypedArrayWithBuffer(cx, arrayBuffer,
                                                                        byteoffset, intLength);
@@ -2270,23 +2318,23 @@ NewArrayWithBuffer(JSContext *cx, JSObje
 #define IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Name,NativeType)                                 \
   JS_FRIEND_API(JSObject *) JS_New ## Name ## Array(JSContext *cx, uint32_t nelements)       \
   {                                                                                          \
       MOZ_ASSERT(nelements <= INT32_MAX);                                                    \
       return NewArray<NativeType>(cx, nelements);                                            \
   }                                                                                          \
   JS_FRIEND_API(JSObject *) JS_New ## Name ## ArrayFromArray(JSContext *cx, JSObject *other) \
   {                                                                                          \
-      return TypedArrayTemplate<NativeType>::createTypedArrayFromArray(cx, other);           \
+      return TypedArrayTemplate<NativeType>::createTypedArrayFromArray(cx, RootedVarObject(cx, other)); \
   }                                                                                          \
   JS_FRIEND_API(JSObject *) JS_New ## Name ## ArrayWithBuffer(JSContext *cx,                 \
                                JSObject *arrayBuffer, uint32_t byteoffset, int32_t length)   \
   {                                                                                          \
       MOZ_ASSERT(byteoffset <= INT32_MAX);                                                   \
-      return NewArrayWithBuffer<NativeType>(cx, arrayBuffer, byteoffset, length);            \
+      return NewArrayWithBuffer<NativeType>(cx, RootedVarObject(cx, arrayBuffer), byteoffset, length); \
   }                                                                                          \
   JS_FRIEND_API(JSBool) JS_Is ## Name ## Array(JSObject *obj, JSContext *cx)                 \
   {                                                                                          \
       obj = UnwrapObject(obj);                                                               \
       Class *clasp = obj->getClass();                                                        \
       return (clasp == &TypedArray::fastClasses[TypedArrayTemplate<NativeType>::ArrayTypeID()]); \
   }
 
@@ -2376,25 +2424,25 @@ IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Floa
         _typedArray::obj_typeOf,                                               \
         NULL,                /* thisObject  */                                 \
         NULL,                /* clear       */                                 \
     }                                                                          \
 }
 
 template<class ArrayType>
 static inline JSObject *
-InitTypedArrayClass(JSContext *cx, GlobalObject *global)
+InitTypedArrayClass(JSContext *cx, Handle<GlobalObject*> global)
 {
-    JSObject *proto = global->createBlankPrototype(cx, ArrayType::protoClass());
+    RootedVarObject proto(cx, global->createBlankPrototype(cx, ArrayType::protoClass()));
     if (!proto)
         return NULL;
 
-    JSFunction *ctor =
-        global->createConstructor(cx, ArrayType::class_constructor,
-                                  cx->runtime->atomState.classAtoms[ArrayType::key], 3);
+    RootedVarFunction ctor(cx);
+    ctor = global->createConstructor(cx, ArrayType::class_constructor,
+                                     cx->runtime->atomState.classAtoms[ArrayType::key], 3);
     if (!ctor)
         return NULL;
 
     if (!LinkConstructorAndPrototype(cx, ctor, proto))
         return NULL;
 
     if (!ctor->defineProperty(cx, cx->runtime->atomState.BYTES_PER_ELEMENTAtom,
                               Int32Value(ArrayType::BYTES_PER_ELEMENT),
@@ -2447,25 +2495,25 @@ Class TypedArray::protoClasses[TYPE_MAX]
     IMPL_TYPED_ARRAY_PROTO_CLASS(Int32Array),
     IMPL_TYPED_ARRAY_PROTO_CLASS(Uint32Array),
     IMPL_TYPED_ARRAY_PROTO_CLASS(Float32Array),
     IMPL_TYPED_ARRAY_PROTO_CLASS(Float64Array),
     IMPL_TYPED_ARRAY_PROTO_CLASS(Uint8ClampedArray)
 };
 
 static JSObject *
-InitArrayBufferClass(JSContext *cx, GlobalObject *global)
+InitArrayBufferClass(JSContext *cx, Handle<GlobalObject*> global)
 {
-    JSObject *arrayBufferProto = global->createBlankPrototype(cx, &ArrayBufferObject::protoClass);
+    RootedVarObject arrayBufferProto(cx, global->createBlankPrototype(cx, &ArrayBufferObject::protoClass));
     if (!arrayBufferProto)
         return NULL;
 
-    JSFunction *ctor =
-        global->createConstructor(cx, ArrayBufferObject::class_constructor,
-                                  CLASS_ATOM(cx, ArrayBuffer), 1);
+    RootedVarFunction ctor(cx);
+    ctor = global->createConstructor(cx, ArrayBufferObject::class_constructor,
+                                     CLASS_ATOM(cx, ArrayBuffer), 1);
     if (!ctor)
         return NULL;
 
     if (!LinkConstructorAndPrototype(cx, ctor, arrayBufferProto))
         return NULL;
 
     if (!DefinePropertiesAndBrand(cx, arrayBufferProto, ArrayBufferObject::jsprops, ArrayBufferObject::jsfuncs))
         return NULL;
@@ -2476,17 +2524,17 @@ InitArrayBufferClass(JSContext *cx, Glob
     return arrayBufferProto;
 }
 
 JSObject *
 js_InitTypedArrayClasses(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isNative());
 
-    GlobalObject *global = &obj->asGlobal();
+    RootedVar<GlobalObject*> global(cx, &obj->asGlobal());
 
     /* Idempotency required: we initialize several things, possibly lazily. */
     JSObject *stop;
     if (!js_GetClassObject(cx, global, JSProto_ArrayBuffer, &stop))
         return NULL;
     if (stop)
         return stop;
 
--- a/js/src/jswatchpoint.cpp
+++ b/js/src/jswatchpoint.cpp
@@ -51,44 +51,45 @@ DefaultHasher<WatchKey>::hash(const Look
     return DefaultHasher<JSObject *>::hash(key.object.get()) ^ HashId(key.id.get());
 }
 
 class AutoEntryHolder {
     typedef WatchpointMap::Map Map;
     Map &map;
     Map::Ptr p;
     uint32_t gen;
-    WatchKey key;
+    RootedVarObject obj;
+    RootedVarId id;
 
   public:
-    AutoEntryHolder(Map &map, Map::Ptr p)
-        : map(map), p(p), gen(map.generation()), key(p->key) {
+    AutoEntryHolder(JSContext *cx, Map &map, Map::Ptr p)
+        : map(map), p(p), gen(map.generation()), obj(cx, p->key.object), id(cx, p->key.id) {
         JS_ASSERT(!p->value.held);
         p->value.held = true;
     }
 
     ~AutoEntryHolder() {
         if (gen != map.generation())
-            p = map.lookup(key);
+            p = map.lookup(WatchKey(obj, id));
         if (p)
             p->value.held = false;
     }
 };
 
 bool
 WatchpointMap::init()
 {
     return map.init();
 }
 
 bool
-WatchpointMap::watch(JSContext *cx, JSObject *obj, jsid id,
-                     JSWatchPointHandler handler, JSObject *closure)
+WatchpointMap::watch(JSContext *cx, HandleObject obj, HandleId id,
+                     JSWatchPointHandler handler, HandleObject closure)
 {
-    JS_ASSERT(id == js_CheckForStringIndex(id));
+    JS_ASSERT(id.value() == js_CheckForStringIndex(id));
     JS_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id));
 
     if (!obj->setWatched(cx))
         return false;
 
     Watchpoint w;
     w.handler = handler;
     w.closure = closure;
@@ -126,28 +127,28 @@ WatchpointMap::unwatchObject(JSObject *o
 
 void
 WatchpointMap::clear()
 {
     map.clear();
 }
 
 bool
-WatchpointMap::triggerWatchpoint(JSContext *cx, JSObject *obj, jsid id, Value *vp)
+WatchpointMap::triggerWatchpoint(JSContext *cx, HandleObject obj, HandleId id, Value *vp)
 {
-    JS_ASSERT(id == js_CheckForStringIndex(id));
+    JS_ASSERT(id.value() == js_CheckForStringIndex(id));
     Map::Ptr p = map.lookup(WatchKey(obj, id));
     if (!p || p->value.held)
         return true;
 
-    AutoEntryHolder holder(map, p);
+    AutoEntryHolder holder(cx, map, p);
 
     /* Copy the entry, since GC would invalidate p. */
     JSWatchPointHandler handler = p->value.handler;
-    JSObject *closure = p->value.closure;
+    RootedVarObject closure(cx, p->value.closure);
 
     /* Determine the property's old value. */
     Value old;
     old.setUndefined();
     if (obj->isNative()) {
         if (const Shape *shape = obj->nativeLookup(cx, id)) {
             if (shape->hasSlot())
                 old = obj->nativeGetSlot(shape->slot());
--- a/js/src/jswatchpoint.h
+++ b/js/src/jswatchpoint.h
@@ -74,24 +74,24 @@ struct DefaultHasher<WatchKey> {
     }
 };
 
 class WatchpointMap {
   public:
     typedef HashMap<WatchKey, Watchpoint, DefaultHasher<WatchKey>, SystemAllocPolicy> Map;
 
     bool init();
-    bool watch(JSContext *cx, JSObject *obj, jsid id,
-               JSWatchPointHandler handler, JSObject *closure);
+    bool watch(JSContext *cx, HandleObject obj, HandleId id,
+               JSWatchPointHandler handler, HandleObject closure);
     void unwatch(JSObject *obj, jsid id,
                  JSWatchPointHandler *handlerp, JSObject **closurep);
     void unwatchObject(JSObject *obj);
     void clear();
 
-    bool triggerWatchpoint(JSContext *cx, JSObject *obj, jsid id, Value *vp);
+    bool triggerWatchpoint(JSContext *cx, HandleObject obj, HandleId id, Value *vp);
 
     static bool markAllIteratively(JSTracer *trc);
     bool markIteratively(JSTracer *trc);
     void markAll(JSTracer *trc);
     static void sweepAll(JSRuntime *rt);
     void sweep();
 
     static void traceAll(WeakMapTracer *trc);
--- a/js/src/jsweakmap.cpp
+++ b/js/src/jsweakmap.cpp
@@ -387,24 +387,25 @@ static JSFunctionSpec weak_map_methods[]
     JS_FS_END
 };
 
 JSObject *
 js_InitWeakMapClass(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isNative());
 
-    GlobalObject *global = &obj->asGlobal();
+    RootedVar<GlobalObject*> global(cx, &obj->asGlobal());
 
-    JSObject *weakMapProto = global->createBlankPrototype(cx, &WeakMapClass);
+    RootedVarObject weakMapProto(cx, global->createBlankPrototype(cx, &WeakMapClass));
     if (!weakMapProto)
         return NULL;
 
-    JSFunction *ctor = global->createConstructor(cx, WeakMap_construct,
-                                                 CLASS_ATOM(cx, WeakMap), 0);
+    RootedVarFunction ctor(cx);
+    ctor = global->createConstructor(cx, WeakMap_construct,
+                                     CLASS_ATOM(cx, WeakMap), 0);
     if (!ctor)
         return NULL;
 
     if (!LinkConstructorAndPrototype(cx, ctor, weakMapProto))
         return NULL;
 
     if (!DefinePropertiesAndBrand(cx, weakMapProto, NULL, weak_map_methods))
         return NULL;
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -516,17 +516,17 @@ ErrorCopier::~ErrorCopier()
     if (cx->compartment == ac.destination &&
         ac.origin != ac.destination &&
         cx->isExceptionPending())
     {
         Value exc = cx->getPendingException();
         if (exc.isObject() && exc.toObject().isError() && exc.toObject().getPrivate()) {
             cx->clearPendingException();
             ac.leave();
-            JSObject *copyobj = js_CopyErrorObject(cx, &exc.toObject(), scope);
+            JSObject *copyobj = js_CopyErrorObject(cx, RootedVarObject(cx, &exc.toObject()), scope);
             if (copyobj)
                 cx->setPendingException(ObjectValue(*copyobj));
         }
     }
 }
 
 /* Cross compartment wrappers. */
 
@@ -631,25 +631,27 @@ CrossCompartmentWrapper::get(JSContext *
 {
     PIERCE(cx, wrapper, GET,
            call.destination->wrap(cx, &receiver) && call.destination->wrapId(cx, &id),
            Wrapper::get(cx, wrapper, receiver, id, vp),
            call.origin->wrap(cx, vp));
 }
 
 bool
-CrossCompartmentWrapper::set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id,
+CrossCompartmentWrapper::set(JSContext *cx, JSObject *wrapper_, JSObject *receiver_, jsid id_,
                              bool strict, Value *vp)
 {
-    AutoValueRooter tvr(cx, *vp);
+    RootedVarObject wrapper(cx, wrapper_), receiver(cx, receiver_);
+    RootedVarId id(cx, id_);
+    RootedVarValue value(cx, *vp);
     PIERCE(cx, wrapper, SET,
-           call.destination->wrap(cx, &receiver) &&
-           call.destination->wrapId(cx, &id) &&
-           call.destination->wrap(cx, tvr.addr()),
-           Wrapper::set(cx, wrapper, receiver, id, strict, tvr.addr()),
+           call.destination->wrap(cx, receiver.address()) &&
+           call.destination->wrapId(cx, id.address()) &&
+           call.destination->wrap(cx, value.address()),
+           Wrapper::set(cx, wrapper, receiver, id, strict, value.address()),
            NOTHING);
 }
 
 bool
 CrossCompartmentWrapper::keys(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
 {
     PIERCE(cx, wrapper, GET,
            NOTHING,
@@ -732,18 +734,20 @@ CrossCompartmentWrapper::iterate(JSConte
 {
     PIERCE(cx, wrapper, GET,
            NOTHING,
            Wrapper::iterate(cx, wrapper, flags, vp),
            CanReify(vp) ? Reify(cx, call.origin, vp) : call.origin->wrap(cx, vp));
 }
 
 bool
-CrossCompartmentWrapper::call(JSContext *cx, JSObject *wrapper, unsigned argc, Value *vp)
+CrossCompartmentWrapper::call(JSContext *cx, JSObject *wrapper_, unsigned argc, Value *vp)
 {
+    RootedVarObject wrapper(cx, wrapper_);
+
     AutoCompartment call(cx, wrappedObject(wrapper));
     if (!call.enter())
         return false;
 
     vp[0] = ObjectValue(*call.target);
     if (!call.destination->wrap(cx, &vp[1]))
         return false;
     Value *argv = JS_ARGV(cx, vp);
@@ -754,19 +758,21 @@ CrossCompartmentWrapper::call(JSContext 
     if (!Wrapper::call(cx, wrapper, argc, vp))
         return false;
 
     call.leave();
     return call.origin->wrap(cx, vp);
 }
 
 bool
-CrossCompartmentWrapper::construct(JSContext *cx, JSObject *wrapper, unsigned argc, Value *argv,
+CrossCompartmentWrapper::construct(JSContext *cx, JSObject *wrapper_, unsigned argc, Value *argv,
                                    Value *rval)
 {
+    RootedVarObject wrapper(cx, wrapper_);
+
     AutoCompartment call(cx, wrappedObject(wrapper));
     if (!call.enter())
         return false;
 
     for (size_t n = 0; n < argc; ++n) {
         if (!call.destination->wrap(cx, &argv[n]))
             return false;
     }
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -4767,17 +4767,17 @@ xml_lookupGeneric(JSContext *cx, JSObjec
             return js_LookupProperty(cx, obj, funid, objp, propp);
         found = HasNamedProperty(xml, qn);
     }
     if (!found) {
         *objp = NULL;
         *propp = NULL;
     } else {
         const Shape *shape =
-            js_AddNativeProperty(cx, obj, id, GetProperty, PutProperty,
+            js_AddNativeProperty(cx, RootedVarObject(cx, obj), id, GetProperty, PutProperty,
                                  SHAPE_INVALID_SLOT, JSPROP_ENUMERATE,
                                  0, 0);
         if (!shape)
             return JS_FALSE;
 
         *objp = obj;
         *propp = (JSProperty *) shape;
     }
@@ -4802,17 +4802,17 @@ xml_lookupElement(JSContext *cx, JSObjec
         return true;
     }
 
     jsid id;
     if (!IndexToId(cx, index, &id))
         return false;
 
     const Shape *shape =
-        js_AddNativeProperty(cx, obj, id, GetProperty, PutProperty,
+        js_AddNativeProperty(cx, RootedVarObject(cx, obj), id, GetProperty, PutProperty,
                              SHAPE_INVALID_SLOT, JSPROP_ENUMERATE,
                              0, 0);
     if (!shape)
         return false;
 
     *objp = obj;
     *propp = (JSProperty *) shape;
     return true;
@@ -7254,16 +7254,18 @@ XMLList(JSContext *cx, unsigned argc, js
 #ifdef DEBUG_notme
 JSCList xml_leaks = JS_INIT_STATIC_CLIST(&xml_leaks);
 uint32_t  xml_serial;
 #endif
 
 JSXML *
 js_NewXML(JSContext *cx, JSXMLClass xml_class)
 {
+    cx->runtime->gcExactScanningEnabled = false;
+
     JSXML *xml = js_NewGCXML(cx);
     if (!xml)
         return NULL;
 
     xml->object.init(NULL);
     xml->domnode = NULL;
     xml->parent.init(NULL);
     xml->name.init(NULL);
@@ -7386,18 +7388,19 @@ js_GetXMLObject(JSContext *cx, JSXML *xm
         return NULL;
     xml->object = obj;
     return obj;
 }
 
 JSObject *
 js_InitNamespaceClass(JSContext *cx, JSObject *obj)
 {
+    cx->runtime->gcExactScanningEnabled = false;
+
     JS_ASSERT(obj->isNative());
-
     GlobalObject *global = &obj->asGlobal();
 
     JSObject *namespaceProto = global->createBlankPrototype(cx, &NamespaceClass);
     if (!namespaceProto)
         return NULL;
     JSFlatString *empty = cx->runtime->emptyString;
     namespaceProto->setNamePrefix(empty);
     namespaceProto->setNameURI(empty);
@@ -7418,18 +7421,19 @@ js_InitNamespaceClass(JSContext *cx, JSO
         return NULL;
 
     return namespaceProto;
 }
 
 JSObject *
 js_InitQNameClass(JSContext *cx, JSObject *obj)
 {
+    cx->runtime->gcExactScanningEnabled = false;
+
     JS_ASSERT(obj->isNative());
-
     GlobalObject *global = &obj->asGlobal();
 
     JSObject *qnameProto = global->createBlankPrototype(cx, &QNameClass);
     if (!qnameProto)
         return NULL;
     JSAtom *empty = cx->runtime->emptyString;
     if (!InitXMLQName(cx, qnameProto, empty, empty, empty))
         return NULL;
@@ -7450,18 +7454,19 @@ js_InitQNameClass(JSContext *cx, JSObjec
         return NULL;
 
     return qnameProto;
 }
 
 JSObject *
 js_InitXMLClass(JSContext *cx, JSObject *obj)
 {
+    cx->runtime->gcExactScanningEnabled = false;
+
     JS_ASSERT(obj->isNative());
-
     GlobalObject *global = &obj->asGlobal();
 
     JSObject *xmlProto = global->createBlankPrototype(cx, &XMLClass);
     if (!xmlProto)
         return NULL;
     JSXML *xml = js_NewXML(cx, JSXML_CLASS_TEXT);
     if (!xml)
         return NULL;
--- a/js/src/methodjit/MonoIC.cpp
+++ b/js/src/methodjit/MonoIC.cpp
@@ -1113,17 +1113,17 @@ ic::SplatApplyArgs(VMFrame &f)
 
     /* Step 3. */
     if (!args[1].isObject()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_APPLY_ARGS, js_apply_str);
         THROWV(false);
     }
 
     /* Steps 4-5. */
-    JSObject *aobj = &args[1].toObject();
+    RootedVarObject aobj(cx, &args[1].toObject());
     uint32_t length;
     if (!js_GetLengthProperty(cx, aobj, &length))
         THROWV(false);
 
     /* Step 6. */
     if (length > StackSpace::ARGS_LENGTH_MAX) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                              JSMSG_TOO_MANY_FUN_APPLY_ARGS);
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -1297,18 +1297,18 @@ class GetPropCompiler : public PICStubCo
     }
 };
 
 class ScopeNameCompiler : public PICStubCompiler
 {
   private:
     typedef Vector<Jump, 8> JumpList;
 
-    JSObject *scopeChain;
-    PropertyName *name;
+    RootedVarObject scopeChain;
+    RootedVarPropertyName name;
     GetPropHelper<ScopeNameCompiler> getprop;
     ScopeNameCompiler *thisFromCtor() { return this; }
 
     void patchPreviousToHere(CodeLocationLabel cs)
     {
         ScopeNameLabels &       labels = pic.scopeNameLabels();
         Repatcher               repatcher(pic.lastCodeBlock(f.chunk()));
         CodeLocationLabel       start = pic.lastPathStart();
@@ -1355,17 +1355,17 @@ class ScopeNameCompiler : public PICStub
 
         return Lookup_Cacheable;
     }
 
   public:
     ScopeNameCompiler(VMFrame &f, JSScript *script, JSObject *scopeChain, ic::PICInfo &pic,
                       PropertyName *name, VoidStubPIC stub)
       : PICStubCompiler("name", f, script, pic, JS_FUNC_TO_DATA_PTR(void *, stub)),
-        scopeChain(scopeChain), name(name),
+        scopeChain(f.cx, scopeChain), name(f.cx, name),
         getprop(f.cx, NULL, name, *thisFromCtor(), f)
     { }
 
     static void reset(Repatcher &repatcher, ic::PICInfo &pic)
     {
         ScopeNameLabels &labels = pic.scopeNameLabels();
 
         /* Link the inline path back to the slow path. */
@@ -1638,24 +1638,24 @@ class ScopeNameCompiler : public PICStub
             normalized = &obj->asWith().object();
         NATIVE_GET(cx, normalized, holder, shape, 0, vp, return false);
         return true;
     }
 };
 
 class BindNameCompiler : public PICStubCompiler
 {
-    JSObject *scopeChain;
-    PropertyName *name;
+    RootedVarObject scopeChain;
+    RootedVarPropertyName name;
 
   public:
     BindNameCompiler(VMFrame &f, JSScript *script, JSObject *scopeChain, ic::PICInfo &pic,
                      PropertyName *name, VoidStubPIC stub)
       : PICStubCompiler("bind", f, script, pic, JS_FUNC_TO_DATA_PTR(void *, stub)),
-        scopeChain(scopeChain), name(name)
+        scopeChain(f.cx, scopeChain), name(f.cx, name)
     { }
 
     static void reset(Repatcher &repatcher, ic::PICInfo &pic)
     {
         BindNameLabels &labels = pic.bindNameLabels();
 
         /* Link the inline jump back to the slow path. */
         JSC::CodeLocationJump inlineJump = labels.getInlineJump(pic.getFastShapeGuard());
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -82,17 +82,18 @@
 using namespace js;
 using namespace js::mjit;
 using namespace js::types;
 using namespace JSC;
 
 void JS_FASTCALL
 stubs::BindName(VMFrame &f, PropertyName *name)
 {
-    JSObject *obj = FindIdentifierBase(f.cx, f.fp()->scopeChain(), name);
+    JSObject *obj = FindIdentifierBase(f.cx, f.fp()->scopeChain(),
+                                       RootedVarPropertyName(f.cx, name));
     if (!obj)
         THROW();
     f.regs.sp[0].setObject(*obj);
 }
 
 JSObject * JS_FASTCALL
 stubs::BindGlobalName(VMFrame &f)
 {
--- a/js/src/perf/jsperf.cpp
+++ b/js/src/perf/jsperf.cpp
@@ -196,17 +196,17 @@ static JSClass pm_class = {
 
 static JSBool
 pm_construct(JSContext* cx, unsigned argc, jsval* vp)
 {
     uint32_t mask;
     if (!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "u", &mask))
         return JS_FALSE;
 
-    JSObject *obj = JS_NewObjectForConstructor(cx, &pm_class, vp);
+    js::RootedVarObject obj(cx, JS_NewObjectForConstructor(cx, &pm_class, vp));
     if (!obj)
         return JS_FALSE;
 
     if (!JS_FreezeObject(cx, obj))
         return JS_FALSE;
 
     PerfMeasurement* p = cx->new_<PerfMeasurement>(PerfMeasurement::EventMask(mask));
     if (!p) {
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -734,17 +734,17 @@ Options(JSContext *cx, unsigned argc, js
         return JS_FALSE;
     *vp = STRING_TO_JSVAL(str);
     return JS_TRUE;
 }
 
 static JSBool
 Load(JSContext *cx, unsigned argc, jsval *vp)
 {
-    JSObject *thisobj = JS_THIS_OBJECT(cx, vp);
+    RootedVarObject thisobj(cx, JS_THIS_OBJECT(cx, vp));
     if (!thisobj)
         return JS_FALSE;
 
     jsval *argv = JS_ARGV(cx, vp);
     for (unsigned i = 0; i < argc; i++) {
         JSString *str = JS_ValueToString(cx, argv[i]);
         if (!str)
             return false;
@@ -1693,16 +1693,18 @@ DisassembleScript(JSContext *cx, JSScrip
 #undef SHOW_FLAG
 
         if (fun->isNullClosure())
             Sprint(sp, " NULL_CLOSURE");
 
         Sprint(sp, "\n");
     }
 
+    Root<JSScript*> scriptRoot(cx, &script);
+
     if (!js_Disassemble(cx, script, lines, sp))
         return false;
     SrcNotes(cx, script, sp);
     TryNotes(cx, script, sp);
 
     if (recursive && JSScript::isValidOffset(script->objectsOffset)) {
         JSObjectArray *objects = script->objects();
         for (unsigned i = 0; i != objects->length; ++i) {
@@ -2121,25 +2123,25 @@ DumpObject(JSContext *cx, unsigned argc,
 
 /*
  * This shell function is temporary (used by testStackIter.js) and should be
  * removed once JSD2 lands wholly subsumes the functionality here.
  */
 JSBool
 DumpStack(JSContext *cx, unsigned argc, Value *vp)
 {
-    JSObject *arr = JS_NewArrayObject(cx, 0, NULL);
+    RootedVarObject arr(cx, JS_NewArrayObject(cx, 0, NULL));
     if (!arr)
         return false;
 
-    JSString *evalStr = JS_NewStringCopyZ(cx, "eval-code");
+    RootedVarString evalStr(cx, JS_NewStringCopyZ(cx, "eval-code"));
     if (!evalStr)
         return false;
 
-    JSString *globalStr = JS_NewStringCopyZ(cx, "global-code");
+    RootedVarString globalStr(cx, JS_NewStringCopyZ(cx, "global-code"));
     if (!globalStr)
         return false;
 
     StackIter iter(cx);
     JS_ASSERT(iter.isNativeCall() && iter.callee()->native() == DumpStack);
     ++iter;
 
     uint32_t index = 0;
@@ -2520,32 +2522,37 @@ ThrowError(JSContext *cx, unsigned argc,
 typedef struct ComplexObject {
     JSBool isInner;
     JSBool frozen;
     JSObject *inner;
     JSObject *outer;
 } ComplexObject;
 
 static JSBool
-sandbox_enumerate(JSContext *cx, JSObject *obj)
+sandbox_enumerate(JSContext *cx, JSObject *obj_)
 {
+    RootedVarObject obj(cx, obj_);
+
     jsval v;
     JSBool b;
 
     if (!JS_GetProperty(cx, obj, "lazy", &v))
         return JS_FALSE;
 
     JS_ValueToBoolean(cx, v, &b);
     return !b || JS_EnumerateStandardClasses(cx, obj);
 }
 
 static JSBool
-sandbox_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
+sandbox_resolve(JSContext *cx, JSObject *obj_, jsid id_, unsigned flags,
                 JSObject **objp)
 {
+    RootedVarObject obj(cx, obj_);
+    RootedVarId id(cx, id_);
+
     jsval v;
     JSBool b, resolved;
 
     if (!JS_GetProperty(cx, obj, "lazy", &v))
         return JS_FALSE;
 
     JS_ValueToBoolean(cx, v, &b);
     if (b && (flags & JSRESOLVE_ASSIGNING) == 0) {
@@ -2567,34 +2574,34 @@ static JSClass sandbox_class = {
     JS_PropertyStub,   JS_StrictPropertyStub,
     sandbox_enumerate, (JSResolveOp)sandbox_resolve,
     JS_ConvertStub
 };
 
 static JSObject *
 NewSandbox(JSContext *cx, bool lazy)
 {
-    JSObject *obj = JS_NewCompartmentAndGlobalObject(cx, &sandbox_class, NULL);
+    RootedVarObject obj(cx, JS_NewCompartmentAndGlobalObject(cx, &sandbox_class, NULL));
     if (!obj)
         return NULL;
 
     {
         JSAutoEnterCompartment ac;
         if (!ac.enter(cx, obj))
             return NULL;
 
         if (!lazy && !JS_InitStandardClasses(cx, obj))
             return NULL;
 
-        AutoValueRooter root(cx, BooleanValue(lazy));
-        if (!JS_SetProperty(cx, obj, "lazy", root.jsval_addr()))
+        RootedVarValue value(cx, BooleanValue(lazy));
+        if (!JS_SetProperty(cx, obj, "lazy", value.address()))
             return NULL;
     }
 
-    if (!cx->compartment->wrap(cx, &obj))
+    if (!cx->compartment->wrap(cx, obj.address()))
         return NULL;
     return obj;
 }
 
 static JSBool
 EvalInContext(JSContext *cx, unsigned argc, jsval *vp)
 {
     JSString *str;
@@ -2602,16 +2609,18 @@ EvalInContext(JSContext *cx, unsigned ar
     if (!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "S / o", &str, &sobj))
         return false;
 
     size_t srclen;
     const jschar *src = JS_GetStringCharsAndLength(cx, str, &srclen);
     if (!src)
         return false;
 
+    SkipRoot skip(cx, &src);
+
     bool lazy = false;
     if (srclen == 4) {
         if (src[0] == 'l' && src[1] == 'a' && src[2] == 'z' && src[3] == 'y') {
             lazy = true;
             srclen = 0;
         }
     }
 
@@ -3405,34 +3414,34 @@ Serialize(JSContext *cx, unsigned argc, 
     JS_free(cx, datap);
     JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(array));
     return true;
 }
 
 JSBool
 Deserialize(JSContext *cx, unsigned argc, jsval *vp)
 {
-    jsval v = argc > 0 ? JS_ARGV(cx, vp)[0] : JSVAL_VOID;
+    RootedVar<jsval> v(cx, argc > 0 ? JS_ARGV(cx, vp)[0] : JSVAL_VOID);
     JSObject *obj;
     if (JSVAL_IS_PRIMITIVE(v) || !(obj = JSVAL_TO_OBJECT(v))->isTypedArray()) {
         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "deserialize");
         return false;
     }
     JSObject *array = TypedArray::getTypedArray(obj);
     if ((TypedArray::getByteLength(array) & 7) != 0) {
         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "deserialize");
         return false;
     }
     if ((uintptr_t(TypedArray::getDataOffset(array)) & 7) != 0) {
         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_BAD_ALIGNMENT);
         return false;
     }
 
     if (!JS_ReadStructuredClone(cx, (uint64_t *) TypedArray::getDataOffset(array), TypedArray::getByteLength(array),
-                                JS_STRUCTURED_CLONE_VERSION, &v, NULL, NULL)) {
+                                JS_STRUCTURED_CLONE_VERSION, v.address(), NULL, NULL)) {
         return false;
     }
     JS_SET_RVAL(cx, vp, v);
     return true;
 }
 
 enum CompartmentKind { SAME_COMPARTMENT, NEW_COMPARTMENT };
 
--- a/js/src/shell/jsheaptools.cpp
+++ b/js/src/shell/jsheaptools.cpp
@@ -347,30 +347,30 @@ HeapReverser::getEdgeDescription()
 
 
 /*** class ReferenceFinder ***********************************************************************/
 
 /* A class for finding an object's referrers, given a reversed heap map. */
 class ReferenceFinder {
   public:
     ReferenceFinder(JSContext *cx, const HeapReverser &reverser) 
-      : context(cx), reverser(reverser) { }
+      : context(cx), reverser(reverser), result(cx) { }
 
     /* Produce an object describing all references to |target|. */
-    JSObject *findReferences(JSObject *target);
+    JSObject *findReferences(HandleObject target);
 
   private:
     /* The context in which to do allocation and error-handling. */
     JSContext *context;
 
     /* A reversed map of the current heap. */
     const HeapReverser &reverser;
 
     /* The results object we're currently building. */
-    JSObject *result;
+    RootedVarObject result;
 
     /* A list of edges we've traversed to get to a certain point. */
     class Path {
       public:
         Path(const HeapReverser::Edge &edge, Path *next) : edge(edge), next(next) { }
         
         /*
          * Compute the full path represented by this Path. The result is
@@ -508,42 +508,44 @@ ReferenceFinder::addReferrer(jsval refer
     if (!context->compartment->wrap(context, &referrer))
         return NULL;
 
     char *pathName = path->computeName(context);
     if (!pathName)
         return false;
     AutoReleasePtr releasePathName(context, pathName);
 
+    Root<jsval> referrerRoot(context, &referrer);
+
     /* Find the property of the results object named |pathName|. */
     jsval v;
     if (!JS_GetProperty(context, result, pathName, &v))
         return false;
     if (JSVAL_IS_VOID(v)) {
         /* Create an array to accumulate referents under this path. */
         JSObject *array = JS_NewArrayObject(context, 1, &referrer);
         if (!array)
             return false;
         v = OBJECT_TO_JSVAL(array);
         return !!JS_SetProperty(context, result, pathName, &v);
     }
 
     /* The property's value had better be an array. */
     JS_ASSERT(JSVAL_IS_OBJECT(v) && !JSVAL_IS_NULL(v));
-    JSObject *array = JSVAL_TO_OBJECT(v);
+    RootedVarObject array(context, JSVAL_TO_OBJECT(v));
     JS_ASSERT(JS_IsArrayObject(context, array));
 
     /* Append our referrer to this array. */
     uint32_t length;
     return JS_GetArrayLength(context, array, &length) &&
            JS_SetElement(context, array, length, &referrer);
 }
 
 JSObject *
-ReferenceFinder::findReferences(JSObject *target)
+ReferenceFinder::findReferences(HandleObject target)
 {
     result = JS_NewObject(context, NULL, NULL, NULL);
     if (!result)
         return NULL;
     if (!visit(target, NULL))
         return NULL;
 
     return result;
@@ -568,17 +570,17 @@ FindReferences(JSContext *cx, unsigned a
 
     /* Walk the JSRuntime, producing a reversed map of the heap. */
     HeapReverser reverser(cx);
     if (!reverser.init() || !reverser.reverseHeap())
         return false;
 
     /* Given the reversed map, find the referents of target. */
     ReferenceFinder finder(cx, reverser);
-    JSObject *references = finder.findReferences(JSVAL_TO_OBJECT(target));
+    JSObject *references = finder.findReferences(RootedVarObject(cx, JSVAL_TO_OBJECT(target)));
     if (!references)
         return false;
     
     JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(references));
     return true;
 }
 
 #endif /* DEBUG */
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -260,18 +260,18 @@ ArgSetter(JSContext *cx, JSObject *obj, 
     /*
      * For simplicity we use delete/define to replace the property with one
      * backed by the default Object getter and setter. Note that we rely on
      * args_delProperty to clear the corresponding reserved slot so the GC can
      * collect its value. Note also that we must define the property instead
      * of setting it in case the user has changed the prototype to an object
      * that has a setter for this id.
      */
-    AutoValueRooter tvr(cx);
-    return js_DeleteGeneric(cx, &argsobj, id, tvr.addr(), false) &&
+    RootedVarValue value(cx);
+    return js_DeleteGeneric(cx, &argsobj, id, value.address(), false) &&
            js_DefineProperty(cx, &argsobj, id, vp, NULL, NULL, JSPROP_ENUMERATE);
 }
 
 static JSBool
 args_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
              JSObject **objp)
 {
     *objp = NULL;
@@ -348,33 +348,33 @@ NormalArgumentsObject::optimizedGetElem(
         return false;
 
     return proto->getGeneric(cx, id, vp);
 }
 
 static JSBool
 args_enumerate(JSContext *cx, JSObject *obj)
 {
-    NormalArgumentsObject &argsobj = obj->asNormalArguments();
+    RootedVar<NormalArgumentsObject*> argsobj(cx, &obj->asNormalArguments());
 
     /*
      * Trigger reflection in args_resolve using a series of js_LookupProperty
      * calls.
      */
-    int argc = int(argsobj.initialLength());
+    int argc = int(argsobj->initialLength());
     for (int i = -2; i != argc; i++) {
         jsid id = (i == -2)
                   ? ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)
                   : (i == -1)
                   ? ATOM_TO_JSID(cx->runtime->atomState.calleeAtom)
                   : INT_TO_JSID(i);
 
         JSObject *pobj;
         JSProperty *prop;
-        if (!js_LookupProperty(cx, &argsobj, id, &pobj, &prop))
+        if (!js_LookupProperty(cx, argsobj, id, &pobj, &prop))
             return false;
     }
     return true;
 }
 
 static JSBool
 StrictArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
@@ -419,57 +419,57 @@ StrictArgSetter(JSContext *cx, JSObject 
     }
 
     /*
      * For simplicity we use delete/set to replace the property with one
      * backed by the default Object getter and setter. Note that we rely on
      * args_delProperty to clear the corresponding reserved slot so the GC can
      * collect its value.
      */
-    AutoValueRooter tvr(cx);
-    return js_DeleteGeneric(cx, argsobj, id, tvr.addr(), strict) &&
+    RootedVarValue value(cx);
+    return js_DeleteGeneric(cx, argsobj, id, value.address(), strict) &&
            js_SetPropertyHelper(cx, argsobj, id, 0, vp, strict);
 }
 
 static JSBool
 strictargs_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags, JSObject **objp)
 {
     *objp = NULL;
 
-    StrictArgumentsObject &argsobj = obj->asStrictArguments();
+    RootedVar<StrictArgumentsObject*> argsobj(cx, &obj->asStrictArguments());
 
     unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
     PropertyOp getter = StrictArgGetter;
     StrictPropertyOp setter = StrictArgSetter;
 
     if (JSID_IS_INT(id)) {
         uint32_t arg = uint32_t(JSID_TO_INT(id));
-        if (arg >= argsobj.initialLength() || argsobj.isElementDeleted(arg))
+        if (arg >= argsobj->initialLength() || argsobj->isElementDeleted(arg))
             return true;
 
         attrs |= JSPROP_ENUMERATE;
     } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
-        if (argsobj.hasOverriddenLength())
+        if (argsobj->hasOverriddenLength())
             return true;
     } else {
         if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom) &&
             !JSID_IS_ATOM(id, cx->runtime->atomState.callerAtom)) {
             return true;
         }
 
         attrs = JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED;
-        getter = CastAsPropertyOp(argsobj.global().getThrowTypeError());
-        setter = CastAsStrictPropertyOp(argsobj.global().getThrowTypeError());
+        getter = CastAsPropertyOp(argsobj->global().getThrowTypeError());
+        setter = CastAsStrictPropertyOp(argsobj->global().getThrowTypeError());
     }
 
     Value undef = UndefinedValue();
-    if (!js_DefineProperty(cx, &argsobj, id, &undef, getter, setter, attrs))
+    if (!js_DefineProperty(cx, argsobj, id, &undef, getter, setter, attrs))
         return false;
 
-    *objp = &argsobj;
+    *objp = argsobj;
     return true;
 }
 
 static JSBool
 strictargs_enumerate(JSContext *cx, JSObject *obj)
 {
     StrictArgumentsObject *argsobj = &obj->asStrictArguments();
 
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -520,17 +520,17 @@ Debugger::hasAnyLiveHooks() const
     return false;
 }
 
 JSTrapStatus
 Debugger::slowPathOnEnterFrame(JSContext *cx, Value *vp)
 {
     /* Build the list of recipients. */
     AutoValueVector triggered(cx);
-    GlobalObject *global = &cx->fp()->global();
+    RootedVar<GlobalObject*> global(cx, &cx->fp()->global());
     if (GlobalObject::DebuggerVector *debuggers = global->getDebuggers()) {
         for (Debugger **p = debuggers->begin(); p != debuggers->end(); p++) {
             Debugger *dbg = *p;
             JS_ASSERT(dbg->observesFrame(cx->fp()));
             if (dbg->observesEnterFrame() && !triggered.append(ObjectValue(*dbg->toJSObject())))
                 return JSTRAP_ERROR;
         }
     }
@@ -552,40 +552,40 @@ Debugger::slowPathOnEnterFrame(JSContext
  * Handle leaving a frame with debuggers watching. |frameOk| indicates whether
  * the frame is exiting normally or abruptly. Set |cx|'s exception and/or
  * |cx->fp()|'s return value, and return a new success value.
  */
 bool
 Debugger::slowPathOnLeaveFrame(JSContext *cx, bool frameOk)
 {
     StackFrame *fp = cx->fp();
-    GlobalObject *global = &fp->global();
+    RootedVar<GlobalObject*> global(cx, &fp->global());
 
     /* Save the frame's completion value. */
     JSTrapStatus status;
-    Value value;
-    Debugger::resultToCompletion(cx, frameOk, fp->returnValue(), &status, &value);
+    RootedVarValue value(cx);
+    Debugger::resultToCompletion(cx, frameOk, fp->returnValue(), &status, value.address());
 
     /* Build a list of the recipients. */
     AutoObjectVector frames(cx);
     for (FrameRange r(cx, fp, global); !r.empty(); r.popFront()) {
         if (!frames.append(r.frontFrame())) {
             cx->clearPendingException();
             return false;
         }
     }
 
     /* For each Debugger.Frame, fire its onPop handler, if any. */
     for (JSObject **p = frames.begin(); p != frames.end(); p++) {
-        JSObject *frameobj = *p;
+        RootedVarObject frameobj(cx, *p);
         Debugger *dbg = Debugger::fromChildJSObject(frameobj);
 
         if (dbg->enabled &&
             !frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER).isUndefined()) {
-            const Value &handler = frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER);
+            RootedVarValue handler(cx, frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER));
 
             AutoCompartment ac(cx, dbg->object);
 
             if (!ac.enter()) {
                 status = JSTRAP_ERROR;
                 break;
             }
                 
@@ -664,17 +664,17 @@ Debugger::slowPathOnLeaveFrame(JSContext
         return false;
 
       default:
         JS_NOT_REACHED("bad final trap status");
     }
 }
 
 bool
-Debugger::wrapEnvironment(JSContext *cx, Env *env, Value *rval)
+Debugger::wrapEnvironment(JSContext *cx, Handle<Env*> env, Value *rval)
 {
     if (!env) {
         rval->setNull();
         return true;
     }
 
     JSObject *envobj;
     ObjectWeakMap::AddPtr p = environments.lookupForAdd(env);
@@ -683,32 +683,32 @@ Debugger::wrapEnvironment(JSContext *cx,
     } else {
         /* Create a new Debugger.Environment for env. */
         JSObject *proto = &object->getReservedSlot(JSSLOT_DEBUG_ENV_PROTO).toObject();
         envobj = NewObjectWithGivenProto(cx, &DebuggerEnv_class, proto, NULL);
         if (!envobj)
             return false;
         envobj->setPrivate(env);
         envobj->setReservedSlot(JSSLOT_DEBUGENV_OWNER, ObjectValue(*object));
-        if (!environments.relookupOrAdd(p, env, envobj)) {
+        if (!environments.relookupOrAdd(p, env.value(), envobj)) {
             js_ReportOutOfMemory(cx);
             return false;
         }
     }
     rval->setObject(*envobj);
     return true;
 }
 
 bool
 Debugger::wrapDebuggeeValue(JSContext *cx, Value *vp)
 {
     assertSameCompartment(cx, object.get());
 
     if (vp->isObject()) {
-        JSObject *obj = &vp->toObject();
+        RootedVarObject obj(cx, &vp->toObject());
 
         ObjectWeakMap::AddPtr p = objects.lookupForAdd(obj);
         if (p) {
             vp->setObject(*p->value);
         } else {
             /* Create a new Debugger.Object for obj. */
             JSObject *proto = &object->getReservedSlot(JSSLOT_DEBUG_OBJECT_PROTO).toObject();
             JSObject *dobj =
@@ -803,17 +803,18 @@ bool
 Debugger::newCompletionValue(JSContext *cx, JSTrapStatus status, Value value, Value *result)
 {
     /* 
      * We must be in the debugger's compartment, since that's where we want
      * to construct the completion value.
      */
     assertSameCompartment(cx, object.get());
 
-    jsid key;
+    RootedVarId key(cx);
+    RootValue valueRoot(cx, &value);
 
     switch (status) {
       case JSTRAP_RETURN:
         key = ATOM_TO_JSID(cx->runtime->atomState.returnAtom);
         break;
 
       case JSTRAP_THROW:
         key = ATOM_TO_JSID(cx->runtime->atomState.throwAtom);
@@ -914,17 +915,17 @@ CallMethodIfPresent(JSContext *cx, Handl
            js_GetMethod(cx, obj, ATOM_TO_JSID(atom), 0, &fval) &&
            (!js_IsCallable(fval) ||
             Invoke(cx, ObjectValue(*obj), fval, argc, argv, rval));
 }
 
 JSTrapStatus
 Debugger::fireDebuggerStatement(JSContext *cx, Value *vp)
 {
-    JSObject *hook = getHook(OnDebuggerStatement);
+    RootedVarObject hook(cx, getHook(OnDebuggerStatement));
     JS_ASSERT(hook);
     JS_ASSERT(hook->isCallable());
 
     /* Grab cx->fp() before pushing a dummy frame. */
     StackFrame *fp = cx->fp();
     AutoCompartment ac(cx, object);
     if (!ac.enter())
         return JSTRAP_ERROR;
@@ -936,45 +937,47 @@ Debugger::fireDebuggerStatement(JSContex
     Value rv;
     bool ok = Invoke(cx, ObjectValue(*object), ObjectValue(*hook), 1, argv, &rv);
     return parseResumptionValue(ac, ok, rv, vp);
 }
 
 JSTrapStatus
 Debugger::fireExceptionUnwind(JSContext *cx, Value *vp)
 {
-    JSObject *hook = getHook(OnExceptionUnwind);
+    RootedVarObject hook(cx, getHook(OnExceptionUnwind));
     JS_ASSERT(hook);
     JS_ASSERT(hook->isCallable());
 
     StackFrame *fp = cx->fp();
-    Value exc = cx->getPendingException();
+    RootedVarValue exc(cx, cx->getPendingException());
     cx->clearPendingException();
 
     AutoCompartment ac(cx, object);
     if (!ac.enter())
         return JSTRAP_ERROR;
 
     Value argv[2];
+    AutoValueArray avr(cx, argv, 2);
+
     argv[1] = exc;
     if (!getScriptFrame(cx, fp, &argv[0]) || !wrapDebuggeeValue(cx, &argv[1]))
         return handleUncaughtException(ac, vp, false);
 
     Value rv;
     bool ok = Invoke(cx, ObjectValue(*object), ObjectValue(*hook), 2, argv, &rv);
     JSTrapStatus st = parseResumptionValue(ac, ok, rv, vp);
     if (st == JSTRAP_CONTINUE)
         cx->setPendingException(exc);
     return st;
 }
 
 JSTrapStatus
 Debugger::fireEnterFrame(JSContext *cx, Value *vp)
 {
-    JSObject *hook = getHook(OnEnterFrame);
+    RootedVarObject hook(cx, getHook(OnEnterFrame));
     JS_ASSERT(hook);
     JS_ASSERT(hook->isCallable());
 
     StackFrame *fp = cx->fp();
     AutoCompartment ac(cx, object);
     if (!ac.enter())
         return JSTRAP_ERROR;
 
@@ -983,19 +986,19 @@ Debugger::fireEnterFrame(JSContext *cx, 
         return handleUncaughtException(ac, vp, false);
 
     Value rv;
     bool ok = Invoke(cx, ObjectValue(*object), ObjectValue(*hook), 1, argv, &rv);
     return parseResumptionValue(ac, ok, rv, vp);
 }
 
 void
-Debugger::fireNewScript(JSContext *cx, JSScript *script)
+Debugger::fireNewScript(JSContext *cx, Handle<JSScript*> script)
 {
-    JSObject *hook = getHook(OnNewScript);
+    RootedVarObject hook(cx, getHook(OnNewScript));
     JS_ASSERT(hook);
     JS_ASSERT(hook->isCallable());
 
     AutoCompartment ac(cx, object);
     if (!ac.enter())
         return;
 
     JSObject *dsobj = wrapScript(cx, script);
@@ -1020,17 +1023,17 @@ Debugger::dispatchHook(JSContext *cx, Va
      * Determine which debuggers will receive this event, and in what order.
      * Make a copy of the list, since the original is mutable and we will be
      * calling into arbitrary JS.
      *
      * Note: In the general case, 'triggered' contains references to objects in
      * different compartments--every compartment *except* this one.
      */
     AutoValueVector triggered(cx);
-    GlobalObject *global = &cx->fp()->global();
+    RootedVar<GlobalObject*> global(cx, &cx->fp()->global());
     if (GlobalObject::DebuggerVector *debuggers = global->getDebuggers()) {
         for (Debugger **p = debuggers->begin(); p != debuggers->end(); p++) {
             Debugger *dbg = *p;
             if (dbg->enabled && dbg->getHook(which)) {
                 if (!triggered.append(ObjectValue(*dbg->toJSObject())))
                     return JSTRAP_ERROR;
             }
         }
@@ -1066,18 +1069,21 @@ AddNewScriptRecipients(GlobalObject::Deb
         {
             return false;
         }
     }
     return true;
 }
 
 void
-Debugger::slowPathOnNewScript(JSContext *cx, JSScript *script, GlobalObject *compileAndGoGlobal)
+Debugger::slowPathOnNewScript(JSContext *cx, JSScript *script_, GlobalObject *compileAndGoGlobal_)
 {
+    RootedVar<JSScript*> script(cx, script_);
+    RootedVar<GlobalObject*> compileAndGoGlobal(cx, compileAndGoGlobal_);
+
     JS_ASSERT(script->compileAndGo == !!compileAndGoGlobal);
 
     /*
      * Build the list of recipients. For compile-and-go scripts, this is the
      * same as the generic Debugger::dispatchHook code, but non-compile-and-go
      * scripts are not tied to particular globals. We deliver them to every
      * debugger observing any global in the script's compartment.
      */
@@ -1107,18 +1113,18 @@ Debugger::slowPathOnNewScript(JSContext 
         }
     }
 }
 
 JSTrapStatus
 Debugger::onTrap(JSContext *cx, Value *vp)
 {
     StackFrame *fp = cx->fp();
-    JSScript *script = fp->script();
-    GlobalObject *scriptGlobal = &fp->global();
+    RootedVar<JSScript*> script(cx, fp->script());
+    RootedVar<GlobalObject*> scriptGlobal(cx, &fp->global());
     jsbytecode *pc = cx->regs().pc;
     BreakpointSite *site = script->getBreakpointSite(pc);
     JSOp op = JSOp(*pc);
 
     /* Build list of breakpoint handlers. */
     Vector<Breakpoint *> triggered(cx);
     for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = bp->nextInSite()) {
         if (!triggered.append(bp))
@@ -1169,17 +1175,17 @@ Debugger::onSingleStep(JSContext *cx, Va
     StackFrame *fp = cx->fp();
 
     /*
      * We may be stepping over a JSOP_EXCEPTION, that pushes the context's
      * pending exception for a 'catch' clause to handle. Don't let the
      * onStep handlers mess with that (other than by returning a resumption
      * value).
      */
-    Value exception = UndefinedValue();
+    RootedVarValue exception(cx, UndefinedValue());
     bool exceptionPending = cx->isExceptionPending();
     if (exceptionPending) {
         exception = cx->getPendingException();
         cx->clearPendingException();
     }
 
     /* We should only receive single-step traps for scripted frames. */
     JS_ASSERT(fp->isScriptFrame());
@@ -1700,20 +1706,20 @@ Debugger::unwrapDebuggeeArgument(JSConte
     return obj;
 }
 
 JSBool
 Debugger::addDebuggee(JSContext *cx, unsigned argc, Value *vp)
 {
     REQUIRE_ARGC("Debugger.addDebuggee", 1);
     THIS_DEBUGGER(cx, argc, vp, "addDebuggee", args, dbg);
-    JSObject *referent = dbg->unwrapDebuggeeArgument(cx, args[0]);
+    RootedVarObject referent(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
     if (!referent)
         return false;
-    GlobalObject *global = &referent->global();
+    RootedVar<GlobalObject*> global(cx, &referent->global());
     if (!dbg->addDebuggeeGlobal(cx, global))
         return false;
 
     Value v = ObjectValue(*referent);
     if (!dbg->wrapDebuggeeValue(cx, &v))
         return false;
     args.rval() = v;
     return true;
@@ -1745,17 +1751,17 @@ Debugger::hasDebuggee(JSContext *cx, uns
     args.rval().setBoolean(!!dbg->debuggees.lookup(&referent->global()));
     return true;
 }
 
 JSBool
 Debugger::getDebuggees(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGGER(cx, argc, vp, "getDebuggees", args, dbg);
-    JSObject *arrobj = NewDenseAllocatedArray(cx, dbg->debuggees.count(), NULL);
+    RootedVarObject arrobj(cx, NewDenseAllocatedArray(cx, dbg->debuggees.count(), NULL));
     if (!arrobj)
         return false;
     arrobj->ensureDenseArrayInitializedLength(cx, 0, dbg->debuggees.count());
     unsigned i = 0;
     for (GlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront()) {
         Value v = ObjectValue(*e.front());
         if (!dbg->wrapDebuggeeValue(cx, &v))
             return false;
@@ -1807,52 +1813,52 @@ Debugger::construct(JSContext *cx, unsig
             return false;
         }
     }
 
     /* Get Debugger.prototype. */
     Value v;
     if (!args.callee().getProperty(cx, cx->runtime->atomState.classPrototypeAtom, &v))
         return false;
-    JSObject *proto = &v.toObject();
+    RootedVarObject proto(cx, &v.toObject());
     JS_ASSERT(proto->getClass() == &Debugger::jsclass);
 
     /*
      * Make the new Debugger object. Each one has a reference to
      * Debugger.{Frame,Object,Script}.prototype in reserved slots. The rest of
      * the reserved slots are for hooks; they default to undefined.
      */
-    JSObject *obj = NewObjectWithGivenProto(cx, &Debugger::jsclass, proto, NULL);
+    RootedVarObject obj(cx, NewObjectWithGivenProto(cx, &Debugger::jsclass, proto, NULL));
     if (!obj)
         return false;
     for (unsigned slot = JSSLOT_DEBUG_PROTO_START; slot < JSSLOT_DEBUG_PROTO_STOP; slot++)
         obj->setReservedSlot(slot, proto->getReservedSlot(slot));
 
-    Debugger *dbg = cx->new_<Debugger>(cx, obj);
+    Debugger *dbg = cx->new_<Debugger>(cx, obj.reference());
     if (!dbg)
         return false;
     obj->setPrivate(dbg);
     if (!dbg->init(cx)) {
         cx->delete_(dbg);
         return false;
     }
 
     /* Add the initial debuggees, if any. */
     for (unsigned i = 0; i < argc; i++) {
-        GlobalObject *debuggee = &GetProxyPrivate(&args[i].toObject()).toObject().global();
+        RootedVar<GlobalObject*> debuggee(cx, &GetProxyPrivate(&args[i].toObject()).toObject().global());
         if (!dbg->addDebuggeeGlobal(cx, debuggee))
             return false;
     }
 
     args.rval().setObject(*obj);
     return true;
 }
 
 bool
-Debugger::addDebuggeeGlobal(JSContext *cx, GlobalObject *global)
+Debugger::addDebuggeeGlobal(JSContext *cx, Handle<GlobalObject*> global)
 {
     if (debuggees.has(global))
         return true;
 
     JSCompartment *debuggeeCompartment = global->compartment();
 
     /*
      * Check for cycles. If global's compartment is reachable from this
@@ -1892,17 +1898,17 @@ Debugger::addDebuggeeGlobal(JSContext *c
 
     /*
      * Each debugger-debuggee relation must be stored in up to three places.
      * JSCompartment::addDebuggee enables debug mode if needed.
      */
     AutoCompartment ac(cx, global);
     if (!ac.enter())
         return false;
-    GlobalObject::DebuggerVector *v = global->getOrCreateDebuggers(cx);
+    GlobalObject::DebuggerVector *v = GlobalObject::getOrCreateDebuggers(cx, global);
     if (!v || !v->append(this)) {
         js_ReportOutOfMemory(cx);
     } else {
         if (!debuggees.put(global)) {
             js_ReportOutOfMemory(cx);
         } else {
             if (global->getDebuggers()->length() > 1)
                 return true;
@@ -2317,24 +2323,24 @@ Debugger::findScripts(JSContext *cx, uns
      * the JS array as we go, because we mustn't allocate JS objects or GC
      * while we use the CellIter.
      */
     AutoScriptVector scripts(cx);
 
     if (!query.findScripts(&scripts))
         return false;
 
-    JSObject *result = NewDenseAllocatedArray(cx, scripts.length(), NULL);
+    RootedVarObject result(cx, NewDenseAllocatedArray(cx, scripts.length(), NULL));
     if (!result)
         return false;
 
     result->ensureDenseArrayInitializedLength(cx, 0, scripts.length());
 
     for (size_t i = 0; i < scripts.length(); i++) {
-        JSObject *scriptObject = dbg->wrapScript(cx, scripts[i]);
+        JSObject *scriptObject = dbg->wrapScript(cx, RootedVar<JSScript*>(cx, scripts[i]));
         if (!scriptObject)
             return false;
         result->setDenseArrayElement(i, ObjectValue(*scriptObject));
     }
 
     args.rval().setObject(*result);
     return true;
 }
@@ -2399,42 +2405,42 @@ Class DebuggerScript_class = {
     NULL,                 /* checkAccess */
     NULL,                 /* call        */
     NULL,                 /* construct   */
     NULL,                 /* hasInstance */
     DebuggerScript_trace
 };
 
 JSObject *
-Debugger::newDebuggerScript(JSContext *cx, JSScript *script)
+Debugger::newDebuggerScript(JSContext *cx, Handle<JSScript*> script)
 {
     assertSameCompartment(cx, object.get());
 
     JSObject *proto = &object->getReservedSlot(JSSLOT_DEBUG_SCRIPT_PROTO).toObject();
     JS_ASSERT(proto);
     JSObject *scriptobj = NewObjectWithGivenProto(cx, &DebuggerScript_class, proto, NULL);
     if (!scriptobj)
         return NULL;
     scriptobj->setReservedSlot(JSSLOT_DEBUGSCRIPT_OWNER, ObjectValue(*object));
     scriptobj->setPrivate(script);
 
     return scriptobj;
 }
 
 JSObject *
-Debugger::wrapScript(JSContext *cx, JSScript *script)
+Debugger::wrapScript(JSContext *cx, Handle<JSScript*> script)
 {
     assertSameCompartment(cx, object.get());
     JS_ASSERT(cx->compartment != script->compartment());
     ScriptWeakMap::AddPtr p = scripts.lookupForAdd(script);
     if (!p) {
         JSObject *scriptobj = newDebuggerScript(cx, script);
 
         /* The allocation may have caused a GC, which can remove table entries. */
-        if (!scriptobj || !scripts.relookupOrAdd(p, script, scriptobj))
+        if (!scriptobj || !scripts.relookupOrAdd(p, script.value(), scriptobj))
             return NULL;
     }
 
     JS_ASSERT(GetScriptReferent(p->value) == script);
     return p->value;
 }
 
 static JSObject *
@@ -2468,20 +2474,20 @@ DebuggerScript_check(JSContext *cx, cons
 static JSObject *
 DebuggerScript_checkThis(JSContext *cx, const CallArgs &args, const char *fnname)
 {
     return DebuggerScript_check(cx, args.thisv(), "Debugger.Script", fnname);
 }
 
 #define THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, fnname, args, obj, script)            \
     CallArgs args = CallArgsFromVp(argc, vp);                                       \
-    JSObject *obj = DebuggerScript_checkThis(cx, args, fnname);                     \
+    RootedVarObject obj(cx, DebuggerScript_checkThis(cx, args, fnname));            \
     if (!obj)                                                                       \
         return false;                                                               \
-    JSScript *script = GetScriptReferent(obj)
+    RootedVar<JSScript*> script(cx, GetScriptReferent(obj))
 
 static JSBool
 DebuggerScript_getUrl(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get url)", args, obj, script);
 
     JSString *str = js_NewStringCopyZ(cx, script->filename);
     if (!str)
@@ -2531,17 +2537,17 @@ DebuggerScript_getChildScripts(JSContext
          * and the calling function is stored as script->objects()->vector[0].
          * It is not really a child script of this script, so skip it.
          */
         JSObjectArray *objects = script->objects();
         for (uint32_t i = script->savedCallerFun ? 1 : 0; i < objects->length; i++) {
             JSObject *obj = objects->vector[i];
             if (obj->isFunction()) {
                 JSFunction *fun = static_cast<JSFunction *>(obj);
-                JSObject *s = dbg->wrapScript(cx, fun->script());
+                JSObject *s = dbg->wrapScript(cx, RootedVar<JSScript*>(cx, fun->script()));
                 if (!s || !js_NewbornArrayPush(cx, result, ObjectValue(*s)))
                     return false;
             }
         }
     }
     args.rval().setObject(*result);
     return true;
 }
@@ -2828,17 +2834,17 @@ DebuggerScript_getLineOffsets(JSContext 
 
 static JSBool
 DebuggerScript_setBreakpoint(JSContext *cx, unsigned argc, Value *vp)
 {
     REQUIRE_ARGC("Debugger.Script.setBreakpoint", 2);
     THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "setBreakpoint", args, obj, script);
     Debugger *dbg = Debugger::fromChildJSObject(obj);
 
-    GlobalObject *scriptGlobal = script->getGlobalObjectOrNull();
+    RootedVar<GlobalObject*> scriptGlobal(cx, script->getGlobalObjectOrNull());
     if (!dbg->observesGlobal(ScriptGlobal(cx, script, scriptGlobal))) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_NOT_DEBUGGING);
         return false;
     }
 
     size_t offset;
     if (!ScriptOffset(cx, script, args[0], &offset))
         return false;
@@ -3003,17 +3009,17 @@ StackContains(JSContext *cx, StackFrame 
             return true;
     }
     return false;
 }
 #endif
 
 #define THIS_FRAME(cx, argc, vp, fnname, args, thisobj, fp)                  \
     CallArgs args = CallArgsFromVp(argc, vp);                                \
-    JSObject *thisobj = CheckThisFrame(cx, args, fnname, true);              \
+    RootedVarObject thisobj(cx, CheckThisFrame(cx, args, fnname, true));     \
     if (!thisobj)                                                            \
         return false;                                                        \
     StackFrame *fp = (StackFrame *) thisobj->getPrivate();                   \
     JS_ASSERT(StackContains(cx, fp))
 
 #define THIS_FRAME_OWNER(cx, argc, vp, fnname, args, thisobj, fp, dbg)       \
     THIS_FRAME(cx, argc, vp, fnname, args, thisobj, fp);                     \
     Debugger *dbg = Debugger::fromChildJSObject(thisobj)
@@ -3044,17 +3050,17 @@ Frame_GetEnv(JSContext *cx, StackFrame *
     return GetScopeChain(cx, fp);
 }
 
 static JSBool
 DebuggerFrame_getEnvironment(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_FRAME_OWNER(cx, argc, vp, "get environment", args, thisobj, fp, dbg);
 
-    Env *env;
+    RootedVar<Env*> env(cx);
     {
         AutoCompartment ac(cx, fp->scopeChain());
         if (!ac.enter())
             return false;
         env = Frame_GetEnv(cx, fp);
         if (!env)
             return false;
     }
@@ -3199,22 +3205,22 @@ DebuggerFrame_getArguments(JSContext *cx
         if (!DefineNativeProperty(cx, argsobj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),
                                   Int32Value(fargc), NULL, NULL,
                                   JSPROP_PERMANENT | JSPROP_READONLY, 0, 0))
         {
             return false;
         }
 
         for (int32_t i = 0; i < fargc; i++) {
-            JSFunction *getobj =
-                js_NewFunction(cx, NULL, DebuggerArguments_getArg, 0, 0, global, NULL,
-                               JSFunction::ExtendedFinalizeKind);
+            RootedVarFunction getobj(cx);
+            getobj = js_NewFunction(cx, NULL, DebuggerArguments_getArg, 0, 0, global, NULL,
+                                    JSFunction::ExtendedFinalizeKind);
             if (!getobj ||
                 !DefineNativeProperty(cx, argsobj, INT_TO_JSID(i), UndefinedValue(),
-                                      JS_DATA_TO_FUNC_PTR(PropertyOp, getobj), NULL,
+                                      JS_DATA_TO_FUNC_PTR(PropertyOp, getobj.reference()), NULL,
                                       JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_GETTER, 0, 0))
             {
                 return false;
             }
             getobj->setExtendedSlot(0, Int32Value(i));
         }
     } else {
         argsobj = NULL;
@@ -3229,27 +3235,27 @@ DebuggerFrame_getScript(JSContext *cx, u
 {
     THIS_FRAME(cx, argc, vp, "get script", args, thisobj, fp);
     Debugger *debug = Debugger::fromChildJSObject(thisobj);
 
     JSObject *scriptObject = NULL;
     if (fp->isFunctionFrame() && !fp->isEvalFrame()) {
         JSFunction *callee = fp->callee().toFunction();
         if (callee->isInterpreted()) {
-            scriptObject = debug->wrapScript(cx, callee->script());
+            scriptObject = debug->wrapScript(cx, RootedVar<JSScript*>(cx, callee->script()));
             if (!scriptObject)
                 return false;
         }
     } else if (fp->isScriptFrame()) {
         /*
          * We got eval, JS_Evaluate*, or JS_ExecuteScript non-function script
          * frames.
          */
         JSScript *script = fp->script();
-        scriptObject = debug->wrapScript(cx, script);
+        scriptObject = debug->wrapScript(cx, RootedVar<JSScript*>(cx, script));
         if (!scriptObject)
             return false;
     }
     args.rval().setObjectOrNull(scriptObject);
     return true;
 }
 
 static JSBool
@@ -3357,21 +3363,24 @@ DebuggerFrame_setOnPop(JSContext *cx, un
     thisobj->setReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER, args[0]);
     args.rval().setUndefined();
     return true;
 }
 
 namespace js {
 
 JSBool
-EvaluateInEnv(JSContext *cx, Env *env, StackFrame *fp, const jschar *chars,
+EvaluateInEnv(JSContext *cx, Handle<Env*> env, StackFrame *fp, const jschar *chars,
               unsigned length, const char *filename, unsigned lineno, Value *rval)
 {
     assertSameCompartment(cx, env, fp);
 
+    JS_ASSERT(!IsPoisonedPtr(chars));
+    SkipRoot skip(cx, &chars);
+
     if (fp) {
         /* Execute assumes an already-computed 'this" value. */
         if (!ComputeThis(cx, fp))
             return false;
     }
 
     /*
      * NB: This function breaks the assumption that the compiler can see all
@@ -3408,17 +3417,17 @@ DebuggerFrameEval(JSContext *cx, unsigne
     Debugger *dbg = Debugger::fromChildJSObject(thisobj);
 
     /* Check the first argument, the eval code string. */
     if (!args[0].isString()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_EXPECTED_TYPE,
                              "Debugger.Frame.eval", "string", InformalValueTypeName(args[0]));
         return false;
     }
-    JSLinearString *linearStr = args[0].toString()->ensureLinear(cx);
+    RootedVar<JSLinearString*> linearStr(cx, args[0].toString()->ensureLinear(cx));
     if (!linearStr)
         return false;
 
     /*
      * Gather keys and values of bindings, if any. This must be done in the
      * debugger compartment, since that is where any exceptions must be
      * thrown.
      */
@@ -3652,17 +3661,17 @@ static JSBool
 DebuggerObject_getParameterNames(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, "get parameterNames", args, obj);
     if (!obj->isFunction()) {
         args.rval().setUndefined();
         return true;
     }
 
-    const JSFunction *fun = obj->toFunction();
+    RootedVarFunction fun(cx, obj->toFunction());
     JSObject *result = NewDenseAllocatedArray(cx, fun->nargs, NULL);
     if (!result)
         return false;
     result->ensureDenseArrayInitializedLength(cx, 0, fun->nargs);
 
     if (fun->isInterpreted()) {
         JS_ASSERT(fun->nargs == fun->script()->bindings.numArgs());
 
@@ -3696,17 +3705,17 @@ DebuggerObject_getScript(JSContext *cx, 
     }
 
     JSFunction *fun = obj->toFunction();
     if (!fun->isInterpreted()) {
         args.rval().setUndefined();
         return true;
     }
 
-    JSObject *scriptObject = dbg->wrapScript(cx, fun->script());
+    JSObject *scriptObject = dbg->wrapScript(cx, RootedVar<JSScript*>(cx, fun->script()));
     if (!scriptObject)
         return false;
 
     args.rval().setObject(*scriptObject);
     return true;
 }
 
 static JSBool
@@ -3715,17 +3724,17 @@ DebuggerObject_getEnvironment(JSContext 
     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "get environment", args, dbg, obj);
 
     /* Don't bother switching compartments just to check obj's type and get its env. */
     if (!obj->isFunction() || !obj->toFunction()->isInterpreted()) {
         args.rval().setUndefined();
         return true;
     }
 
-    Env *env = obj->toFunction()->environment();
+    RootedVar<Env*> env(cx, obj->toFunction()->environment());
     return dbg->wrapEnvironment(cx, env, &args.rval());
 }
 
 static JSBool
 DebuggerObject_getOwnPropertyDescriptor(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "getOwnPropertyDescriptor", args, dbg, obj);
 
@@ -4043,17 +4052,17 @@ ApplyOrCall(JSContext *cx, unsigned argc
     AutoValueVector argv(cx);
     if (mode == ApplyMode) {
         if (argc >= 2 && !args[1].isNullOrUndefined()) {
             if (!args[1].isObject()) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_APPLY_ARGS,
                                      js_apply_str);
                 return false;
             }
-            JSObject *argsobj = &args[1].toObject();
+            RootedVarObject argsobj(cx, &args[1].toObject());
             if (!js_GetLengthProperty(cx, argsobj, &callArgc))
                 return false;
             callArgc = unsigned(JS_MIN(callArgc, StackSpace::ARGS_LENGTH_MAX));
             if (!argv.growBy(callArgc) || !GetElements(cx, argsobj, callArgc, argv.begin()))
                 return false;
             callArgv = argv.begin();
         }
     } else {
@@ -4210,17 +4219,17 @@ DebuggerEnv_checkThis(JSContext *cx, con
     return thisobj;
 }
 
 #define THIS_DEBUGENV(cx, argc, vp, fnname, args, envobj, env)                \
     CallArgs args = CallArgsFromVp(argc, vp);                                 \
     JSObject *envobj = DebuggerEnv_checkThis(cx, args, fnname);               \
     if (!envobj)                                                              \
         return false;                                                         \
-    Env *env = static_cast<Env *>(envobj->getPrivate());                      \
+    RootedVar<Env*> env(cx, static_cast<Env *>(envobj->getPrivate()));        \
     JS_ASSERT(env)
 
 #define THIS_DEBUGENV_OWNER(cx, argc, vp, fnname, args, envobj, env, dbg)     \
     THIS_DEBUGENV(cx, argc, vp, fnname, args, envobj, env);                   \
     Debugger *dbg = Debugger::fromChildJSObject(envobj)
 
 static JSBool
 DebuggerEnv_construct(JSContext *cx, unsigned argc, Value *vp)
@@ -4249,17 +4258,17 @@ DebuggerEnv_getType(JSContext *cx, unsig
 }
 
 static JSBool
 DebuggerEnv_getParent(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGENV_OWNER(cx, argc, vp, "get parent", args, envobj, env, dbg);
 
     /* Don't bother switching compartments just to get env's parent. */
-    Env *parent = env->enclosingScope();
+    RootedVar<Env*> parent(cx, env->enclosingScope());
     return dbg->wrapEnvironment(cx, parent, &args.rval());
 }
 
 static JSBool
 DebuggerEnv_getObject(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGENV_OWNER(cx, argc, vp, "get type", args, envobj, env, dbg);
 
@@ -4375,39 +4384,39 @@ DebuggerEnv_setVariable(JSContext *cx, u
 {
     REQUIRE_ARGC("Debugger.Environment.setVariable", 2);
     THIS_DEBUGENV_OWNER(cx, argc, vp, "setVariable", args, envobj, env, dbg);
 
     jsid id;
     if (!ValueToIdentifier(cx, args[0], &id))
         return false;
 
-    Value v = args[1];
-    if (!dbg->unwrapDebuggeeValue(cx, &v))
+    RootedVarValue v(cx, args[1]);
+    if (!dbg->unwrapDebuggeeValue(cx, v.address()))
         return false;
 
     {
         AutoCompartment ac(cx, env);
-        if (!ac.enter() || !cx->compartment->wrapId(cx, &id) || !cx->compartment->wrap(cx, &v))
+        if (!ac.enter() || !cx->compartment->wrapId(cx, &id) || !cx->compartment->wrap(cx, v.address()))
             return false;
 
         /* This can trigger setters. */
         ErrorCopier ec(ac, dbg->toJSObject());
 
         /* Make sure the environment actually has the specified binding. */
         bool has;
         if (!env->hasProperty(cx, id, &has))
             return false;
         if (!has) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_VARIABLE_NOT_FOUND);
             return false;
         }
 
         /* Just set the property. */
-        if (!env->setGeneric(cx, id, &v, true))
+        if (!env->setGeneric(cx, id, v.address(), true))
             return false;
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 static JSPropertySpec DebuggerEnv_properties[] = {
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -117,17 +117,17 @@ class Debugger {
     ObjectWeakMap objects;
 
     /* The map from debuggee Envs to Debugger.Environment instances. */
     ObjectWeakMap environments;
 
     class FrameRange;
     class ScriptQuery;
 
-    bool addDebuggeeGlobal(JSContext *cx, GlobalObject *obj);
+    bool addDebuggeeGlobal(JSContext *cx, Handle<GlobalObject*> obj);
     void removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
                               GlobalObjectSet::Enum *compartmentEnum,
                               GlobalObjectSet::Enum *debugEnum);
 
     /*
      * Cope with an error or exception in a debugger hook.
      *
      * If callHook is true, then call the uncaughtExceptionHook, if any. If, in
@@ -219,23 +219,23 @@ class Debugger {
     JSTrapStatus fireDebuggerStatement(JSContext *cx, Value *vp);
     JSTrapStatus fireExceptionUnwind(JSContext *cx, Value *vp);
     JSTrapStatus fireEnterFrame(JSContext *cx, Value *vp);
 
     /*
      * Allocate and initialize a Debugger.Script instance whose referent is
      * |script|.
      */
-    JSObject *newDebuggerScript(JSContext *cx, JSScript *script);
+    JSObject *newDebuggerScript(JSContext *cx, Handle<JSScript*> script);
 
     /*
      * Receive a "new script" event from the engine. A new script was compiled
      * or deserialized.
      */
-    void fireNewScript(JSContext *cx, JSScript *script);
+    void fireNewScript(JSContext *cx, Handle<JSScript*> script);
 
     static inline Debugger *fromLinks(JSCList *links);
     inline Breakpoint *firstBreakpoint() const;
 
   public:
     Debugger(JSContext *cx, JSObject *dbg);
     ~Debugger();
 
@@ -284,17 +284,17 @@ class Debugger {
     inline bool observesGlobal(GlobalObject *global) const;
     inline bool observesFrame(StackFrame *fp) const;
 
     /*
      * If env is NULL, call vp->setNull() and return true. Otherwise, find or
      * create a Debugger.Environment object for the given Env. On success,
      * store the Environment object in *vp and return true.
      */
-    bool wrapEnvironment(JSContext *cx, Env *env, Value *vp);
+    bool wrapEnvironment(JSContext *cx, Handle<Env*> env, Value *vp);
 
     /*
      * Like cx->compartment->wrap(cx, vp), but for the debugger compartment.
      *
      * Preconditions: *vp is a value from a debuggee compartment; cx is in the
      * debugger's compartment.
      *
      * If *vp is an object, this produces a (new or existing) Debugger.Object
@@ -365,17 +365,17 @@ class Debugger {
      */
     bool receiveCompletionValue(AutoCompartment &ac, bool ok, Value val, Value *vp);
 
     /*
      * Return the Debugger.Script object for |script|, or create a new one if
      * needed. The context |cx| must be in the debugger compartment; |script|
      * must be a script in a debuggee compartment.
      */
-    JSObject *wrapScript(JSContext *cx, JSScript *script);
+    JSObject *wrapScript(JSContext *cx, Handle<JSScript*> script);
 
   private:
     Debugger(const Debugger &) MOZ_DELETE;
     Debugger & operator=(const Debugger &) MOZ_DELETE;
 };
 
 class BreakpointSite {
     friend class Breakpoint;
@@ -557,14 +557,14 @@ Debugger::onNewScript(JSContext *cx, JSS
 {
     JS_ASSERT_IF(script->compileAndGo, compileAndGoGlobal);
     JS_ASSERT_IF(!script->compileAndGo, !compileAndGoGlobal);
     if (!script->compartment()->getDebuggees().empty())
         slowPathOnNewScript(cx, script, compileAndGoGlobal);
 }
 
 extern JSBool
-EvaluateInEnv(JSContext *cx, Env *env, StackFrame *fp, const jschar *chars,
+EvaluateInEnv(JSContext *cx, Handle<Env*> env, StackFrame *fp, const jschar *chars,
               unsigned length, const char *filename, unsigned lineno, Value *rval);
 
 }
 
 #endif /* Debugger_h__ */
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -284,52 +284,52 @@ GlobalObject::create(JSContext *cx, Clas
     if (!res)
         return NULL;
     obj->initSlot(REGEXP_STATICS, ObjectValue(*res));
     obj->initFlags(0);
 
     return obj;
 }
 
-bool
-GlobalObject::initStandardClasses(JSContext *cx)
+/* static */ bool
+GlobalObject::initStandardClasses(JSContext *cx, Handle<GlobalObject*> global)
 {
     JSAtomState &state = cx->runtime->atomState;
 
     /* Define a top-level property 'undefined' with the undefined value. */
-    if (!defineProperty(cx, state.typeAtoms[JSTYPE_VOID], UndefinedValue(),
-                        JS_PropertyStub, JS_StrictPropertyStub, JSPROP_PERMANENT | JSPROP_READONLY))
+    if (!global->defineProperty(cx, state.typeAtoms[JSTYPE_VOID], UndefinedValue(),
+                                JS_PropertyStub, JS_StrictPropertyStub, JSPROP_PERMANENT | JSPROP_READONLY))
     {
         return false;
     }
 
-    if (!initFunctionAndObjectClasses(cx))
+    if (!global->initFunctionAndObjectClasses(cx))
         return false;
 
     /* Initialize the rest of the standard objects and functions. */
-    return js_InitArrayClass(cx, this) &&
-           js_InitBooleanClass(cx, this) &&
-           js_InitExceptionClasses(cx, this) &&
-           js_InitMathClass(cx, this) &&
-           js_InitNumberClass(cx, this) &&
-           js_InitJSONClass(cx, this) &&
-           js_InitRegExpClass(cx, this) &&
-           js_InitStringClass(cx, this) &&
-           js_InitTypedArrayClasses(cx, this) &&
+    return js_InitArrayClass(cx, global) &&
+           js_InitBooleanClass(cx, global) &&
+           js_InitExceptionClasses(cx, global) &&
+           js_InitMathClass(cx, global) &&
+           js_InitNumberClass(cx, global) &&
+           js_InitJSONClass(cx, global) &&
+           js_InitRegExpClass(cx, global) &&
+           js_InitStringClass(cx, global) &&
+           js_InitTypedArrayClasses(cx, global) &&
 #if JS_HAS_XML_SUPPORT
-           js_InitXMLClasses(cx, this) &&
+           js_InitXMLClasses(cx, global) &&
 #endif
 #if JS_HAS_GENERATORS
-           js_InitIteratorClasses(cx, this) &&
+           js_InitIteratorClasses(cx, global) &&
 #endif
-           js_InitDateClass(cx, this) &&
-           js_InitWeakMapClass(cx, this) &&
-           js_InitProxyClass(cx, this) &&
-           js_InitMapClass(cx, this) &&
-           js_InitSetClass(cx, this);
+           js_InitDateClass(cx, global) &&
+           js_InitWeakMapClass(cx, global) &&
+           js_InitProxyClass(cx, global) &&
+           js_InitMapClass(cx, global) &&
+           js_InitSetClass(cx, global);
 }
 
 void
 GlobalObject::clear(JSContext *cx)
 {
     for (int key = JSProto_Null; key < JSProto_LIMIT * 3; key++)
         setSlot(key, UndefinedValue());
 
@@ -470,47 +470,47 @@ GlobalObject::getDebuggers()
 {
     Value debuggers = getReservedSlot(DEBUGGERS);
     if (debuggers.isUndefined())
         return NULL;
     JS_ASSERT(debuggers.toObject().getClass() == &GlobalDebuggees_class);
     return (DebuggerVector *) debuggers.toObject().getPrivate();
 }
 
-GlobalObject::DebuggerVector *
-GlobalObject::getOrCreateDebuggers(JSContext *cx)
+/* static */ GlobalObject::DebuggerVector *
+GlobalObject::getOrCreateDebuggers(JSContext *cx, Handle<GlobalObject*> global)
 {
-    assertSameCompartment(cx, this);
-    DebuggerVector *debuggers = getDebuggers();
+    assertSameCompartment(cx, global);
+    DebuggerVector *debuggers = global->getDebuggers();
     if (debuggers)
         return debuggers;
 
-    JSObject *obj = NewObjectWithGivenProto(cx, &GlobalDebuggees_class, NULL, this);
+    JSObject *obj = NewObjectWithGivenProto(cx, &GlobalDebuggees_class, NULL, global);
     if (!obj)
         return NULL;
     debuggers = cx->new_<DebuggerVector>();
     if (!debuggers)
         return NULL;
     obj->setPrivate(debuggers);
-    setReservedSlot(DEBUGGERS, ObjectValue(*obj));
+    global->setReservedSlot(DEBUGGERS, ObjectValue(*obj));
     return debuggers;
 }
 
-bool
-GlobalObject::addDebugger(JSContext *cx, Debugger *dbg)
+/* static */ bool
+GlobalObject::addDebugger(JSContext *cx, Handle<GlobalObject*> global, Debugger *dbg)
 {
-    DebuggerVector *debuggers = getOrCreateDebuggers(cx);
+    DebuggerVector *debuggers = getOrCreateDebuggers(cx, global);
     if (!debuggers)
         return false;
 #ifdef DEBUG
     for (Debugger **p = debuggers->begin(); p != debuggers->end(); p++)
         JS_ASSERT(*p != dbg);
 #endif
-    if (debuggers->empty() && !compartment()->addDebuggee(cx, this))
+    if (debuggers->empty() && !global->compartment()->addDebuggee(cx, global))
         return false;
     if (!debuggers->append(dbg)) {
-        compartment()->removeDebuggee(cx->runtime->defaultFreeOp(), this);
+        global->compartment()->removeDebuggee(cx->runtime->defaultFreeOp(), global);
         return false;
     }
     return true;
 }
 
 } // namespace js
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -339,34 +339,34 @@ class GlobalObject : public JSObject {
 
     const Value &getOriginalEval() const {
         JS_ASSERT(getSlot(EVAL).isObject());
         return getSlot(EVAL);
     }
 
     bool getFunctionNamespace(JSContext *cx, Value *vp);
 
-    bool initGeneratorClass(JSContext *cx);
-    bool initStandardClasses(JSContext *cx);
+    static bool initGeneratorClass(JSContext *cx, Handle<GlobalObject*> global);
+    static bool initStandardClasses(JSContext *cx, Handle<GlobalObject*> global);
 
     typedef js::Vector<js::Debugger *, 0, js::SystemAllocPolicy> DebuggerVector;
 
     /*
      * The collection of Debugger objects debugging this global. If this global
      * is not a debuggee, this returns either NULL or an empty vector.
      */
     DebuggerVector *getDebuggers();
 
     /*
      * The same, but create the empty vector if one does not already
      * exist. Returns NULL only on OOM.
      */
-    DebuggerVector *getOrCreateDebuggers(JSContext *cx);
+    static DebuggerVector *getOrCreateDebuggers(JSContext *cx, Handle<GlobalObject*> global);
 
-    bool addDebugger(JSContext *cx, Debugger *dbg);
+    static bool addDebugger(JSContext *cx, Handle<GlobalObject*> global, Debugger *dbg);
 };
 
 /*
  * Define ctor.prototype = proto as non-enumerable, non-configurable, and
  * non-writable; define proto.constructor = ctor as non-enumerable but
  * configurable and writable.
  */
 extern bool
--- a/js/src/vm/ObjectImpl.h
+++ b/js/src/vm/ObjectImpl.h
@@ -221,16 +221,24 @@ struct PropDesc {
     bool checkGetter(JSContext *cx);
     bool checkSetter(JSContext *cx);
 
     bool unwrapDebuggerObjectsInto(JSContext *cx, Debugger *dbg, JSObject *obj,
                                    PropDesc *unwrapped) const;
 
     bool wrapInto(JSContext *cx, JSObject *obj, const jsid &id, jsid *wrappedId,
                   PropDesc *wrappedDesc) const;
+
+    struct StackRoot {
+        StackRoot(JSContext *cx, PropDesc *pd)
+          : pdRoot(cx, &pd->pd_), valueRoot(cx, &pd->value_),
+            getRoot(cx, &pd->get_), setRoot(cx, &pd->set_)
+        {}
+        RootValue pdRoot, valueRoot, getRoot, setRoot;
+    };
 };
 
 class DenseElementsHeader;
 class SparseElementsHeader;
 class Uint8ElementsHeader;
 class Int8ElementsHeader;
 class Uint16ElementsHeader;
 class Int16ElementsHeader;
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -389,21 +389,23 @@ RegExpObject::createNoStatics(JSContext 
 
     RegExpObjectBuilder builder(cx);
     return builder.build(source, flags);
 }
 
 bool
 RegExpObject::createShared(JSContext *cx, RegExpGuard *g)
 {
+    RootedVar<RegExpObject*> self(cx, this);
+
     JS_ASSERT(!maybeShared());
     if (!cx->compartment->regExps.get(cx, getSource(), getFlags(), g))
         return false;
 
-    setShared(cx, **g);
+    self->setShared(cx, **g);
     return true;
 }
 
 Shape *
 RegExpObject::assignInitialShape(JSContext *cx)
 {
     JS_ASSERT(isRegExp());
     JS_ASSERT(nativeEmpty());
--- a/js/src/vm/RegExpStatics.h
+++ b/js/src/vm/RegExpStatics.h
@@ -236,27 +236,41 @@ class RegExpStatics
 
     /* Substring creators. */
 
     void getParen(size_t pairNum, JSSubString *out) const;
     void getLastMatch(JSSubString *out) const;
     void getLastParen(JSSubString *out) const;
     void getLeftContext(JSSubString *out) const;
     void getRightContext(JSSubString *out) const;
+
+    class StackRoot
+    {
+        Root<JSLinearString*> matchPairsInputRoot;
+        RootString pendingInputRoot;
+
+      public:
+        StackRoot(JSContext *cx, RegExpStatics *buffer)
+          : matchPairsInputRoot(cx, (JSLinearString**) &buffer->matchPairsInput),
+            pendingInputRoot(cx, (JSString**) &buffer->pendingInput)
+        {}
+    };
 };
 
 class PreserveRegExpStatics
 {
     RegExpStatics * const original;
     RegExpStatics buffer;
+    RegExpStatics::StackRoot bufferRoot;
 
   public:
-    explicit PreserveRegExpStatics(RegExpStatics *original)
+    explicit PreserveRegExpStatics(JSContext *cx, RegExpStatics *original)
      : original(original),
-       buffer(RegExpStatics::InitBuffer())
+       buffer(RegExpStatics::InitBuffer()),
+       bufferRoot(cx, &buffer)
     {}
 
     bool init(JSContext *cx) {
         return original->save(cx, &buffer);
     }
 
     inline ~PreserveRegExpStatics();
 };
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -171,20 +171,19 @@ CallObject::create(JSContext *cx, JSScri
     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.
      */
-    JSObject &global = enclosing->global();
-    if (&global != obj->getParent()) {
+    if (&enclosing->global() != obj->getParent()) {
         JS_ASSERT(obj->getParent() == NULL);
-        if (!obj->setParent(cx, &global))
+        if (!JSObject::setParent(cx, obj, RootedVarObject(cx, &enclosing->global())))
             return NULL;
     }
 
 #ifdef DEBUG
     JS_ASSERT(!obj->inDictionaryMode());
     for (Shape::Range r = obj->lastProperty(); !r.empty(); r.popFront()) {
         const Shape &s = r.front();
         if (s.hasSlot()) {
@@ -221,17 +220,18 @@ CallObject::createForFunction(JSContext 
     RootedVarObject scopeChain(cx, fp->scopeChain());
     JS_ASSERT_IF(scopeChain->isWith() || scopeChain->isBlock() || scopeChain->isCall(),
                  scopeChain->getPrivate() != fp);
 
     /*
      * For a named function expression Call's parent points to an environment
      * object holding function's name.
      */
-    if (JSAtom *lambdaName = CallObjectLambdaName(fp->fun())) {
+    RootedVarAtom lambdaName(cx, CallObjectLambdaName(fp->fun()));
+    if (lambdaName) {
         scopeChain = DeclEnvObject::create(cx, fp);
         if (!scopeChain)
             return NULL;
 
         if (!DefineNativeProperty(cx, scopeChain, ATOM_TO_JSID(lambdaName),
                                   ObjectValue(fp->callee()), NULL, NULL,
                                   JSPROP_PERMANENT | JSPROP_READONLY, 0, 0)) {
             return NULL;
@@ -418,17 +418,17 @@ DeclEnvObject::create(JSContext *cx, Sta
         return NULL;
 
     RootedVarShape emptyDeclEnvShape(cx);
     emptyDeclEnvShape = EmptyShape::getInitialShape(cx, &DeclEnvClass, NULL,
                                                     &fp->global(), FINALIZE_KIND);
     if (!emptyDeclEnvShape)
         return NULL;
 
-    JSObject *obj = JSObject::create(cx, FINALIZE_KIND, emptyDeclEnvShape, type, NULL);
+    RootedVarObject obj(cx, JSObject::create(cx, FINALIZE_KIND, emptyDeclEnvShape, type, NULL));
     if (!obj)
         return NULL;
 
     obj->setPrivate(fp);
     if (!obj->asScope().setEnclosingScope(cx, fp->scopeChain()))
         return NULL;
 
     return &obj->asDeclEnv();
@@ -444,17 +444,17 @@ WithObject::create(JSContext *cx, StackF
         return NULL;
 
     RootedVarShape emptyWithShape(cx);
     emptyWithShape = EmptyShape::getInitialShape(cx, &WithClass, proto,
                                                  &enclosing->global(), FINALIZE_KIND);
     if (!emptyWithShape)
         return NULL;
 
-    JSObject *obj = JSObject::create(cx, FINALIZE_KIND, emptyWithShape, type, NULL);
+    RootedVarObject obj(cx, JSObject::create(cx, FINALIZE_KIND, emptyWithShape, type, NULL));
     if (!obj)
         return NULL;
 
     if (!obj->asScope().setEnclosingScope(cx, enclosing))
         return NULL;
 
     obj->setReservedSlot(DEPTH_SLOT, PrivateUint32Value(depth));
     obj->setPrivate(js_FloatingFrameIfGenerator(cx, fp));
@@ -689,46 +689,45 @@ Class js::WithClass = {
         with_Enumerate,
         with_TypeOf,
         with_ThisObject,
         NULL,             /* clear */
     }
 };
 
 ClonedBlockObject *
-ClonedBlockObject::create(JSContext *cx, StaticBlockObject &block, StackFrame *fp)
+ClonedBlockObject::create(JSContext *cx, Handle<StaticBlockObject*> block, StackFrame *fp)
 {
     RootedVarTypeObject type(cx);
-    type = block.getNewType(cx);
+    type = block->getNewType(cx);
     if (!type)
         return NULL;
 
     HeapSlot *slots;
-    if (!PreallocateObjectDynamicSlots(cx, block.lastProperty(), &slots))
+    if (!PreallocateObjectDynamicSlots(cx, block->lastProperty(), &slots))
         return NULL;
 
     RootedVarShape shape(cx);
-    shape = block.lastProperty();
+    shape = block->lastProperty();
 
-    JSObject *obj = JSObject::create(cx, FINALIZE_KIND, shape, type, slots);
+    RootedVarObject obj(cx, JSObject::create(cx, FINALIZE_KIND, shape, type, slots));
     if (!obj)
         return NULL;
 
     /* Set the parent if necessary, as for call objects. */
-    JSObject &global = fp->global();
-    if (&global != obj->getParent()) {
+    if (&fp->global() != obj->getParent()) {
         JS_ASSERT(obj->getParent() == NULL);
-        if (!obj->setParent(cx, &global))
+        if (!JSObject::setParent(cx, obj, RootedVarObject(cx, &fp->global())))
             return NULL;
     }
 
     JS_ASSERT(!obj->inDictionaryMode());
-    JS_ASSERT(obj->slotSpan() >= block.slotCount() + RESERVED_SLOTS);
+    JS_ASSERT(obj->slotSpan() >= block->slotCount() + RESERVED_SLOTS);
 
-    obj->setReservedSlot(DEPTH_SLOT, PrivateUint32Value(block.stackDepth()));
+    obj->setReservedSlot(DEPTH_SLOT, PrivateUint32Value(block->stackDepth()));
     obj->setPrivate(js_FloatingFrameIfGenerator(cx, fp));
 
     if (obj->lastProperty()->extensibleParents() && !obj->generateOwnShape(cx))
         return NULL;
 
     return &obj->asClonedBlock();
 }
 
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -272,17 +272,17 @@ class StaticBlockObject : public BlockOb
     bool isAliased(unsigned i);
 
     const Shape *addVar(JSContext *cx, jsid id, int index, bool *redeclared);
 };
 
 class ClonedBlockObject : public BlockObject
 {
   public:
-    static ClonedBlockObject *create(JSContext *cx, StaticBlockObject &block, StackFrame *fp);
+    static ClonedBlockObject *create(JSContext *cx, Handle<StaticBlockObject*> block, StackFrame *fp);
 
     /* The static block from which this block was cloned. */
     StaticBlockObject &staticBlock() const;
 
     /*
      * When this block's stack slots are about to be popped, 'put' must be
      * called to copy the slot values into this block's object slots.
      */
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -353,28 +353,25 @@ StackFrame::maintainNestingState() const
 }
 
 inline bool
 StackFrame::functionPrologue(JSContext *cx)
 {
     JS_ASSERT(isNonEvalFunctionFrame());
     JS_ASSERT(!isGeneratorFrame());
 
-    JSFunction *fun = this->fun();
-    JSScript *script = fun->script();
-
-    if (fun->isHeavyweight()) {
+    if (fun()->isHeavyweight()) {
         if (!CallObject::createForFunction(cx, this))
             return false;
     } else {
         /* Force instantiation of the scope chain, for JIT frames. */
         scopeChain();
     }
 
-    if (script->nesting()) {
+    if (script()->nesting()) {
         JS_ASSERT(maintainNestingState());
         types::NestingPrologue(cx, this);
     }
 
     return true;
 }
 
 inline void
--- a/js/src/vm/String-inl.h
+++ b/js/src/vm/String-inl.h
@@ -445,16 +445,18 @@ JSExternalString::finalize(js::FreeOp *f
     fin->finalize(fin, const_cast<jschar *>(chars()));
 }
 
 namespace js {
 
 static JS_ALWAYS_INLINE JSFixedString *
 NewShortString(JSContext *cx, const jschar *chars, size_t length)
 {
+    SkipRoot skip(cx, &chars);
+
     /*
      * Don't bother trying to find a static atom; measurement shows that not
      * many get here (for one, Atomize is catching them).
      */
     JS_ASSERT(JSShortString::lengthFits(length));
     JSInlineString *str = JSInlineString::lengthFits(length)
                           ? JSInlineString::new_(cx)
                           : JSShortString::new_(cx);
--- a/js/src/vm/StringObject-inl.h
+++ b/js/src/vm/StringObject-inl.h
@@ -66,17 +66,17 @@ StringObject::init(JSContext *cx, Handle
         } else {
             Shape *shape = assignInitialShape(cx);
             if (!shape)
                 return false;
             EmptyShape::insertInitialShape(cx, shape, self->getProto());
         }
     }
 
-    JS_ASSERT(nativeLookupNoAllocation(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom))->slot()
+    JS_ASSERT(self->nativeLookupNoAllocation(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom))->slot()
               == LENGTH_SLOT);
 
     self->setStringThis(str);
 
     return true;
 }
 
 inline StringObject *