Bug 879079 - Fix some exact rooting static analysis failures under js/src; r=bhackett
authorTerrence Cole <terrence@mozilla.com>
Mon, 03 Jun 2013 16:30:00 -0700
changeset 134516 9305b1486c2dfe418d642fef80fc01d78626a774
parent 134515 17aa0fec31dc5bb301c79b98c0d94ec310961203
child 134517 ba350ac49e418c245ec1222a1ca48e567239dd31
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersbhackett
bugs879079
milestone24.0a1
Bug 879079 - Fix some exact rooting static analysis failures under js/src; r=bhackett
js/public/CallArgs.h
js/src/builtin/Object.cpp
js/src/builtin/RegExp.cpp
js/src/frontend/Parser.cpp
js/src/jsapi.cpp
js/src/jsarray.cpp
js/src/jsatom.h
js/src/jsatominlines.h
js/src/jscntxtinlines.h
js/src/jscompartment.cpp
js/src/jsdate.cpp
js/src/jsdbgapi.cpp
js/src/jsexn.cpp
js/src/jsfun.cpp
js/src/jsiter.cpp
js/src/jsnum.cpp
js/src/json.cpp
js/src/jspropertytree.cpp
js/src/jsproxy.cpp
js/src/jsreflect.cpp
js/src/jsstr.cpp
js/src/jsstr.h
js/src/jswrapper.cpp
js/src/shell/js.cpp
js/src/vm/Debugger.cpp
js/src/vm/Interpreter.cpp
js/src/vm/SelfHosting.cpp
js/src/vm/Shape-inl.h
--- a/js/public/CallArgs.h
+++ b/js/public/CallArgs.h
@@ -54,16 +54,18 @@ typedef JSBool
  * |undefined|, use |JS_GetGlobalForObject| to compute the global object.  If
  * the value is some other primitive, use |JS_ValueToObject| to box it.
  */
 extern JS_PUBLIC_API(JS::Value)
 JS_ComputeThis(JSContext *cx, JS::Value *vp);
 
 namespace JS {
 
+extern JS_PUBLIC_DATA(const HandleValue) UndefinedHandleValue;
+
 /*
  * JS::CallReceiver encapsulates access to the callee, |this|, and eventual
  * return value for a function call.  The principal way to create a
  * CallReceiver is using JS::CallReceiverFromVp:
  *
  *   static JSBool
  *   FunctionReturningThis(JSContext *cx, unsigned argc, JS::Value *vp)
  *   {
@@ -294,16 +296,26 @@ class MOZ_STACK_CLASS CallArgsBase :
      * Returns the i-th zero-indexed argument, or |undefined| if there's no
      * such argument.
      */
     Value get(unsigned i) const {
         return i < length() ? this->argv_[i] : UndefinedValue();
     }
 
     /*
+     * Returns the i-th zero-indexed argument as a handle, or |undefined| if
+     * there's no such argument.
+     */
+    HandleValue handleOrUndefinedAt(unsigned i) const {
+        return i < length()
+               ? HandleValue::fromMarkedLocation(&this->argv_[i])
+               : UndefinedHandleValue;
+    }
+
+    /*
      * Returns true if the i-th zero-indexed argument is present and is not
      * |undefined|.
      */
     bool hasDefined(unsigned i) const {
         return i < argc_ && !this->argv_[i].isUndefined();
     }
 
   public:
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -67,17 +67,17 @@ js::obj_construct(JSContext *cx, unsigne
 /* ES5 15.2.4.7. */
 static JSBool
 obj_propertyIsEnumerable(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* Step 1. */
     RootedId id(cx);
-    if (!ValueToId<CanGC>(cx, args.get(0), &id))
+    if (!ValueToId<CanGC>(cx, args.handleOrUndefinedAt(0), &id))
         return false;
 
     /* Step 2. */
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
     /* Steps 3. */
@@ -178,17 +178,18 @@ obj_toSource(JSContext *cx, unsigned arg
                 valcnt = 1;
                 gsop[0].set(NULL);
                 if (!JSObject::getGeneric(cx, obj, obj, id, val[0]))
                     return false;
             }
         }
 
         /* Convert id to a linear string. */
-        JSString *s = ToString<CanGC>(cx, IdToValue(id));
+        RootedValue idv(cx, IdToValue(id));
+        JSString *s = ToString<CanGC>(cx, idv);
         if (!s)
             return false;
         Rooted<JSLinearString*> idstr(cx, s->ensureLinear(cx));
         if (!idstr)
             return false;
 
         /*
          * If id is a string that's not an identifier, or if it's a negative
@@ -375,17 +376,17 @@ 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;
     }
 
     RootedId id(cx);
-    if (!ValueToId<CanGC>(cx, args[0], &id))
+    if (!ValueToId<CanGC>(cx, args.handleAt(0), &id))
         return false;
 
     RootedObject descObj(cx, NewBuiltinClassInstance(cx, &ObjectClass));
     if (!descObj)
         return false;
 
     JSAtomState &names = cx->names();
     RootedValue trueVal(cx, BooleanValue(true));
@@ -427,17 +428,17 @@ js::obj_defineSetter(JSContext *cx, unsi
 }
 
 static JSBool
 obj_lookupGetter(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     RootedId id(cx);
-    if (!ValueToId<CanGC>(cx, args.get(0), &id))
+    if (!ValueToId<CanGC>(cx, args.handleOrUndefinedAt(0), &id))
         return JS_FALSE;
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return JS_FALSE;
     if (obj->isProxy()) {
         // The vanilla getter lookup code below requires that the object is
         // native. Handle proxies separately.
         args.rval().setUndefined();
@@ -463,17 +464,17 @@ obj_lookupGetter(JSContext *cx, unsigned
 }
 
 static JSBool
 obj_lookupSetter(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     RootedId id(cx);
-    if (!ValueToId<CanGC>(cx, args.get(0), &id))
+    if (!ValueToId<CanGC>(cx, args.handleOrUndefinedAt(0), &id))
         return JS_FALSE;
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return JS_FALSE;
     if (obj->isProxy()) {
         // The vanilla setter lookup code below requires that the object is
         // native. Handle proxies separately.
         args.rval().setUndefined();
@@ -568,17 +569,17 @@ obj_watch(JSContext *cx, unsigned argc, 
         return false;
     }
 
     RootedObject callable(cx, ValueToCallable(cx, args[1], args.length() - 2));
     if (!callable)
         return false;
 
     RootedId propid(cx);
-    if (!ValueToId<CanGC>(cx, args[0], &propid))
+    if (!ValueToId<CanGC>(cx, args.handleAt(0), &propid))
         return false;
 
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
     RootedValue tmp(cx);
     unsigned attrs;
@@ -596,33 +597,33 @@ obj_unwatch(JSContext *cx, unsigned argc
     CallArgs args = CallArgsFromVp(argc, vp);
 
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
     args.rval().setUndefined();
     RootedId id(cx);
     if (argc != 0) {
-        if (!ValueToId<CanGC>(cx, args[0], &id))
+        if (!ValueToId<CanGC>(cx, args.handleAt(0), &id))
             return false;
     } else {
         id = JSID_VOID;
     }
     return JS_ClearWatchPoint(cx, obj, id, NULL, NULL);
 }
 
 #endif /* JS_HAS_OBJ_WATCHPOINT */
 
 /* ECMA 15.2.4.5. */
 static JSBool
 obj_hasOwnProperty(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    Value idValue = args.get(0);
+    HandleValue idValue = args.handleOrUndefinedAt(0);
 
     /* Step 1, 2. */
     jsid id;
     if (args.thisv().isObject() && ValueToId<NoGC>(cx, idValue, &id)) {
         JSObject *obj = &args.thisv().toObject(), *obj2;
         Shape *prop;
         if (!obj->isProxy() &&
             HasOwnProperty<NoGC>(cx, obj->getOps()->lookupGeneric, obj, id, &obj2, &prop))
@@ -741,17 +742,17 @@ obj_create(JSContext *cx, unsigned argc,
 static JSBool
 obj_getOwnPropertyDescriptor(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedObject obj(cx);
     if (!GetFirstArgumentAsObject(cx, args, "Object.getOwnPropertyDescriptor", &obj))
         return JS_FALSE;
     RootedId id(cx);
-    if (!ValueToId<CanGC>(cx, args.get(1), &id))
+    if (!ValueToId<CanGC>(cx, args.handleOrUndefinedAt(1), &id))
         return JS_FALSE;
     return GetOwnPropertyDescriptor(cx, obj, id, args.rval());
 }
 
 static JSBool
 obj_keys(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -846,17 +847,17 @@ static JSBool
 obj_defineProperty(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedObject obj(cx);
     if (!GetFirstArgumentAsObject(cx, args, "Object.defineProperty", &obj))
         return false;
 
     RootedId id(cx);
-    if (!ValueToId<CanGC>(cx, args.get(1), &id))
+    if (!ValueToId<CanGC>(cx, args.handleOrUndefinedAt(1), &id))
         return JS_FALSE;
 
     const Value descval = args.get(2);
 
     JSBool junk;
     if (!js_DefineOwnProperty(cx, obj, id, descval, &junk))
         return false;
 
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -272,17 +272,17 @@ CompileRegExpObject(JSContext *cx, RegEx
 
         source = AtomizeString<CanGC>(cx, str);
         if (!source)
             return false;
     }
 
     RegExpFlag flags = RegExpFlag(0);
     if (args.hasDefined(1)) {
-        RootedString flagStr(cx, ToString<CanGC>(cx, args[1]));
+        RootedString flagStr(cx, ToString<CanGC>(cx, args.handleAt(1)));
         if (!flagStr)
             return false;
         args[1].setString(flagStr);
         if (!ParseRegExpFlags(cx, flagStr, &flags))
             return false;
     }
 
     RootedAtom escapedSourceStr(cx, EscapeNakedForwardSlashes(cx, source));
@@ -597,17 +597,17 @@ js::ExecuteRegExp(JSContext *cx, HandleO
 /* ES5 15.10.6.2 (and 15.10.6.3, which calls 15.10.6.2). */
 static RegExpRunStatus
 ExecuteRegExp(JSContext *cx, CallArgs args, MatchConduit &matches)
 {
     /* Step 1 (a) was performed by CallNonGenericMethod. */
     RootedObject regexp(cx, &args.thisv().toObject());
 
     /* Step 2. */
-    RootedString string(cx, ToString<CanGC>(cx, args.get(0)));
+    RootedString string(cx, ToString<CanGC>(cx, args.handleOrUndefinedAt(0)));
     if (!string)
         return RegExpRunStatus_Error;
 
     return ExecuteRegExp(cx, regexp, string, matches);
 }
 
 /* ES5 15.10.6.2. */
 static bool
@@ -617,17 +617,17 @@ regexp_exec_impl(JSContext *cx, CallArgs
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
     MatchConduit conduit(&matches);
 
     /*
      * Extract arguments to share between ExecuteRegExp()
      * and CreateRegExpMatchResult().
      */
     RootedObject regexp(cx, &args.thisv().toObject());
-    RootedString string(cx, ToString<CanGC>(cx, args.get(0)));
+    RootedString string(cx, ToString<CanGC>(cx, args.handleOrUndefinedAt(0)));
     if (!string)
         return false;
 
     RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, conduit);
 
     if (status == RegExpRunStatus_Error)
         return false;
 
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -6719,21 +6719,23 @@ Parser<ParseHandler>::primaryExpr(TokenK
             VALUE   = 0x4 | GET | SET
         };
 
         pn = handler.newList(PNK_OBJECT, null(), JSOP_NEWINIT);
         if (!pn)
             return null();
 
         RootedAtom atom(context);
+        Value tmp;
         for (;;) {
             TokenKind ltok = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
             switch (ltok) {
               case TOK_NUMBER:
-                atom = ToAtom<CanGC>(context, DoubleValue(tokenStream.currentToken().number()));
+                tmp = DoubleValue(tokenStream.currentToken().number());
+                atom = ToAtom<CanGC>(context, HandleValue::fromMarkedLocation(&tmp));
                 if (!atom)
                     return null();
                 pn3 = handler.newNumber(tokenStream.currentToken());
                 break;
               case TOK_NAME:
                 {
                     atom = tokenStream.currentToken().name();
                     if (atom == context->names().get) {
@@ -6756,27 +6758,29 @@ Parser<ParseHandler>::primaryExpr(TokenK
                     } else if (tt == TOK_STRING) {
                         atom = tokenStream.currentToken().atom();
 
                         uint32_t index;
                         if (atom->isIndex(&index)) {
                             pn3 = handler.newNumber(index);
                             if (!pn3)
                                 return null();
-                            atom = ToAtom<CanGC>(context, DoubleValue(index));
+                            tmp = DoubleValue(index);
+                            atom = ToAtom<CanGC>(context, HandleValue::fromMarkedLocation(&tmp));
                             if (!atom)
                                 return null();
                         } else {
                             pn3 = handler.newName(atom->asPropertyName(), pc, PNK_STRING);
                             if (!pn3)
                                 return null();
                         }
                     } else if (tt == TOK_NUMBER) {
                         double number = tokenStream.currentToken().number();
-                        atom = ToAtom<CanGC>(context, DoubleValue(number));
+                        tmp = DoubleValue(number);
+                        atom = ToAtom<CanGC>(context, HandleValue::fromMarkedLocation(&tmp));
                         if (!atom)
                             return null();
                         pn3 = handler.newNumber(tokenStream.currentToken());
                         if (!pn3)
                             return null();
                     } else {
                         tokenStream.ungetToken();
                         pn3 = handler.newAtom(PNK_NAME, atom);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -283,16 +283,17 @@ JS_PUBLIC_API(JSBool)
 JS_ConvertArgumentsVA(JSContext *cx, unsigned argc, jsval *argv, const char *format, va_list ap)
 {
     jsval *sp;
     JSBool required;
     char c;
     double d;
     JSString *str;
     RootedObject obj(cx);
+    RootedValue val(cx);
 
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, JSValueArray(argv - 2, argc + 2));
     sp = argv;
     required = JS_TRUE;
     while ((c = *format++) != '\0') {
         if (isspace(c))
@@ -342,17 +343,18 @@ JS_ConvertArgumentsVA(JSContext *cx, uns
             break;
           case 'I':
             if (!JS_ValueToNumber(cx, *sp, &d))
                 return JS_FALSE;
             *va_arg(ap, double *) = ToInteger(d);
             break;
           case 'S':
           case 'W':
-            str = ToString<CanGC>(cx, *sp);
+            val = *sp;
+            str = ToString<CanGC>(cx, val);
             if (!str)
                 return JS_FALSE;
             *sp = STRING_TO_JSVAL(str);
             if (c == 'W') {
                 JSStableString *stable = str->ensureStable(cx);
                 if (!stable)
                     return JS_FALSE;
                 *va_arg(ap, const jschar **) = stable->chars().get();
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -133,17 +133,18 @@ js::StringIsArrayIndex(JSLinearString *s
 }
 
 bool
 DoubleIndexToId(JSContext *cx, double index, MutableHandleId id)
 {
     if (index == uint32_t(index))
         return IndexToId(cx, uint32_t(index), id);
 
-    return ValueToId<CanGC>(cx, DoubleValue(index), id);
+    Value tmp = DoubleValue(index);
+    return ValueToId<CanGC>(cx, HandleValue::fromMarkedLocation(&tmp), id);
 }
 
 /*
  * 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.
  */
@@ -345,17 +346,18 @@ DeletePropertyOrThrow(JSContext *cx, Han
 {
     JSBool succeeded;
     if (!DeleteArrayElement(cx, obj, index, &succeeded))
         return false;
     if (succeeded)
         return true;
 
     RootedId id(cx);
-    if (!ValueToId<CanGC>(cx, NumberValue(index), &id))
+    RootedValue indexv(cx, NumberValue(index));
+    if (!ValueToId<CanGC>(cx, indexv, &id))
         return false;
     return obj->reportNotConfigurable(cx, id, JSREPORT_ERROR);
 }
 
 JSBool
 js::SetLengthProperty(JSContext *cx, HandleObject obj, double length)
 {
     RootedValue v(cx, NumberValue(length));
@@ -887,17 +889,17 @@ array_join_sub(JSContext *cx, CallArgs &
     // Steps 2 and 3
     uint32_t length;
     if (!GetLengthProperty(cx, obj, &length))
         return false;
 
     // Steps 4 and 5
     RootedString sepstr(cx, NULL);
     if (!locale && args.hasDefined(0)) {
-        sepstr = ToString<CanGC>(cx, args[0]);
+        sepstr = ToString<CanGC>(cx, args.handleAt(0));
         if (!sepstr)
             return false;
     }
     static const jschar comma = ',';
     const jschar *sep;
     size_t seplen;
     if (sepstr) {
         sep = NULL;
@@ -1128,21 +1130,24 @@ InitArrayElements(JSContext *cx, HandleO
     }
 
     if (vector == end)
         return true;
 
     JS_ASSERT(start == MAX_ARRAY_INDEX + 1);
     RootedValue value(cx);
     RootedId id(cx);
+    RootedValue indexv(cx);
     double index = MAX_ARRAY_INDEX + 1;
     do {
         value = *vector++;
-        if (!ValueToId<CanGC>(cx, DoubleValue(index), &id) ||
-            !JSObject::setGeneric(cx, obj, obj, id, &value, true)) {
+        indexv = DoubleValue(index);
+        if (!ValueToId<CanGC>(cx, indexv, &id) ||
+            !JSObject::setGeneric(cx, obj, obj, id, &value, true))
+        {
             return false;
         }
         index += 1;
     } while (vector != end);
 
     return true;
 }
 
--- a/js/src/jsatom.h
+++ b/js/src/jsatom.h
@@ -230,17 +230,17 @@ AtomizeChars(JSContext *cx, const jschar
              js::InternBehavior ib = js::DoNotInternAtom);
 
 template <AllowGC allowGC>
 extern JSAtom *
 AtomizeString(JSContext *cx, JSString *str, js::InternBehavior ib = js::DoNotInternAtom);
 
 template <AllowGC allowGC>
 inline JSAtom *
-ToAtom(JSContext *cx, const js::Value &v);
+ToAtom(JSContext *cx, typename MaybeRooted<Value, allowGC>::HandleType v);
 
 template<XDRMode mode>
 bool
 XDRAtom(XDRState<mode> *xdr, js::MutableHandleAtom atomp);
 
 } /* namespace js */
 
 #endif /* jsatom_h___ */
--- a/js/src/jsatominlines.h
+++ b/js/src/jsatominlines.h
@@ -38,17 +38,17 @@ AtomToId(JSAtom *atom)
     if (atom->isIndex(&index) && index <= JSID_INT_MAX)
         return INT_TO_JSID(int32_t(index));
 
     return JSID_FROM_BITS(size_t(atom));
 }
 
 template <AllowGC allowGC>
 inline JSAtom *
-ToAtom(JSContext *cx, const js::Value &v)
+ToAtom(JSContext *cx, typename MaybeRooted<Value, allowGC>::HandleType v)
 {
     if (!v.isString()) {
         JSString *str = js::ToStringSlow<allowGC>(cx, v);
         if (!str)
             return NULL;
         JS::Anchor<JSString *> anchor(str);
         return AtomizeString<allowGC>(cx, str);
     }
@@ -58,17 +58,18 @@ ToAtom(JSContext *cx, const js::Value &v
         return &str->asAtom();
 
     JS::Anchor<JSString *> anchor(str);
     return AtomizeString<allowGC>(cx, str);
 }
 
 template <AllowGC allowGC>
 inline bool
-ValueToId(JSContext* cx, const Value &v, typename MaybeRooted<jsid, allowGC>::MutableHandleType idp)
+ValueToId(JSContext* cx, typename MaybeRooted<Value, allowGC>::HandleType v,
+          typename MaybeRooted<jsid, allowGC>::MutableHandleType idp)
 {
     int32_t i;
     if (ValueFitsInInt32(v, &i) && INT_FITS_IN_JSID(i)) {
         idp.set(INT_TO_JSID(i));
         return true;
     }
 
     JSAtom *atom = ToAtom<allowGC>(cx, v);
@@ -141,17 +142,18 @@ static JS_ALWAYS_INLINE JSFlatString *
 IdToString(JSContext *cx, jsid id)
 {
     if (JSID_IS_STRING(id))
         return JSID_TO_ATOM(id);
 
     if (JS_LIKELY(JSID_IS_INT(id)))
         return Int32ToString<CanGC>(cx, JSID_TO_INT(id));
 
-    JSString *str = ToStringSlow<CanGC>(cx, IdToValue(id));
+    RootedValue idv(cx, IdToValue(id));
+    JSString *str = ToStringSlow<CanGC>(cx, idv);
     if (!str)
         return NULL;
 
     return str->ensureFlat(cx);
 }
 
 inline
 AtomHasher::Lookup::Lookup(const JSAtom *atom)
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -442,18 +442,20 @@ CallJSDeletePropertyOp(JSContext *cx, JS
     assertSameCompartment(cx, receiver, id);
     return op(cx, receiver, id, succeeded);
 }
 
 inline bool
 CallSetter(JSContext *cx, HandleObject obj, HandleId id, StrictPropertyOp op, unsigned attrs,
            unsigned shortid, JSBool strict, MutableHandleValue vp)
 {
-    if (attrs & JSPROP_SETTER)
-        return InvokeGetterOrSetter(cx, obj, CastAsObjectJsval(op), 1, vp.address(), vp.address());
+    if (attrs & JSPROP_SETTER) {
+        RootedValue opv(cx, CastAsObjectJsval(op));
+        return InvokeGetterOrSetter(cx, obj, opv, 1, vp.address(), vp.address());
+    }
 
     if (attrs & JSPROP_GETTER)
         return js_ReportGetterOnlyAssignment(cx);
 
     if (!(attrs & JSPROP_SHORTID))
         return CallJSPropertyOpSetter(cx, op, obj, id, strict, vp);
 
     RootedId nid(cx, INT_TO_JSID(shortid));
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -372,17 +372,17 @@ JSCompartment::wrapId(JSContext *cx, jsi
 {
     MOZ_ASSERT(*idp != JSID_VOID, "JSID_VOID is an out-of-band sentinel value");
     if (JSID_IS_INT(*idp))
         return true;
     RootedValue value(cx, IdToValue(*idp));
     if (!wrap(cx, &value))
         return false;
     RootedId id(cx);
-    if (!ValueToId<CanGC>(cx, value.get(), &id))
+    if (!ValueToId<CanGC>(cx, value, &id))
         return false;
 
     *idp = id;
     return true;
 }
 
 bool
 JSCompartment::wrap(JSContext *cx, PropertyOp *propp)
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -1205,17 +1205,17 @@ static JSBool
 date_parse(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() == 0) {
         vp->setDouble(js_NaN);
         return true;
     }
 
-    JSString *str = ToString<CanGC>(cx, args[0]);
+    JSString *str = ToString<CanGC>(cx, args.handleAt(0));
     if (!str)
         return false;
 
     JSLinearString *linearStr = str->ensureLinear(cx);
     if (!linearStr)
         return false;
 
     double result;
@@ -2891,17 +2891,17 @@ date_toLocaleFormat_impl(JSContext *cx, 
 #if defined(_WIN32) && !defined(__MWERKS__)
                               "%#c"
 #else
                               "%c"
 #endif
                              , args.rval());
     }
 
-    RootedString fmt(cx, ToString<CanGC>(cx, args[0]));
+    RootedString fmt(cx, ToString<CanGC>(cx, args.handleAt(0)));
     if (!fmt)
         return false;
 
     JSAutoByteString fmtbytes(cx, fmt);
     if (!fmtbytes)
         return false;
 
     return ToLocaleFormatHelper(cx, thisObj, fmtbytes.ptr(), args.rval());
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -306,17 +306,18 @@ JS_SetWatchPoint(JSContext *cx, JSObject
     RootedId 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 (!ValueToId<CanGC>(cx, IdToValue(id), &propid))
+        RootedValue val(cx, IdToValue(id));
+        if (!ValueToId<CanGC>(cx, val, &propid))
             return false;
     }
 
     /*
      * 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))
@@ -1017,18 +1018,19 @@ class AutoPropertyDescArray
     }
 
     JSPropertyDescArray * operator ->() {
         return &descArray_;
     }
 };
 
 static const char *
-FormatValue(JSContext *cx, const Value &v, JSAutoByteString &bytes)
+FormatValue(JSContext *cx, const Value &vArg, JSAutoByteString &bytes)
 {
+    RootedValue v(cx, vArg);
     JSString *str = ToString<CanGC>(cx, v);
     if (!str)
         return NULL;
     const char *buf = bytes.encodeLatin1(cx, str);
     if (!buf)
         return NULL;
     const char *found = strstr(buf, "function ");
     if (found && (found - buf <= 2))
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -554,32 +554,32 @@ Exception(JSContext *cx, unsigned argc, 
 
     RootedObject obj(cx, NewObjectWithGivenProto(cx, &ErrorClass, &protov.toObject(), NULL));
     if (!obj)
         return false;
 
     /* Set the 'message' property. */
     RootedString message(cx);
     if (args.hasDefined(0)) {
-        message = ToString<CanGC>(cx, args[0]);
+        message = ToString<CanGC>(cx, args.handleAt(0));
         if (!message)
             return false;
         args[0].setString(message);
     } else {
         message = NULL;
     }
 
     /* Find the scripted caller. */
     NonBuiltinScriptFrameIter iter(cx);
 
     /* Set the 'fileName' property. */
     RootedScript script(cx, iter.done() ? NULL : iter.script());
     RootedString filename(cx);
     if (args.length() > 1) {
-        filename = ToString<CanGC>(cx, args[1]);
+        filename = ToString<CanGC>(cx, args.handleAt(1));
         if (!filename)
             return false;
         args[1].setString(filename);
     } else {
         filename = cx->runtime->emptyString;
         if (!iter.done()) {
             if (const char *cfilename = script->filename()) {
                 filename = FilenameToString(cx, cfilename);
@@ -1078,17 +1078,17 @@ js_ReportUncaughtException(JSContext *cx
                 return false;
         } else if (name) {
             str = name;
         } else if (msg) {
             str = msg;
         }
 
         if (JS_GetProperty(cx, exnObject, filename_str, &roots[4])) {
-            JSString *tmp = ToString<CanGC>(cx, roots[4]);
+            JSString *tmp = ToString<CanGC>(cx, HandleValue::fromMarkedLocation(&roots[4]));
             if (tmp)
                 filename.encodeLatin1(cx, tmp);
         }
 
         uint32_t lineno;
         if (!JS_GetProperty(cx, exnObject, js_lineNumber_str, &roots[5]) ||
             !ToUint32(cx, roots[5], &lineno))
         {
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1340,17 +1340,17 @@ js::Function(JSContext *cx, unsigned arg
          * comments and linefeeds.  XXX It might be better to concatenate
          * everything up into a function definition and pass it to the
          * compiler, but doing it this way is less of a delta from the old
          * code.  See ECMA 15.3.2.1.
          */
         size_t args_length = 0;
         for (unsigned i = 0; i < n; i++) {
             /* Collect the lengths for all the function-argument arguments. */
-            arg = ToString<CanGC>(cx, args[i]);
+            arg = ToString<CanGC>(cx, args.handleAt(i));
             if (!arg)
                 return false;
             args[i].setString(arg);
 
             /*
              * Check for overflow.  The < test works because the maximum
              * JSString length fits in 2 fewer bits than size_t has.
              */
@@ -1460,17 +1460,17 @@ js::Function(JSContext *cx, unsigned arg
         JS_ASSERT(str->asAtom().asPropertyName() == formals[i]);
     }
 #endif
 
     RootedString str(cx);
     if (!args.length())
         str = cx->runtime->emptyString;
     else
-        str = ToString<CanGC>(cx, args[args.length() - 1]);
+        str = ToString<CanGC>(cx, args.handleAt(args.length() - 1));
     if (!str)
         return false;
     JSLinearString *linear = str->ensureLinear(cx);
     if (!linear)
         return false;
 
     JS::Anchor<JSString *> strAnchor(str);
     const jschar *chars = linear->chars();
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -1087,17 +1087,18 @@ SuppressDeletedPropertyHelper(JSContext 
                      */
                     RootedObject proto(cx);
                     if (!JSObject::getProto(cx, obj, &proto))
                         return false;
                     if (proto) {
                         RootedObject obj2(cx);
                         RootedShape prop(cx);
                         RootedId id(cx);
-                        if (!ValueToId<CanGC>(cx, StringValue(*idp), &id))
+                        RootedValue idv(cx, StringValue(*idp));
+                        if (!ValueToId<CanGC>(cx, idv, &id))
                             return false;
                         if (!JSObject::lookupGeneric(cx, proto, id, &obj2, &prop))
                             return false;
                         if (prop) {
                             unsigned attrs;
                             if (obj2->isNative())
                                 attrs = GetShapeAttributes(prop);
                             else if (!JSObject::getGenericAttributes(cx, obj2, id, &attrs))
@@ -1218,17 +1219,18 @@ js_IteratorMore(JSContext *cx, HandleObj
 
     /* We're reentering below and can call anything. */
     JS_CHECK_RECURSION(cx, return false);
 
     /* Fetch and cache the next value from the iterator. */
     if (ni) {
         JS_ASSERT(!ni->isKeyIter());
         RootedId id(cx);
-        if (!ValueToId<CanGC>(cx, StringValue(*ni->current()), &id))
+        RootedValue current(cx, StringValue(*ni->current()));
+        if (!ValueToId<CanGC>(cx, current, &id))
             return false;
         ni->incCursor();
         RootedObject obj(cx, ni->obj);
         if (!JSObject::getGeneric(cx, obj, obj, id, rval))
             return false;
         if ((ni->flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, rval, rval))
             return false;
     } else {
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -262,38 +262,38 @@ num_isFinite(JSContext *cx, unsigned arg
         return JS_FALSE;
     vp->setBoolean(mozilla::IsFinite(x));
     return JS_TRUE;
 }
 
 static JSBool
 num_parseFloat(JSContext *cx, unsigned argc, Value *vp)
 {
-    JSString *str;
-    double d;
-    const jschar *bp, *end, *ep;
+    CallArgs args = CallArgsFromVp(argc, vp);
 
-    if (argc == 0) {
-        vp->setDouble(js_NaN);
+    if (args.length() == 0) {
+        args.rval().setDouble(js_NaN);
         return JS_TRUE;
     }
-    str = ToString<CanGC>(cx, vp[2]);
+    JSString *str = ToString<CanGC>(cx, args.handleAt(0));
     if (!str)
         return JS_FALSE;
-    bp = str->getChars(cx);
+    const jschar *bp = str->getChars(cx);
     if (!bp)
         return JS_FALSE;
-    end = bp + str->length();
+    const jschar *end = bp + str->length();
+    const jschar *ep;
+    double d;
     if (!js_strtod(cx, bp, end, &ep, &d))
         return JS_FALSE;
     if (ep == bp) {
-        vp->setDouble(js_NaN);
+        args.rval().setDouble(js_NaN);
         return JS_TRUE;
     }
-    vp->setNumber(d);
+    args.rval().setDouble(d);
     return JS_TRUE;
 }
 
 /* ES5 15.1.2.2. */
 JSBool
 js::num_parseInt(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -335,17 +335,17 @@ js::num_parseInt(JSContext *cx, unsigned
             if (d == 0.0) {
                 args.rval().setInt32(0);
                 return true;
             }
         }
     }
 
     /* Step 1. */
-    RootedString inputString(cx, ToString<CanGC>(cx, args[0]));
+    RootedString inputString(cx, ToString<CanGC>(cx, args.handleAt(0)));
     if (!inputString)
         return false;
     args[0].setString(inputString);
 
     /* Steps 6-9. */
     bool stripPrefix = true;
     int32_t radix;
     if (!args.hasDefined(1)) {
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -629,17 +629,17 @@ js_Stringify(JSContext *cx, MutableHandl
     if (space.isObject()) {
         RootedObject spaceObj(cx, &space.toObject());
         if (ObjectClassIs(spaceObj, ESClass_Number, cx)) {
             double d;
             if (!ToNumber(cx, space, &d))
                 return false;
             space = NumberValue(d);
         } else if (ObjectClassIs(spaceObj, ESClass_String, cx)) {
-            JSString *str = ToStringSlow<CanGC>(cx, space);
+            JSString *str = ToStringSlow<CanGC>(cx, spaceRoot);
             if (!str)
                 return false;
             space = StringValue(str);
         }
     }
 
     StringBuffer gap(cx);
 
@@ -829,17 +829,19 @@ json_toSource(JSContext *cx, unsigned ar
 
 /* ES5 15.12.2. */
 JSBool
 js_json_parse(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* Step 1. */
-    JSString *str = (argc >= 1) ? ToString<CanGC>(cx, args[0]) : cx->names().undefined;
+    JSString *str = (args.length() >= 1)
+                    ? ToString<CanGC>(cx, args.handleAt(0))
+                    : cx->names().undefined;
     if (!str)
         return false;
 
     JSStableString *stable = str->ensureStable(cx);
     if (!stable)
         return false;
 
     JS::Anchor<JSString *> anchor(stable);
--- a/js/src/jspropertytree.cpp
+++ b/js/src/jspropertytree.cpp
@@ -262,17 +262,18 @@ Shape::dump(JSContext *cx, FILE *fp) con
     if (JSID_IS_INT(propid)) {
         fprintf(fp, "[%ld]", (long) JSID_TO_INT(propid));
     } else {
         JSLinearString *str;
         if (JSID_IS_ATOM(propid)) {
             str = JSID_TO_ATOM(propid);
         } else {
             JS_ASSERT(JSID_IS_OBJECT(propid));
-            JSString *s = ToStringSlow<CanGC>(cx, IdToValue(propid));
+            RootedValue v(cx, IdToValue(propid));
+            JSString *s = ToStringSlow<CanGC>(cx, v);
             fputs("object ", fp);
             str = s ? s->ensureLinear(cx) : NULL;
         }
         if (!str)
             fputs("<error>", fp);
         else
             FileEscapedString(fp, str, '"');
     }
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -670,29 +670,31 @@ Trap(JSContext *cx, HandleObject handler
      MutableHandleValue rval)
 {
     return Invoke(cx, ObjectValue(*handler), fval, argc, argv, rval.address());
 }
 
 static bool
 Trap1(JSContext *cx, HandleObject handler, HandleValue fval, HandleId id, MutableHandleValue rval)
 {
-    JSString *str = ToString<CanGC>(cx, IdToValue(id));
+    rval.set(IdToValue(id)); // Re-use out-param to avoid Rooted overhead.
+    JSString *str = ToString<CanGC>(cx, rval);
     if (!str)
         return false;
     rval.setString(str);
     return Trap(cx, handler, fval, 1, rval.address(), rval);
 }
 
 static bool
 Trap2(JSContext *cx, HandleObject handler, HandleValue fval, HandleId id, Value v_,
       MutableHandleValue rval)
 {
     RootedValue v(cx, v_);
-    JSString *str = ToString<CanGC>(cx, IdToValue(id));
+    rval.set(IdToValue(id)); // Re-use out-param to avoid Rooted overhead.
+    JSString *str = ToString<CanGC>(cx, rval);
     if (!str)
         return false;
     rval.setString(str);
     Value argv[2] = { rval.get(), v };
     AutoValueArray ava(cx, argv, 2);
     return Trap(cx, handler, fval, 2, argv, rval);
 }
 
@@ -943,17 +945,18 @@ ScriptedIndirectProxyHandler::hasOwn(JSC
            ValueToBool(value, bp);
 }
 
 bool
 ScriptedIndirectProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver,
                                   HandleId id, MutableHandleValue vp)
 {
     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
-    JSString *str = ToString<CanGC>(cx, IdToValue(id));
+    RootedValue idv(cx, IdToValue(id));
+    JSString *str = ToString<CanGC>(cx, idv);
     if (!str)
         return false;
     RootedValue value(cx, StringValue(str));
     Value argv[] = { ObjectOrNullValue(receiver), value };
     AutoValueArray ava(cx, argv, 2);
     RootedValue fval(cx);
     if (!GetDerivedTrap(cx, handler, cx->names().get, &fval))
         return false;
@@ -962,17 +965,18 @@ ScriptedIndirectProxyHandler::get(JSCont
     return Trap(cx, handler, fval, 2, argv, vp);
 }
 
 bool
 ScriptedIndirectProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver,
                                   HandleId id, bool strict, MutableHandleValue vp)
 {
     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
-    JSString *str = ToString<CanGC>(cx, IdToValue(id));
+    RootedValue idv(cx, IdToValue(id));
+    JSString *str = ToString<CanGC>(cx, idv);
     if (!str)
         return false;
     RootedValue value(cx, StringValue(str));
     Value argv[] = { ObjectOrNullValue(receiver), value, vp.get() };
     AutoValueArray ava(cx, argv, 3);
     RootedValue fval(cx);
     if (!GetDerivedTrap(cx, handler, cx->names().set, &fval))
         return false;
@@ -1333,17 +1337,18 @@ HasOwn(JSContext *cx, HandleObject obj, 
         return false;
     *bp = (desc.obj == obj);
     return true;
 }
 
 static bool
 IdToValue(JSContext *cx, HandleId id, MutableHandleValue value)
 {
-    JSString *name = ToString<CanGC>(cx, IdToValue(id));
+    value.set(IdToValue(id)); // Re-use out-param to avoid Rooted overhead.
+    JSString *name = ToString<CanGC>(cx, value);
     if (!name)
         return false;
     value.set(StringValue(name));
     return true;
 }
 
 // TrapGetOwnProperty(O, P)
 static bool
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -2966,33 +2966,35 @@ ASTSerializer::moduleOrFunctionBody(Pars
     }
 
     return builder.blockStatement(elts, pos, dst);
 }
 
 static JSBool
 reflect_parse(JSContext *cx, uint32_t argc, jsval *vp)
 {
-    if (argc < 1) {
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (args.length() < 1) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
                              "Reflect.parse", "0", "s");
         return JS_FALSE;
     }
 
-    RootedString src(cx, ToString<CanGC>(cx, JS_ARGV(cx, vp)[0]));
+    RootedString src(cx, ToString<CanGC>(cx, args.handleAt(0)));
     if (!src)
         return JS_FALSE;
 
     ScopedJSFreePtr<char> filename;
     uint32_t lineno = 1;
     bool loc = true;
 
     RootedObject builder(cx);
 
-    RootedValue arg(cx, argc > 1 ? JS_ARGV(cx, vp)[1] : UndefinedValue());
+    RootedValue arg(cx, args.get(1));
 
     if (!arg.isNullOrUndefined()) {
         if (!arg.isObject()) {
             js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
                                      JSDVG_SEARCH_STACK, arg, NullPtr(), "not an object", NULL);
             return JS_FALSE;
         }
 
@@ -3076,21 +3078,21 @@ reflect_parse(JSContext *cx, uint32_t ar
     serialize.setParser(&parser);
 
     ParseNode *pn = parser.parse(NULL);
     if (!pn)
         return JS_FALSE;
 
     RootedValue val(cx);
     if (!serialize.program(pn, &val)) {
-        JS_SET_RVAL(cx, vp, JSVAL_NULL);
+        args.rval().setNull();
         return JS_FALSE;
     }
 
-    JS_SET_RVAL(cx, vp, val);
+    args.rval().set(val);
     return JS_TRUE;
 }
 
 static const JSFunctionSpec static_methods[] = {
     JS_FN("parse", reflect_parse, 1, 0),
     JS_FS_END
 };
 
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -71,22 +71,21 @@ using mozilla::PodEqual;
 typedef Handle<JSLinearString*> HandleLinearString;
 
 static JSLinearString *
 ArgToRootedString(JSContext *cx, CallArgs &args, unsigned argno)
 {
     if (argno >= args.length())
         return cx->names().undefined;
 
-    Value &arg = args[argno];
-    JSString *str = ToString<CanGC>(cx, arg);
+    JSString *str = ToString<CanGC>(cx, args.handleAt(argno));
     if (!str)
         return NULL;
 
-    arg = StringValue(str);
+    args[argno] = StringValue(str);
     return str->ensureLinear(cx);
 }
 
 /*
  * Forward declarations for URI encode/decode and helper routines
  */
 static JSBool
 str_decodeURI(JSContext *cx, unsigned argc, Value *vp);
@@ -821,18 +820,17 @@ str_toLocaleUpperCase(JSContext *cx, uns
 static JSBool
 str_localeCompare(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedString str(cx, ThisToStringForStringProto(cx, args));
     if (!str)
         return false;
 
-    Value thatValue = args.length() > 0 ? args[0] : UndefinedValue();
-    RootedString thatStr(cx, ToString<CanGC>(cx, thatValue));
+    RootedString thatStr(cx, ToString<CanGC>(cx, args.handleOrUndefinedAt(0)));
     if (!thatStr)
         return false;
 
     if (cx->runtime->localeCallbacks && cx->runtime->localeCallbacks->localeCompare) {
         RootedValue result(cx);
         if (!cx->runtime->localeCallbacks->localeCompare(cx, str, thatStr, &result))
             return false;
 
@@ -1715,17 +1713,17 @@ class StringRegExpGuard
     bool normalizeRegExp(JSContext *cx, bool flat, unsigned optarg, CallArgs args)
     {
         if (re_.initialized())
             return true;
 
         /* Build RegExp from pattern string. */
         RootedString opt(cx);
         if (optarg < args.length()) {
-            opt = ToString<CanGC>(cx, args[optarg]);
+            opt = ToString<CanGC>(cx, args.handleAt(optarg));
             if (!opt)
                 return false;
         } else {
             opt = NULL;
         }
 
         Rooted<JSAtom *> patstr(cx);
         if (flat) {
@@ -3176,20 +3174,20 @@ static JSBool
 str_concat(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JSString *str = ThisToStringForStringProto(cx, args);
     if (!str)
         return false;
 
     for (unsigned i = 0; i < args.length(); i++) {
-        JSString *argStr = ToString<NoGC>(cx, args[i]);
+        JSString *argStr = ToString<NoGC>(cx, args.handleAt(i));
         if (!argStr) {
             RootedString strRoot(cx, str);
-            argStr = ToString<CanGC>(cx, args[i]);
+            argStr = ToString<CanGC>(cx, args.handleAt(i));
             if (!argStr)
                 return false;
             str = strRoot;
         }
 
         JSString *next = ConcatStrings<NoGC>(cx, str, argStr);
         if (next) {
             str = next;
@@ -3503,17 +3501,17 @@ static const JSFunctionSpec string_metho
 
 JSBool
 js_String(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     RootedString str(cx);
     if (args.length() > 0) {
-        str = ToString<CanGC>(cx, args[0]);
+        str = ToString<CanGC>(cx, args.handleAt(0));
         if (!str)
             return false;
     } else {
         str = cx->runtime->emptyString;
     }
 
     if (IsConstructing(args)) {
         StringObject *strobj = StringObject::create(cx, str);
@@ -3739,34 +3737,35 @@ js_NewStringCopyZ(JSContext *cx, const c
 
 template JSFlatString *
 js_NewStringCopyZ<CanGC>(JSContext *cx, const char *s);
 
 template JSFlatString *
 js_NewStringCopyZ<NoGC>(JSContext *cx, const char *s);
 
 const char *
-js_ValueToPrintable(JSContext *cx, const Value &v, JSAutoByteString *bytes, bool asSource)
+js_ValueToPrintable(JSContext *cx, const Value &vArg, JSAutoByteString *bytes, bool asSource)
 {
+    RootedValue v(cx, vArg);
     JSString *str;
     if (asSource)
         str = ValueToSource(cx, v);
     else
         str = ToString<CanGC>(cx, v);
     if (!str)
         return NULL;
     str = js_QuoteString(cx, str, 0);
     if (!str)
         return NULL;
     return bytes->encodeLatin1(cx, str);
 }
 
 template <AllowGC allowGC>
 JSString *
-js::ToStringSlow(JSContext *cx, const Value &arg)
+js::ToStringSlow(JSContext *cx, typename MaybeRooted<Value, allowGC>::HandleType arg)
 {
     /* As with ToObjectSlow, callers must verify that |arg| isn't a string. */
     JS_ASSERT(!arg.isString());
 
     Value v = arg;
     if (!v.isPrimitive()) {
         if (!allowGC)
             return NULL;
@@ -3789,20 +3788,20 @@ js::ToStringSlow(JSContext *cx, const Va
         str = cx->names().null;
     } else {
         str = cx->names().undefined;
     }
     return str;
 }
 
 template JSString *
-js::ToStringSlow<CanGC>(JSContext *cx, const Value &arg);
+js::ToStringSlow<CanGC>(JSContext *cx, HandleValue arg);
 
 template JSString *
-js::ToStringSlow<NoGC>(JSContext *cx, const Value &arg);
+js::ToStringSlow<NoGC>(JSContext *cx, Value arg);
 
 JSString *
 js::ValueToSource(JSContext *cx, const Value &v)
 {
     JS_CHECK_RECURSION(cx, return NULL);
     assertSameCompartment(cx, v);
 
     if (v.isUndefined())
@@ -3812,17 +3811,18 @@ js::ValueToSource(JSContext *cx, const V
     if (v.isPrimitive()) {
         /* Special case to preserve negative zero, _contra_ toString. */
         if (v.isDouble() && IsNegativeZero(v.toDouble())) {
             /* NB: _ucNstr rather than _ucstr to indicate non-terminated. */
             static const jschar js_negzero_ucNstr[] = {'-', '0'};
 
             return js_NewStringCopyN<CanGC>(cx, js_negzero_ucNstr, 2);
         }
-        return ToString<CanGC>(cx, v);
+        RootedValue vRoot(cx, v);
+        return ToString<CanGC>(cx, vRoot);
     }
 
     RootedValue rval(cx, NullValue());
     RootedValue fval(cx);
     RootedObject obj(cx, &v.toObject());
     if (!JSObject::getProperty(cx, obj, obj, cx->names().toSource, &fval))
         return NULL;
     if (js_IsCallable(fval)) {
--- a/js/src/jsstr.h
+++ b/js/src/jsstr.h
@@ -121,26 +121,26 @@ js_ValueToPrintable(JSContext *cx, const
 namespace js {
 
 /*
  * Convert a non-string value to a string, returning null after reporting an
  * error, otherwise returning a new string reference.
  */
 template <AllowGC allowGC>
 extern JSString *
-ToStringSlow(JSContext *cx, const Value &v);
+ToStringSlow(JSContext *cx, typename MaybeRooted<Value, allowGC>::HandleType arg);
 
 /*
  * Convert the given value to a string.  This method includes an inline
  * fast-path for the case where the value is already a string; if the value is
  * known not to be a string, use ToStringSlow instead.
  */
 template <AllowGC allowGC>
 static JS_ALWAYS_INLINE JSString *
-ToString(JSContext *cx, const js::Value &v)
+ToString(JSContext *cx, JS::HandleValue v)
 {
 #ifdef DEBUG
     if (allowGC) {
         SkipRoot skip(cx, &v);
         MaybeCheckStackRoots(cx);
     }
 #endif
 
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -384,17 +384,18 @@ Reify(JSContext *cx, JSCompartment *orig
     size_t length = ni->numKeys();
     bool isKeyIter = ni->isKeyIter();
     AutoIdVector keys(cx);
     if (length > 0) {
         if (!keys.reserve(length))
             return false;
         for (size_t i = 0; i < length; ++i) {
             RootedId id(cx);
-            if (!ValueToId<CanGC>(cx, StringValue(ni->begin()[i]), &id))
+            RootedValue v(cx, StringValue(ni->begin()[i]));
+            if (!ValueToId<CanGC>(cx, v, &id))
                 return false;
             keys.infallibleAppend(id);
             if (!origin->wrapId(cx, &keys[i]))
                 return false;
         }
     }
 
     close.clear();
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3562,17 +3562,17 @@ GetSelfHostedValue(JSContext *cx, unsign
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (argc != 1 || !args[0].isString()) {
         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS,
                              "getSelfHostedValue");
         return false;
     }
-    RootedAtom srcAtom(cx, ToAtom<CanGC>(cx, args[0]));
+    RootedAtom srcAtom(cx, ToAtom<CanGC>(cx, args.handleAt(0)));
     if (!srcAtom)
         return false;
     RootedPropertyName srcName(cx, srcAtom->asPropertyName());
     return cx->runtime->cloneSelfHostedValue(cx, srcName, args.rval());
 }
 
 static JSFunctionSpecWithHelp shell_functions[] = {
     JS_FN_HELP("version", Version, 0, 0,
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -107,17 +107,17 @@ ReportMoreArgsNeeded(JSContext *cx, cons
 bool
 ReportObjectRequired(JSContext *cx)
 {
     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
     return false;
 }
 
 bool
-ValueToIdentifier(JSContext *cx, const Value &v, MutableHandleId id)
+ValueToIdentifier(JSContext *cx, HandleValue v, MutableHandleId id)
 {
     if (!ValueToId<CanGC>(cx, v, id))
         return false;
     if (!JSID_IS_ATOM(id) || !IsIdentifier(JSID_TO_ATOM(id))) {
         RootedValue val(cx, v);
         js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
                                  JSDVG_SEARCH_STACK, val, NullPtr(), "not an identifier", NULL);
         return false;
@@ -3242,19 +3242,20 @@ DebuggerScript_getAllOffsets(JSContext *
             } else {
                 JS_ASSERT(offsetsv.isUndefined());
 
                 /*
                  * Create an empty offsets array for this line.
                  * Store it in the result array.
                  */
                 RootedId id(cx);
+                RootedValue v(cx, NumberValue(lineno));
                 offsets = NewDenseEmptyArray(cx);
                 if (!offsets ||
-                    !ValueToId<CanGC>(cx, NumberValue(lineno), &id))
+                    !ValueToId<CanGC>(cx, v, &id))
                 {
                     return false;
                 }
 
                 RootedValue value(cx, ObjectValue(*offsets));
                 if (!JSObject::defineGeneric(cx, result, id, value))
                     return false;
             }
@@ -4564,17 +4565,17 @@ DebuggerObject_getGlobal(JSContext *cx, 
 }
 
 static JSBool
 DebuggerObject_getOwnPropertyDescriptor(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "getOwnPropertyDescriptor", args, dbg, obj);
 
     RootedId id(cx);
-    if (!ValueToId<CanGC>(cx, argc >= 1 ? args[0] : UndefinedValue(), &id))
+    if (!ValueToId<CanGC>(cx, args.handleOrUndefinedAt(0), &id))
         return false;
 
     /* Bug: This can cause the debuggee to run! */
     AutoPropertyDescriptorRooter desc(cx);
     {
         Maybe<AutoCompartment> ac;
         ac.construct(cx, obj);
         if (!cx->compartment->wrapId(cx, id.address()))
@@ -4654,17 +4655,17 @@ DebuggerObject_getOwnPropertyNames(JSCon
 
 static JSBool
 DebuggerObject_defineProperty(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "defineProperty", args, dbg, obj);
     REQUIRE_ARGC("Debugger.Object.defineProperty", 2);
 
     RootedId id(cx);
-    if (!ValueToId<CanGC>(cx, args[0], &id))
+    if (!ValueToId<CanGC>(cx, args.handleAt(0), &id))
         return false;
 
     const Value &descval = args[1];
     AutoPropDescArrayRooter descs(cx);
     if (!descs.reserve(3)) // desc, unwrappedDesc, rewrappedDesc
         return false;
     PropDesc *desc = descs.append();
     if (!desc || !desc->initialize(cx, descval, false))
@@ -5297,17 +5298,17 @@ DebuggerEnv_names(JSContext *cx, unsigne
 
 static JSBool
 DebuggerEnv_find(JSContext *cx, unsigned argc, Value *vp)
 {
     REQUIRE_ARGC("Debugger.Environment.find", 1);
     THIS_DEBUGENV_OWNER(cx, argc, vp, "find", args, envobj, env, dbg);
 
     RootedId id(cx);
-    if (!ValueToIdentifier(cx, args[0], &id))
+    if (!ValueToIdentifier(cx, args.handleAt(0), &id))
         return false;
 
     {
         Maybe<AutoCompartment> ac;
         ac.construct(cx, env);
         if (!cx->compartment->wrapId(cx, id.address()))
             return false;
 
@@ -5328,17 +5329,17 @@ DebuggerEnv_find(JSContext *cx, unsigned
 
 static JSBool
 DebuggerEnv_getVariable(JSContext *cx, unsigned argc, Value *vp)
 {
     REQUIRE_ARGC("Debugger.Environment.getVariable", 1);
     THIS_DEBUGENV_OWNER(cx, argc, vp, "getVariable", args, envobj, env, dbg);
 
     RootedId id(cx);
-    if (!ValueToIdentifier(cx, args[0], &id))
+    if (!ValueToIdentifier(cx, args.handleAt(0), &id))
         return false;
 
     RootedValue v(cx);
     {
         Maybe<AutoCompartment> ac;
         ac.construct(cx, env);
         if (!cx->compartment->wrapId(cx, id.address()))
             return false;
@@ -5357,17 +5358,17 @@ DebuggerEnv_getVariable(JSContext *cx, u
 
 static JSBool
 DebuggerEnv_setVariable(JSContext *cx, unsigned argc, Value *vp)
 {
     REQUIRE_ARGC("Debugger.Environment.setVariable", 2);
     THIS_DEBUGENV_OWNER(cx, argc, vp, "setVariable", args, envobj, env, dbg);
 
     RootedId id(cx);
-    if (!ValueToIdentifier(cx, args[0], &id))
+    if (!ValueToIdentifier(cx, args.handleAt(0), &id))
         return false;
 
     RootedValue v(cx, args[1]);
     if (!dbg->unwrapDebuggeeValue(cx, &v))
         return false;
 
     {
         Maybe<AutoCompartment> ac;
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -1512,18 +1512,17 @@ BEGIN_CASE(JSOP_AND)
         len = GET_JUMP_OFFSET(regs.pc);
         DO_NEXT_OP(len);
     }
 }
 END_CASE(JSOP_AND)
 
 #define FETCH_ELEMENT_ID(n, id)                                               \
     JS_BEGIN_MACRO                                                            \
-        const Value &idval_ = regs.sp[n];                                     \
-        if (!ValueToId<CanGC>(cx, idval_, &id))                               \
+        if (!ValueToId<CanGC>(cx, HandleValue::fromMarkedLocation(&regs.sp[n]), &id))\
             goto error;                                                       \
     JS_END_MACRO
 
 #define TRY_BRANCH_AFTER_COND(cond,spdec)                                     \
     JS_BEGIN_MACRO                                                            \
         JS_ASSERT(js_CodeSpec[op].length == 1);                               \
         unsigned diff_ = (unsigned) GET_UINT8(regs.pc) - (unsigned) JSOP_IFEQ;         \
         if (diff_ <= 1) {                                                     \
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -116,17 +116,17 @@ js::intrinsic_ThrowError(JSContext *cx, 
  */
 static JSBool
 intrinsic_AssertionFailed(JSContext *cx, unsigned argc, Value *vp)
 {
 #ifdef DEBUG
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() > 0) {
         // try to dump the informative string
-        JSString *str = ToString<CanGC>(cx, args[0]);
+        JSString *str = ToString<CanGC>(cx, args.handleAt(0));
         if (str) {
             const jschar *chars = str->getChars(cx);
             if (chars) {
                 fprintf(stderr, "Self-hosted JavaScript assertion info: ");
                 JSString::dumpChars(chars, str->length());
                 fputc('\n', stderr);
             }
         }
--- a/js/src/vm/Shape-inl.h
+++ b/js/src/vm/Shape-inl.h
@@ -284,18 +284,20 @@ Shape::getUserId(JSContext *cx, MutableH
 #ifdef DEBUG
     {
         SkipRoot skip(cx, &self);
         MaybeCheckStackRoots(cx);
     }
 #endif
     if (self->hasShortID()) {
         int16_t id = self->shortid();
-        if (id < 0)
-            return ValueToId<CanGC>(cx, Int32Value(id), idp);
+        if (id < 0) {
+            RootedValue v(cx, Int32Value(id));
+            return ValueToId<CanGC>(cx, v, idp);
+        }
         idp.set(INT_TO_JSID(id));
     } else {
         idp.set(self->propid());
     }
     return true;
 }
 
 inline bool