Bug 1169391 - Use a ReservedRooted class for optimized Rooted use in vm/Interpreter.cpp, r=terrence
authorSteve Fink <sfink@mozilla.com>
Sat, 30 May 2015 08:06:35 -0700
changeset 246459 c9c7ec25b894e40523b8c7f78974a944bcc7ab54
parent 246458 495a84cefc2307f1762aa18ed2250c95d3bc9ef6
child 246460 0c7557ef0612adcaf9006bc21d39b5265e00b684
push id28830
push usercbook@mozilla.com
push dateMon, 01 Jun 2015 13:02:44 +0000
treeherdermozilla-central@39c85ec2d644 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs1169391
milestone41.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1169391 - Use a ReservedRooted class for optimized Rooted use in vm/Interpreter.cpp, r=terrence
js/public/RootingAPI.h
js/src/vm/Interpreter.cpp
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -71,17 +71,17 @@
  *   then redundant rooting of multiple copies of the GC thing can be avoided.
  *   Second, if the caller does not pass a rooted value a compile error will be
  *   generated, which is quicker and easier to fix than when relying on a
  *   separate rooting analysis.
  *
  * - MutableHandle<T> is a non-const reference to Rooted<T>. It is used in the
  *   same way as Handle<T> and includes a |set(const T& v)| method to allow
  *   updating the value of the referenced Rooted<T>. A MutableHandle<T> can be
- *   created from a Rooted<T> by using |Rooted<T>::operator&()|.
+ *   created with an implicit cast from a Rooted<T>*.
  *
  * In some cases the small performance overhead of exact rooting (measured to
  * be a few nanoseconds on desktop) is too much. In these cases, try the
  * following:
  *
  * - Move all Rooted<T> above inner loops: this allows you to re-use the root
  *   on each iteration of the loop.
  *
@@ -1180,12 +1180,10 @@ CallTraceCallbackOnNonHeap(T* v, const T
     JS::Heap<T>* asHeapT = reinterpret_cast<JS::Heap<T>*>(v);
     aCallbacks.Trace(asHeapT, aName, aClosure);
 }
 
 } /* namespace gc */
 } /* namespace js */
 
 #undef DELETE_ASSIGNMENT_OPS
-#undef DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS
-#undef DECLARE_NONPOINTER_ACCESSOR_METHODS
 
 #endif  /* js_RootingAPI_h */
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -8,16 +8,17 @@
  * JavaScript bytecode interpreter.
  */
 
 #include "vm/Interpreter-inl.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/FloatingPoint.h"
+#include "mozilla/Maybe.h"
 #include "mozilla/PodOperations.h"
 
 #include <string.h>
 
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsfun.h"
@@ -1650,16 +1651,68 @@ SetObjectElementOperation(JSContext* cx,
         return false;
 
     RootedValue tmp(cx, value);
     ObjectOpResult result;
     return SetProperty(cx, obj, id, tmp, receiver, result) &&
            result.checkStrictErrorOrWarning(cx, obj, id, strict);
 }
 
+/*
+ * As an optimization, the interpreter creates a handful of reserved Rooted<T>
+ * variables at the beginning, thus inserting them into the Rooted list once
+ * upon entry. ReservedRooted "borrows" a reserved Rooted variable and uses it
+ * within a local scope, resetting the value to nullptr (or the appropriate
+ * equivalent for T) at scope end. This avoids inserting/removing the Rooted
+ * from the rooter list, while preventing stale values from being kept alive
+ * unnecessarily.
+ */
+
+template<typename T>
+class ReservedRootedBase {
+};
+
+template<typename T>
+class ReservedRooted : public ReservedRootedBase<T>
+{
+    Rooted<T>* savedRoot;
+
+  public:
+    ReservedRooted(Rooted<T>* root, const T& ptr) : savedRoot(root) {
+        *root = ptr;
+    }
+
+    explicit ReservedRooted(Rooted<T>* root) : savedRoot(root) {
+        *root = js::GCMethods<T>::initial();
+    }
+
+    ~ReservedRooted() {
+        *savedRoot = js::GCMethods<T>::initial();
+    }
+
+    void set(const T& p) const { *savedRoot = p; }
+    operator Handle<T>() { return *savedRoot; }
+    operator Rooted<T>&() { return *savedRoot; }
+    MutableHandle<T> operator&() { return &*savedRoot; }
+
+    DECLARE_NONPOINTER_ACCESSOR_METHODS(savedRoot->get())
+    DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(savedRoot->get())
+    DECLARE_POINTER_CONSTREF_OPS(T)
+    DECLARE_POINTER_ASSIGN_OPS(ReservedRooted, T)
+};
+
+template <>
+class ReservedRootedBase<Value> : public ValueOperations<ReservedRooted<Value>>
+{
+    friend class ValueOperations<ReservedRooted<Value>>;
+    const Value* extract() const {
+        return static_cast<const ReservedRooted<Value>*>(this)->address();
+    }
+};
+
 static MOZ_NEVER_INLINE bool
 Interpret(JSContext* cx, RunState& state)
 {
 /*
  * Define macros for an interpreter loop. Opcode dispatch may be either by a
  * switch statement or by indirect goto (aka a threaded interpreter), depending
  * on compiler support.
  *
@@ -2034,21 +2087,19 @@ CASE(JSOP_DUPAT)
 END_CASE(JSOP_DUPAT)
 
 CASE(JSOP_SETRVAL)
     POP_RETURN_VALUE();
 END_CASE(JSOP_SETRVAL)
 
 CASE(JSOP_ENTERWITH)
 {
-    RootedValue& val = rootValue0;
-    RootedObject& staticWith = rootObject0;
-    val = REGS.sp[-1];
+    ReservedRooted<Value> val(&rootValue0, REGS.sp[-1]);
     REGS.sp--;
-    staticWith = script->getObject(REGS.pc);
+    ReservedRooted<JSObject*> staticWith(&rootObject0, script->getObject(REGS.pc));
 
     if (!EnterWithOperation(cx, REGS.fp(), val, staticWith))
         goto error;
 }
 END_CASE(JSOP_ENTERWITH)
 
 CASE(JSOP_LEAVEWITH)
     REGS.fp()->popWith(cx);
@@ -2166,23 +2217,24 @@ END_CASE(JSOP_AND)
 
 CASE(JSOP_IN)
 {
     HandleValue rref = REGS.stackHandleAt(-1);
     if (!rref.isObject()) {
         ReportValueError(cx, JSMSG_IN_NOT_OBJECT, -1, rref, nullptr);
         goto error;
     }
-    RootedObject& obj = rootObject0;
-    obj = &rref.toObject();
-    RootedId& id = rootId0;
-    FETCH_ELEMENT_ID(-2, id);
     bool found;
-    if (!HasProperty(cx, obj, id, &found))
-        goto error;
+    {
+        ReservedRooted<JSObject*> obj(&rootObject0, &rref.toObject());
+        ReservedRooted<jsid> id(&rootId0);
+        FETCH_ELEMENT_ID(-2, id);
+        if (!HasProperty(cx, obj, id, &found))
+            goto error;
+    }
     TRY_BRANCH_AFTER_COND(found, 2);
     REGS.sp--;
     REGS.sp[-1].setBoolean(found);
 }
 END_CASE(JSOP_IN)
 
 CASE(JSOP_ITER)
 {
@@ -2195,35 +2247,33 @@ CASE(JSOP_ITER)
 }
 END_CASE(JSOP_ITER)
 
 CASE(JSOP_MOREITER)
 {
     MOZ_ASSERT(REGS.stackDepth() >= 1);
     MOZ_ASSERT(REGS.sp[-1].isObject());
     PUSH_NULL();
-    RootedObject& obj = rootObject0;
-    obj = &REGS.sp[-2].toObject();
+    ReservedRooted<JSObject*> obj(&rootObject0, &REGS.sp[-2].toObject());
     if (!IteratorMore(cx, obj, REGS.stackHandleAt(-1)))
         goto error;
 }
 END_CASE(JSOP_MOREITER)
 
 CASE(JSOP_ISNOITER)
 {
     bool b = REGS.sp[-1].isMagic(JS_NO_ITER_VALUE);
     PUSH_BOOLEAN(b);
 }
 END_CASE(JSOP_ISNOITER)
 
 CASE(JSOP_ENDITER)
 {
     MOZ_ASSERT(REGS.stackDepth() >= 1);
-    RootedObject& obj = rootObject0;
-    obj = &REGS.sp[-1].toObject();
+    ReservedRooted<JSObject*> obj(&rootObject0, &REGS.sp[-1].toObject());
     bool ok = CloseIterator(cx, obj);
     REGS.sp--;
     if (!ok)
         goto error;
 }
 END_CASE(JSOP_ENDITER)
 
 CASE(JSOP_DUP)
@@ -2260,47 +2310,39 @@ CASE(JSOP_PICK)
     Value lval = REGS.sp[-int(i + 1)];
     memmove(REGS.sp - (i + 1), REGS.sp - i, sizeof(Value) * i);
     REGS.sp[-1] = lval;
 }
 END_CASE(JSOP_PICK)
 
 CASE(JSOP_SETCONST)
 {
-    RootedPropertyName& name = rootName0;
-    name = script->getName(REGS.pc);
-
-    RootedValue& rval = rootValue0;
-    rval = REGS.sp[-1];
-
-    RootedObject& obj = rootObject0;
-    obj = &REGS.fp()->varObj();
+    ReservedRooted<PropertyName*> name(&rootName0, script->getName(REGS.pc));
+    ReservedRooted<Value> rval(&rootValue0, REGS.sp[-1]);
+    ReservedRooted<JSObject*> obj(&rootObject0, &REGS.fp()->varObj());
 
     if (!SetConstOperation(cx, obj, name, rval))
         goto error;
 }
 END_CASE(JSOP_SETCONST)
 
 CASE(JSOP_BINDINTRINSIC)
     PUSH_OBJECT(*cx->global()->intrinsicsHolder());
 END_CASE(JSOP_BINDINTRINSIC)
 
 CASE(JSOP_BINDGNAME)
 CASE(JSOP_BINDNAME)
 {
     JSOp op = JSOp(*REGS.pc);
     if (op == JSOP_BINDNAME || script->hasPollutedGlobalScope()) {
-        RootedObject& scopeChain = rootObject0;
-        scopeChain = REGS.fp()->scopeChain();
-
-        RootedPropertyName& name = rootName0;
-        name = script->getName(REGS.pc);
+        ReservedRooted<JSObject*> scopeChain(&rootObject0, REGS.fp()->scopeChain());
+        ReservedRooted<PropertyName*> name(&rootName0, script->getName(REGS.pc));
 
         /* Assigning to an undeclared name adds a property to the global object. */
-        RootedObject& scope = rootObject1;
+        ReservedRooted<JSObject*> scope(&rootObject1);
         if (!LookupNameUnqualified(cx, name, scopeChain, &scope))
             goto error;
 
         PUSH_OBJECT(*scope);
     } else {
         PUSH_OBJECT(REGS.fp()->global());
     }
 
@@ -2478,59 +2520,51 @@ CASE(JSOP_ADD)
     if (!AddOperation(cx, lval, rval, res))
         goto error;
     REGS.sp--;
 }
 END_CASE(JSOP_ADD)
 
 CASE(JSOP_SUB)
 {
-    RootedValue& lval = rootValue0;
-    RootedValue& rval = rootValue1;
-    lval = REGS.sp[-2];
-    rval = REGS.sp[-1];
+    ReservedRooted<Value> lval(&rootValue0, REGS.sp[-2]);
+    ReservedRooted<Value> rval(&rootValue1, REGS.sp[-1]);
     MutableHandleValue res = REGS.stackHandleAt(-2);
     if (!SubOperation(cx, lval, rval, res))
         goto error;
     REGS.sp--;
 }
 END_CASE(JSOP_SUB)
 
 CASE(JSOP_MUL)
 {
-    RootedValue& lval = rootValue0;
-    RootedValue& rval = rootValue1;
-    lval = REGS.sp[-2];
-    rval = REGS.sp[-1];
+    ReservedRooted<Value> lval(&rootValue0, REGS.sp[-2]);
+    ReservedRooted<Value> rval(&rootValue1, REGS.sp[-1]);
     MutableHandleValue res = REGS.stackHandleAt(-2);
     if (!MulOperation(cx, lval, rval, res))
         goto error;
     REGS.sp--;
 }
 END_CASE(JSOP_MUL)
 
 CASE(JSOP_DIV)
 {
-    RootedValue& lval = rootValue0;
-    RootedValue& rval = rootValue1;
-    lval = REGS.sp[-2];
-    rval = REGS.sp[-1];
+    ReservedRooted<Value> lval(&rootValue0, REGS.sp[-2]);
+    ReservedRooted<Value> rval(&rootValue1, REGS.sp[-1]);
     MutableHandleValue res = REGS.stackHandleAt(-2);
     if (!DivOperation(cx, lval, rval, res))
         goto error;
     REGS.sp--;
 }
 END_CASE(JSOP_DIV)
 
 CASE(JSOP_MOD)
 {
-    RootedValue& lval = rootValue0;
-    RootedValue& rval = rootValue1;
-    lval = REGS.sp[-2];
-    rval = REGS.sp[-1];
+    ReservedRooted<Value> lval(&rootValue0, REGS.sp[-2]);
+    ReservedRooted<Value> rval(&rootValue1, REGS.sp[-1]);
     MutableHandleValue res = REGS.stackHandleAt(-2);
     if (!ModOperation(cx, lval, rval, res))
         goto error;
     REGS.sp--;
 }
 END_CASE(JSOP_MOD)
 
 CASE(JSOP_NOT)
@@ -2548,53 +2582,47 @@ CASE(JSOP_BITNOT)
     if (!BitNot(cx, value, &i))
         goto error;
     REGS.sp[-1].setInt32(i);
 }
 END_CASE(JSOP_BITNOT)
 
 CASE(JSOP_NEG)
 {
-    RootedValue& val = rootValue0;
-    val = REGS.sp[-1];
+    ReservedRooted<Value> val(&rootValue0, REGS.sp[-1]);
     MutableHandleValue res = REGS.stackHandleAt(-1);
     if (!NegOperation(cx, script, REGS.pc, val, res))
         goto error;
 }
 END_CASE(JSOP_NEG)
 
 CASE(JSOP_POS)
     if (!ToNumber(cx, REGS.stackHandleAt(-1)))
         goto error;
 END_CASE(JSOP_POS)
 
 CASE(JSOP_DELNAME)
 {
-    RootedPropertyName& name = rootName0;
-    name = script->getName(REGS.pc);
-
-    RootedObject& scopeObj = rootObject0;
-    scopeObj = REGS.fp()->scopeChain();
+    ReservedRooted<PropertyName*> name(&rootName0, script->getName(REGS.pc));
+    ReservedRooted<JSObject*> scopeObj(&rootObject0, REGS.fp()->scopeChain());
 
     PUSH_BOOLEAN(true);
     MutableHandleValue res = REGS.stackHandleAt(-1);
     if (!DeleteNameOperation(cx, name, scopeObj, res))
         goto error;
 }
 END_CASE(JSOP_DELNAME)
 
 CASE(JSOP_DELPROP)
 CASE(JSOP_STRICTDELPROP)
 {
     static_assert(JSOP_DELPROP_LENGTH == JSOP_STRICTDELPROP_LENGTH,
                   "delprop and strictdelprop must be the same size");
-    RootedId& id = rootId0;
-    id = NameToId(script->getName(REGS.pc));
-
-    RootedObject& obj = rootObject0;
+    ReservedRooted<jsid> id(&rootId0, NameToId(script->getName(REGS.pc)));
+    ReservedRooted<JSObject*> obj(&rootObject0);
     FETCH_OBJECT(cx, -1, obj);
 
     ObjectOpResult result;
     if (!DeleteProperty(cx, obj, id, result))
         goto error;
     if (!result && JSOp(*REGS.pc) == JSOP_STRICTDELPROP) {
         result.reportError(cx, obj, id);
         goto error;
@@ -2605,24 +2633,23 @@ CASE(JSOP_STRICTDELPROP)
 END_CASE(JSOP_DELPROP)
 
 CASE(JSOP_DELELEM)
 CASE(JSOP_STRICTDELELEM)
 {
     static_assert(JSOP_DELELEM_LENGTH == JSOP_STRICTDELELEM_LENGTH,
                   "delelem and strictdelelem must be the same size");
     /* Fetch the left part and resolve it to a non-null object. */
-    RootedObject& obj = rootObject0;
+    ReservedRooted<JSObject*> obj(&rootObject0);
     FETCH_OBJECT(cx, -2, obj);
 
-    RootedValue& propval = rootValue0;
-    propval = REGS.sp[-1];
+    ReservedRooted<Value> propval(&rootValue0, REGS.sp[-1]);
 
     ObjectOpResult result;
-    RootedId& id = rootId0;
+    ReservedRooted<jsid> id(&rootId0);
     if (!ValueToId<CanGC>(cx, propval, &id))
         goto error;
     if (!DeleteProperty(cx, obj, id, result))
         goto error;
     if (!result && JSOp(*REGS.pc) == JSOP_STRICTDELELEM) {
         result.reportError(cx, obj, id);
         goto error;
     }
@@ -2635,21 +2662,18 @@ END_CASE(JSOP_DELELEM)
 
 CASE(JSOP_TOID)
 {
     /*
      * Increment or decrement requires use to lookup the same property twice,
      * but we need to avoid the observable stringification the second time.
      * There must be an object value below the id, which will not be popped.
      */
-    RootedValue& objval = rootValue0;
-    RootedValue& idval = rootValue1;
-    objval = REGS.sp[-2];
-    idval = REGS.sp[-1];
-
+    ReservedRooted<Value> objval(&rootValue0, REGS.sp[-2]);
+    ReservedRooted<Value> idval(&rootValue1, REGS.sp[-1]);
     MutableHandleValue res = REGS.stackHandleAt(-1);
     if (!ToIdOperation(cx, script, REGS.pc, objval, idval, res))
         goto error;
 }
 END_CASE(JSOP_TOID)
 
 CASE(JSOP_TYPEOFEXPR)
 CASE(JSOP_TYPEOF)
@@ -2678,37 +2702,32 @@ CASE(JSOP_CALLPROP)
 
     TypeScript::Monitor(cx, script, REGS.pc, lval);
     assertSameCompartmentDebugOnly(cx, lval);
 }
 END_CASE(JSOP_GETPROP)
 
 CASE(JSOP_GETPROP_SUPER)
 {
-    RootedObject& receiver = rootObject0;
-    RootedObject& obj = rootObject1;
-
+    ReservedRooted<JSObject*> receiver(&rootObject0);
     FETCH_OBJECT(cx, -2, receiver);
-    obj = &REGS.sp[-1].toObject();
-
+    ReservedRooted<JSObject*> obj(&rootObject1, &REGS.sp[-1].toObject());
     MutableHandleValue rref = REGS.stackHandleAt(-2);
 
     if (!GetProperty(cx, obj, receiver, script->getName(REGS.pc), rref))
         goto error;
 
     REGS.sp--;
 }
 END_CASE(JSOP_GETPROP_SUPER)
 
 CASE(JSOP_GETXPROP)
 {
-    RootedObject& obj = rootObject0;
-    obj = &REGS.sp[-1].toObject();
-    RootedId& id = rootId0;
-    id = NameToId(script->getName(REGS.pc));
+    ReservedRooted<JSObject*> obj(&rootObject0, &REGS.sp[-1].toObject());
+    ReservedRooted<jsid> id(&rootId0, NameToId(script->getName(REGS.pc)));
     MutableHandleValue rval = REGS.stackHandleAt(-1);
     if (!GetPropertyForNameLookup(cx, obj, id, rval))
         goto error;
 
     TypeScript::Monitor(cx, script, REGS.pc, rval);
     assertSameCompartmentDebugOnly(cx, rval);
 }
 END_CASE(JSOP_GETXPROP)
@@ -2732,18 +2751,17 @@ CASE(JSOP_STRICTSETNAME)
 {
     static_assert(JSOP_SETNAME_LENGTH == JSOP_STRICTSETNAME_LENGTH,
                   "setname and strictsetname must be the same size");
     static_assert(JSOP_SETGNAME_LENGTH == JSOP_STRICTSETGNAME_LENGTH,
                   "setganem adn strictsetgname must be the same size");
     static_assert(JSOP_SETNAME_LENGTH == JSOP_SETGNAME_LENGTH,
                   "We're sharing the END_CASE so the lengths better match");
 
-    RootedObject& scope = rootObject0;
-    scope = &REGS.sp[-2].toObject();
+    ReservedRooted<JSObject*> scope(&rootObject0, &REGS.sp[-2].toObject());
     HandleValue value = REGS.stackHandleAt(-1);
 
     if (!SetNameOperation(cx, script, REGS.pc, scope, value))
         goto error;
 
     REGS.sp[-2] = REGS.sp[-1];
     REGS.sp--;
 }
@@ -2752,44 +2770,36 @@ END_CASE(JSOP_SETNAME)
 CASE(JSOP_SETPROP)
 CASE(JSOP_STRICTSETPROP)
 {
     static_assert(JSOP_SETPROP_LENGTH == JSOP_STRICTSETPROP_LENGTH,
                   "setprop and strictsetprop must be the same size");
     HandleValue lval = REGS.stackHandleAt(-2);
     HandleValue rval = REGS.stackHandleAt(-1);
 
-    RootedId& id = rootId0;
-    id = NameToId(script->getName(REGS.pc));
+    ReservedRooted<jsid> id(&rootId0, NameToId(script->getName(REGS.pc)));
     if (!SetPropertyOperation(cx, JSOp(*REGS.pc), lval, id, rval))
         goto error;
 
     REGS.sp[-2] = REGS.sp[-1];
     REGS.sp--;
 }
 END_CASE(JSOP_SETPROP)
 
 CASE(JSOP_SETPROP_SUPER)
 CASE(JSOP_STRICTSETPROP_SUPER)
 {
     static_assert(JSOP_SETPROP_SUPER_LENGTH == JSOP_STRICTSETPROP_SUPER_LENGTH,
                   "setprop-super and strictsetprop-super must be the same size");
 
 
-    RootedValue& receiver = rootValue0;
-    receiver = REGS.sp[-3];
-
-    RootedObject& obj = rootObject0;
-    obj = &REGS.sp[-2].toObject();
-
-    RootedValue& rval = rootValue1;
-    rval = REGS.sp[-1];
-
-    RootedId& id = rootId0;
-    id = NameToId(script->getName(REGS.pc));
+    ReservedRooted<Value> receiver(&rootValue0, REGS.sp[-3]);
+    ReservedRooted<JSObject*> obj(&rootObject0, &REGS.sp[-2].toObject());
+    ReservedRooted<Value> rval(&rootValue1, REGS.sp[-1]);
+    ReservedRooted<jsid> id(&rootId0, NameToId(script->getName(REGS.pc)));
 
     ObjectOpResult result;
     if (!SetProperty(cx, obj, id, rval, receiver, result))
         goto error;
 
     bool strict = JSOp(*REGS.pc) == JSOP_STRICTSETPROP_SUPER;
     if (!result.checkStrictErrorOrWarning(cx, obj, id, strict))
         goto error;
@@ -2818,20 +2828,19 @@ CASE(JSOP_CALLELEM)
     TypeScript::Monitor(cx, script, REGS.pc, res);
     REGS.sp--;
 }
 END_CASE(JSOP_GETELEM)
 
 CASE(JSOP_GETELEM_SUPER)
 {
     HandleValue rval = REGS.stackHandleAt(-3);
-    RootedObject& receiver = rootObject0;
+    ReservedRooted<JSObject*> receiver(&rootObject0);
     FETCH_OBJECT(cx, -2, receiver);
-    RootedObject& obj = rootObject1;
-    obj = &REGS.sp[-1].toObject();
+    ReservedRooted<JSObject*> obj(&rootObject1, &REGS.sp[-1].toObject());
 
     MutableHandleValue res = REGS.stackHandleAt(-3);
 
     // Since we have asserted that obj has to be an object, it cannot be
     // either optimized arguments, or indeed any primitive. This simplifies
     // our task some.
     if (!GetObjectElementOperation(cx, JSOp(*REGS.pc), obj, receiver, rval, res))
         goto error;
@@ -2841,42 +2850,39 @@ CASE(JSOP_GETELEM_SUPER)
 }
 END_CASE(JSOP_GETELEM_SUPER)
 
 CASE(JSOP_SETELEM)
 CASE(JSOP_STRICTSETELEM)
 {
     static_assert(JSOP_SETELEM_LENGTH == JSOP_STRICTSETELEM_LENGTH,
                   "setelem and strictsetelem must be the same size");
-    RootedObject& obj = rootObject0;
+    ReservedRooted<JSObject*> obj(&rootObject0);
     FETCH_OBJECT(cx, -3, obj);
-    RootedId& id = rootId0;
+    ReservedRooted<jsid> id(&rootId0);
     FETCH_ELEMENT_ID(-2, id);
     Value& value = REGS.sp[-1];
-    RootedValue& receiver = rootValue0;
-    receiver = ObjectValue(*obj);
+    ReservedRooted<Value> receiver(&rootValue0, ObjectValue(*obj));
     if (!SetObjectElementOperation(cx, obj, receiver, id, value, *REGS.pc == JSOP_STRICTSETELEM))
         goto error;
     REGS.sp[-3] = value;
     REGS.sp -= 2;
 }
 END_CASE(JSOP_SETELEM)
 
 CASE(JSOP_SETELEM_SUPER)
 CASE(JSOP_STRICTSETELEM_SUPER)
 {
     static_assert(JSOP_SETELEM_SUPER_LENGTH == JSOP_STRICTSETELEM_SUPER_LENGTH,
                   "setelem-super and strictsetelem-super must be the same size");
 
-    RootedId& id = rootId0;
+    ReservedRooted<jsid> id(&rootId0);
     FETCH_ELEMENT_ID(-4, id);
-    RootedValue& receiver = rootValue0;
-    receiver = REGS.sp[-3];
-    RootedObject& obj = rootObject1;
-    obj = &REGS.sp[-2].toObject();
+    ReservedRooted<Value> receiver(&rootValue0, REGS.sp[-3]);
+    ReservedRooted<JSObject*> obj(&rootObject1, &REGS.sp[-2].toObject());
     Value& value = REGS.sp[-1];
 
     bool strict = JSOp(*REGS.pc) == JSOP_STRICTSETELEM_SUPER;
     if (!SetObjectElementOperation(cx, obj, receiver, id, value, strict))
         goto error;
     REGS.sp[-4] = value;
     REGS.sp -= 3;
 }
@@ -2939,83 +2945,88 @@ CASE(JSOP_FUNCALL)
     if (REGS.fp()->hasPushedSPSFrame())
         cx->runtime()->spsProfiler.updatePC(script, REGS.pc);
 
     MOZ_ASSERT(REGS.stackDepth() >= 2u + GET_ARGC(REGS.pc));
     CallArgs args = CallArgsFromSp(GET_ARGC(REGS.pc), REGS.sp);
 
     bool construct = (*REGS.pc == JSOP_NEW);
 
-    RootedFunction& fun = rootFunction0;
-    bool isFunction = IsFunctionObject(args.calleev(), fun.address());
+    JSFunction* maybeFun;
+    bool isFunction = IsFunctionObject(args.calleev(), &maybeFun);
 
     /* Don't bother trying to fast-path calls to scripted non-constructors. */
-    if (!isFunction || !fun->isInterpreted() || !fun->isConstructor()) {
+    if (!isFunction || !maybeFun->isInterpreted() || !maybeFun->isConstructor()) {
         if (construct) {
             if (!InvokeConstructor(cx, args))
                 goto error;
         } else {
             if (!Invoke(cx, args))
                 goto error;
         }
         Value* newsp = args.spAfterCall();
         TypeScript::Monitor(cx, script, REGS.pc, newsp[-1]);
         REGS.sp = newsp;
         ADVANCE_AND_DISPATCH(JSOP_CALL_LENGTH);
     }
 
-    RootedScript& funScript = rootScript0;
-    funScript = fun->getOrCreateScript(cx);
-    if (!funScript)
-        goto error;
-
-    InitialFrameFlags initial = construct ? INITIAL_CONSTRUCT : INITIAL_NONE;
-    bool createSingleton = ObjectGroup::useSingletonForNewObject(cx, script, REGS.pc);
-
-    TypeMonitorCall(cx, args, construct);
-
     {
-        InvokeState state(cx, args, initial);
+        MOZ_ASSERT(maybeFun);
+        ReservedRooted<JSFunction*> fun(&rootFunction0, maybeFun);
+        ReservedRooted<JSScript*> funScript(&rootScript0, fun->getOrCreateScript(cx));
+        if (!funScript)
+            goto error;
+
+        InitialFrameFlags initial = construct ? INITIAL_CONSTRUCT : INITIAL_NONE;
+        bool createSingleton = ObjectGroup::useSingletonForNewObject(cx, script, REGS.pc);
+
+        TypeMonitorCall(cx, args, construct);
+
+        mozilla::Maybe<InvokeState> state;
+        state.emplace(cx, args, initial);
+
         if (createSingleton)
-            state.setCreateSingleton();
+            state->setCreateSingleton();
 
         if (!createSingleton && jit::IsIonEnabled(cx)) {
-            jit::MethodStatus status = jit::CanEnter(cx, state);
+            jit::MethodStatus status = jit::CanEnter(cx, state.ref());
             if (status == jit::Method_Error)
                 goto error;
             if (status == jit::Method_Compiled) {
-                jit::JitExecStatus exec = jit::IonCannon(cx, state);
+                jit::JitExecStatus exec = jit::IonCannon(cx, state.ref());
                 CHECK_BRANCH();
                 REGS.sp = args.spAfterCall();
                 interpReturnOK = !IsErrorStatus(exec);
                 goto jit_return;
             }
         }
 
         if (jit::IsBaselineEnabled(cx)) {
-            jit::MethodStatus status = jit::CanEnterBaselineMethod(cx, state);
+            jit::MethodStatus status = jit::CanEnterBaselineMethod(cx, state.ref());
             if (status == jit::Method_Error)
                 goto error;
             if (status == jit::Method_Compiled) {
-                jit::JitExecStatus exec = jit::EnterBaselineMethod(cx, state);
+                jit::JitExecStatus exec = jit::EnterBaselineMethod(cx, state.ref());
                 CHECK_BRANCH();
                 REGS.sp = args.spAfterCall();
                 interpReturnOK = !IsErrorStatus(exec);
                 goto jit_return;
             }
         }
+
+        state.reset();
+        funScript = fun->nonLazyScript();
+
+        if (!activation.pushInlineFrame(args, funScript, initial))
+            goto error;
+
+        if (createSingleton)
+            REGS.fp()->setCreateSingleton();
     }
 
-    funScript = fun->nonLazyScript();
-    if (!activation.pushInlineFrame(args, funScript, initial))
-        goto error;
-
-    if (createSingleton)
-        REGS.fp()->setCreateSingleton();
-
     SET_SCRIPT(REGS.fp()->script());
 
     {
         TraceLoggerEvent event(logger, TraceLogger_Scripts, script);
         TraceLogStartEvent(logger, event);
         TraceLogStartEvent(logger, TraceLogger_Interpreter);
     }
 
@@ -3047,58 +3058,52 @@ CASE(JSOP_THROWMSG)
 }
 END_CASE(JSOP_THROWMSG)
 
 CASE(JSOP_IMPLICITTHIS)
 CASE(JSOP_GIMPLICITTHIS)
 {
     JSOp op = JSOp(*REGS.pc);
     if (op == JSOP_IMPLICITTHIS || script->hasPollutedGlobalScope()) {
-        RootedPropertyName& name = rootName0;
-        name = script->getName(REGS.pc);
-
-        RootedObject& scopeObj = rootObject0;
-        scopeObj = REGS.fp()->scopeChain();
-
-        RootedObject& scope = rootObject1;
+        ReservedRooted<PropertyName*> name(&rootName0, script->getName(REGS.pc));
+        ReservedRooted<JSObject*> scopeObj(&rootObject0, REGS.fp()->scopeChain());
+        ReservedRooted<JSObject*> scope(&rootObject1);
         if (!LookupNameWithGlobalDefault(cx, name, scopeObj, &scope))
             goto error;
 
-        RootedValue& v = rootValue0;
+        ReservedRooted<Value> v(&rootValue0);
         if (!ComputeImplicitThis(cx, scope, &v))
             goto error;
         PUSH_COPY(v);
     } else {
         // Treat it like JSOP_UNDEFINED.
         PUSH_UNDEFINED();
     }
     static_assert(JSOP_IMPLICITTHIS_LENGTH == JSOP_GIMPLICITTHIS_LENGTH,
                   "We're sharing the END_CASE so the lengths better match");
 }
 END_CASE(JSOP_IMPLICITTHIS)
 
 CASE(JSOP_GETGNAME)
 CASE(JSOP_GETNAME)
 {
-    RootedValue& rval = rootValue0;
-
+    ReservedRooted<Value> rval(&rootValue0);
     if (!GetNameOperation(cx, REGS.fp(), REGS.pc, &rval))
         goto error;
 
     PUSH_COPY(rval);
     TypeScript::Monitor(cx, script, REGS.pc, rval);
     static_assert(JSOP_GETNAME_LENGTH == JSOP_GETGNAME_LENGTH,
                   "We're sharing the END_CASE so the lengths better match");
 }
 END_CASE(JSOP_GETNAME)
 
 CASE(JSOP_GETINTRINSIC)
 {
-    RootedValue& rval = rootValue0;
-
+    ReservedRooted<Value> rval(&rootValue0);
     if (!GetIntrinsicOperation(cx, REGS.pc, &rval))
         goto error;
 
     PUSH_COPY(rval);
     TypeScript::Monitor(cx, script, REGS.pc, rval);
 }
 END_CASE(JSOP_GETINTRINSIC)
 
@@ -3144,39 +3149,35 @@ CASE(JSOP_TOSTRING)
 END_CASE(JSOP_TOSTRING)
 
 CASE(JSOP_SYMBOL)
     PUSH_SYMBOL(cx->wellKnownSymbols().get(GET_UINT8(REGS.pc)));
 END_CASE(JSOP_SYMBOL)
 
 CASE(JSOP_OBJECT)
 {
-    RootedObject& ref = rootObject0;
-    ref = script->getObject(REGS.pc);
+    ReservedRooted<JSObject*> ref(&rootObject0, script->getObject(REGS.pc));
     if (JS::CompartmentOptionsRef(cx).cloneSingletons()) {
         JSObject* obj = DeepCloneObjectLiteral(cx, ref, TenuredObject);
         if (!obj)
             goto error;
         PUSH_OBJECT(*obj);
     } else {
         JS::CompartmentOptionsRef(cx).setSingletonsAsValues();
         PUSH_OBJECT(*ref);
     }
 }
 END_CASE(JSOP_OBJECT)
 
 CASE(JSOP_CALLSITEOBJ)
 {
 
-    RootedObject& cso = rootObject0;
-    cso = script->getObject(REGS.pc);
-    RootedObject& raw = rootObject1;
-    raw = script->getObject(GET_UINT32_INDEX(REGS.pc) + 1);
-    RootedValue& rawValue = rootValue0;
-    rawValue.setObject(*raw);
+    ReservedRooted<JSObject*> cso(&rootObject0, script->getObject(REGS.pc));
+    ReservedRooted<JSObject*> raw(&rootObject1, script->getObject(GET_UINT32_INDEX(REGS.pc) + 1));
+    ReservedRooted<Value> rawValue(&rootValue0, ObjectValue(*raw));
 
     if (!ProcessCallSiteObjOperation(cx, cso, raw, rawValue))
         goto error;
 
     PUSH_OBJECT(*cso);
 }
 END_CASE(JSOP_CALLSITEOBJ)
 
@@ -3266,29 +3267,27 @@ CASE(JSOP_RUNONCE)
 {
     if (!RunOnceScriptPrologue(cx, script))
         goto error;
 }
 END_CASE(JSOP_RUNONCE)
 
 CASE(JSOP_REST)
 {
-    RootedObject& rest = rootObject0;
-    rest = REGS.fp()->createRestParameter(cx);
+    ReservedRooted<JSObject*> rest(&rootObject0, REGS.fp()->createRestParameter(cx));
     if (!rest)
         goto error;
     PUSH_COPY(ObjectValue(*rest));
 }
 END_CASE(JSOP_REST)
 
 CASE(JSOP_GETALIASEDVAR)
 {
     ScopeCoordinate sc = ScopeCoordinate(REGS.pc);
-    RootedValue& val = rootValue0;
-    val = REGS.fp()->aliasedVarScope(sc).aliasedVar(sc);
+    ReservedRooted<Value> val(&rootValue0, REGS.fp()->aliasedVarScope(sc).aliasedVar(sc));
     MOZ_ASSERT(!IsUninitializedLexical(val));
     PUSH_COPY(val);
     TypeScript::Monitor(cx, script, REGS.pc, REGS.sp[-1]);
 }
 END_CASE(JSOP_GETALIASEDVAR)
 
 CASE(JSOP_SETALIASEDVAR)
 {
@@ -3296,35 +3295,33 @@ CASE(JSOP_SETALIASEDVAR)
     ScopeObject& obj = REGS.fp()->aliasedVarScope(sc);
     SetAliasedVarOperation(cx, script, REGS.pc, obj, sc, REGS.sp[-1], CheckLexical);
 }
 END_CASE(JSOP_SETALIASEDVAR)
 
 CASE(JSOP_CHECKLEXICAL)
 {
     uint32_t i = GET_LOCALNO(REGS.pc);
-    RootedValue& val = rootValue0;
-    val = REGS.fp()->unaliasedLocal(i);
+    ReservedRooted<Value> val(&rootValue0, REGS.fp()->unaliasedLocal(i));
     if (!CheckUninitializedLexical(cx, script, REGS.pc, val))
         goto error;
 }
 END_CASE(JSOP_CHECKLEXICAL)
 
 CASE(JSOP_INITLEXICAL)
 {
     uint32_t i = GET_LOCALNO(REGS.pc);
     REGS.fp()->unaliasedLocal(i) = REGS.sp[-1];
 }
 END_CASE(JSOP_INITLEXICAL)
 
 CASE(JSOP_CHECKALIASEDLEXICAL)
 {
     ScopeCoordinate sc = ScopeCoordinate(REGS.pc);
-    RootedValue& val = rootValue0;
-    val = REGS.fp()->aliasedVarScope(sc).aliasedVar(sc);
+    ReservedRooted<Value> val(&rootValue0, REGS.fp()->aliasedVarScope(sc).aliasedVar(sc));
     if (!CheckUninitializedLexical(cx, script, REGS.pc, val))
         goto error;
 }
 END_CASE(JSOP_CHECKALIASEDLEXICAL)
 
 CASE(JSOP_INITALIASEDLEXICAL)
 {
     ScopeCoordinate sc = ScopeCoordinate(REGS.pc);
@@ -3388,107 +3385,92 @@ CASE(JSOP_DEFVAR)
     /* ES5 10.5 step 8 (with subsequent errata). */
     unsigned attrs = JSPROP_ENUMERATE;
     if (*REGS.pc == JSOP_DEFCONST)
         attrs |= JSPROP_READONLY;
     else if (!REGS.fp()->isEvalFrame())
         attrs |= JSPROP_PERMANENT;
 
     /* Step 8b. */
-    RootedObject& obj = rootObject0;
-    obj = &REGS.fp()->varObj();
-
-    RootedPropertyName& name = rootName0;
-    name = script->getName(REGS.pc);
+    ReservedRooted<JSObject*> obj(&rootObject0, &REGS.fp()->varObj());
+    ReservedRooted<PropertyName*> name(&rootName0, script->getName(REGS.pc));
 
     if (!DefVarOrConstOperation(cx, obj, name, attrs))
         goto error;
 }
 END_CASE(JSOP_DEFVAR)
 
 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).
      */
-    RootedFunction& fun = rootFunction0;
-    fun = script->getFunction(GET_UINT32_INDEX(REGS.pc));
-
+    ReservedRooted<JSFunction*> fun(&rootFunction0, script->getFunction(GET_UINT32_INDEX(REGS.pc)));
     if (!DefFunOperation(cx, script, REGS.fp()->scopeChain(), fun))
         goto error;
 }
 END_CASE(JSOP_DEFFUN)
 
 CASE(JSOP_LAMBDA)
 {
     /* Load the specified function object literal. */
-    RootedFunction& fun = rootFunction0;
-    fun = script->getFunction(GET_UINT32_INDEX(REGS.pc));
-
+    ReservedRooted<JSFunction*> fun(&rootFunction0, script->getFunction(GET_UINT32_INDEX(REGS.pc)));
     JSObject* obj = Lambda(cx, fun, REGS.fp()->scopeChain());
     if (!obj)
         goto error;
     MOZ_ASSERT(obj->getProto());
     PUSH_OBJECT(*obj);
 }
 END_CASE(JSOP_LAMBDA)
 
 CASE(JSOP_LAMBDA_ARROW)
 {
     /* Load the specified function object literal. */
-    RootedFunction& fun = rootFunction0;
-    fun = script->getFunction(GET_UINT32_INDEX(REGS.pc));
-    RootedValue& thisv = rootValue0;
-    thisv = REGS.sp[-1];
+    ReservedRooted<JSFunction*> fun(&rootFunction0, script->getFunction(GET_UINT32_INDEX(REGS.pc)));
+    ReservedRooted<Value> thisv(&rootValue0, REGS.sp[-1]);
     JSObject* obj = LambdaArrow(cx, fun, REGS.fp()->scopeChain(), thisv);
     if (!obj)
         goto error;
     MOZ_ASSERT(obj->getProto());
     REGS.sp[-1].setObject(*obj);
 }
 END_CASE(JSOP_LAMBDA_ARROW)
 
 CASE(JSOP_CALLEE)
     MOZ_ASSERT(REGS.fp()->isNonEvalFunctionFrame());
     PUSH_COPY(REGS.fp()->calleev());
 END_CASE(JSOP_CALLEE)
 
 CASE(JSOP_INITPROP_GETTER)
 CASE(JSOP_INITPROP_SETTER)
 {
-    RootedObject& obj = rootObject0;
-    RootedPropertyName& name = rootName0;
-    RootedObject& val = rootObject1;
-
     MOZ_ASSERT(REGS.stackDepth() >= 2);
-    obj = &REGS.sp[-2].toObject();
-    name = script->getName(REGS.pc);
-    val = &REGS.sp[-1].toObject();
+
+    ReservedRooted<JSObject*> obj(&rootObject0, &REGS.sp[-2].toObject());
+    ReservedRooted<PropertyName*> name(&rootName0, script->getName(REGS.pc));
+    ReservedRooted<JSObject*> val(&rootObject1, &REGS.sp[-1].toObject());
 
     if (!InitGetterSetterOperation(cx, REGS.pc, obj, name, val))
         goto error;
 
     REGS.sp--;
 }
 END_CASE(JSOP_INITPROP_GETTER)
 
 CASE(JSOP_INITELEM_GETTER)
 CASE(JSOP_INITELEM_SETTER)
 {
-    RootedObject& obj = rootObject0;
-    RootedValue& idval = rootValue0;
-    RootedObject& val = rootObject1;
-
     MOZ_ASSERT(REGS.stackDepth() >= 3);
-    obj = &REGS.sp[-3].toObject();
-    idval = REGS.sp[-2];
-    val = &REGS.sp[-1].toObject();
+
+    ReservedRooted<JSObject*> obj(&rootObject0, &REGS.sp[-3].toObject());
+    ReservedRooted<Value> idval(&rootValue0, REGS.sp[-2]);
+    ReservedRooted<JSObject*> val(&rootObject1, &REGS.sp[-1].toObject());
 
     if (!InitGetterSetterOperation(cx, REGS.pc, obj, idval, val))
         goto error;
 
     REGS.sp -= 2;
 }
 END_CASE(JSOP_INITELEM_GETTER)
 
@@ -3520,23 +3502,21 @@ CASE(JSOP_SPREADCALLARRAY)
     if (!obj)
         goto error;
     PUSH_OBJECT(*obj);
 }
 END_CASE(JSOP_NEWARRAY)
 
 CASE(JSOP_NEWARRAY_COPYONWRITE)
 {
-    RootedObject& baseobj = rootObject0;
-    baseobj = ObjectGroup::getOrFixupCopyOnWriteObject(cx, script, REGS.pc);
+    ReservedRooted<JSObject*> baseobj(&rootObject0, ObjectGroup::getOrFixupCopyOnWriteObject(cx, script, REGS.pc));
     if (!baseobj)
         goto error;
 
-    RootedObject& obj = rootObject1;
-    obj = NewDenseCopyOnWriteArray(cx, baseobj.as<ArrayObject>(), gc::DefaultHeap);
+    ReservedRooted<JSObject*> obj(&rootObject1, NewDenseCopyOnWriteArray(cx, ((RootedObject&)(baseobj)).as<ArrayObject>(), gc::DefaultHeap));
     if (!obj)
         goto error;
 
     PUSH_OBJECT(*obj);
 }
 END_CASE(JSOP_NEWARRAY_COPYONWRITE)
 
 CASE(JSOP_NEWOBJECT)
@@ -3548,21 +3528,18 @@ CASE(JSOP_NEWOBJECT)
 }
 END_CASE(JSOP_NEWOBJECT)
 
 CASE(JSOP_MUTATEPROTO)
 {
     MOZ_ASSERT(REGS.stackDepth() >= 2);
 
     if (REGS.sp[-1].isObjectOrNull()) {
-        RootedObject& newProto = rootObject1;
-        rootObject1 = REGS.sp[-1].toObjectOrNull();
-
-        RootedObject& obj = rootObject0;
-        obj = &REGS.sp[-2].toObject();
+        ReservedRooted<JSObject*> newProto(&rootObject1, REGS.sp[-1].toObjectOrNull());
+        ReservedRooted<JSObject*> obj(&rootObject0, &REGS.sp[-2].toObject());
         MOZ_ASSERT(obj->is<PlainObject>());
 
         if (!SetPrototype(cx, obj, newProto))
             goto error;
     }
 
     REGS.sp--;
 }
@@ -3573,22 +3550,20 @@ CASE(JSOP_INITLOCKEDPROP)
 CASE(JSOP_INITHIDDENPROP)
 {
     static_assert(JSOP_INITPROP_LENGTH == JSOP_INITLOCKEDPROP_LENGTH,
                   "initprop and initlockedprop must be the same size");
     static_assert(JSOP_INITPROP_LENGTH == JSOP_INITHIDDENPROP_LENGTH,
                   "initprop and inithiddenprop must be the same size");
     /* Load the property's initial value into rval. */
     MOZ_ASSERT(REGS.stackDepth() >= 2);
-    RootedValue& rval = rootValue0;
-    rval = REGS.sp[-1];
+    ReservedRooted<Value> rval(&rootValue0, REGS.sp[-1]);
 
     /* Load the object being initialized into lval/obj. */
-    RootedObject& obj = rootObject0;
-    obj = &REGS.sp[-2].toObject();
+    ReservedRooted<JSObject*> obj(&rootObject0, &REGS.sp[-2].toObject());
 
     PropertyName* name = script->getName(REGS.pc);
 
     RootedId& id = rootId0;
     id = NameToId(name);
 
     if (!InitPropertyOperation(cx, JSOp(*REGS.pc), obj, id, rval))
         goto error;
@@ -3598,49 +3573,46 @@ CASE(JSOP_INITHIDDENPROP)
 END_CASE(JSOP_INITPROP)
 
 CASE(JSOP_INITELEM)
 {
     MOZ_ASSERT(REGS.stackDepth() >= 3);
     HandleValue val = REGS.stackHandleAt(-1);
     HandleValue id = REGS.stackHandleAt(-2);
 
-    RootedObject& obj = rootObject0;
-    obj = &REGS.sp[-3].toObject();
+    ReservedRooted<JSObject*> obj(&rootObject0, &REGS.sp[-3].toObject());
 
     if (!InitElemOperation(cx, obj, id, val))
         goto error;
 
     REGS.sp -= 2;
 }
 END_CASE(JSOP_INITELEM)
 
 CASE(JSOP_INITELEM_ARRAY)
 {
     MOZ_ASSERT(REGS.stackDepth() >= 2);
     HandleValue val = REGS.stackHandleAt(-1);
 
-    RootedObject& obj = rootObject0;
-    obj = &REGS.sp[-2].toObject();
+    ReservedRooted<JSObject*> obj(&rootObject0, &REGS.sp[-2].toObject());
 
     uint32_t index = GET_UINT24(REGS.pc);
     if (!InitArrayElemOperation(cx, REGS.pc, obj, index, val))
         goto error;
 
     REGS.sp--;
 }
 END_CASE(JSOP_INITELEM_ARRAY)
 
 CASE(JSOP_INITELEM_INC)
 {
     MOZ_ASSERT(REGS.stackDepth() >= 3);
     HandleValue val = REGS.stackHandleAt(-1);
 
-    RootedObject& obj = rootObject0;
-    obj = &REGS.sp[-3].toObject();
+    ReservedRooted<JSObject*> obj(&rootObject0, &REGS.sp[-3].toObject());
 
     uint32_t index = REGS.sp[-2].toInt32();
     if (!InitArrayElemOperation(cx, REGS.pc, obj, index, val))
         goto error;
 
     REGS.sp[-2].setInt32(index + 1);
     REGS.sp--;
 }
@@ -3689,42 +3661,40 @@ CASE(JSOP_EXCEPTION)
 END_CASE(JSOP_EXCEPTION)
 
 CASE(JSOP_FINALLY)
     CHECK_BRANCH();
 END_CASE(JSOP_FINALLY)
 
 CASE(JSOP_THROWING)
 {
-    RootedValue& v = rootValue0;
+    ReservedRooted<Value> v(&rootValue0);
     POP_COPY_TO(v);
     MOZ_ALWAYS_TRUE(ThrowingOperation(cx, v));
 }
 END_CASE(JSOP_THROWING)
 
 CASE(JSOP_THROW)
 {
     CHECK_BRANCH();
-    RootedValue& v = rootValue0;
+    ReservedRooted<Value> v(&rootValue0);
     POP_COPY_TO(v);
     JS_ALWAYS_FALSE(Throw(cx, v));
     /* let the code at error try to catch the exception. */
     goto error;
 }
 
 CASE(JSOP_INSTANCEOF)
 {
-    RootedValue& rref = rootValue0;
-    rref = REGS.sp[-1];
-    if (rref.isPrimitive()) {
+    ReservedRooted<Value> rref(&rootValue0, REGS.sp[-1]);
+    if (HandleValue(rref).isPrimitive()) {
         ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, -1, rref, nullptr);
         goto error;
     }
-    RootedObject& obj = rootObject0;
-    obj = &rref.toObject();
+    ReservedRooted<JSObject*> obj(&rootObject0, &rref.toObject());
     bool cond = false;
     if (!HasInstance(cx, obj, REGS.stackHandleAt(-2), &cond))
         goto error;
     REGS.sp--;
     REGS.sp[-1].setBoolean(cond);
 }
 END_CASE(JSOP_INSTANCEOF)
 
@@ -3810,102 +3780,96 @@ CASE(JSOP_GENERATOR)
     PUSH_OBJECT(*obj);
 }
 END_CASE(JSOP_GENERATOR)
 
 CASE(JSOP_INITIALYIELD)
 {
     MOZ_ASSERT(!cx->isExceptionPending());
     MOZ_ASSERT(REGS.fp()->isNonEvalFunctionFrame());
-    RootedObject& obj = rootObject0;
-    obj = &REGS.sp[-1].toObject();
+    ReservedRooted<JSObject*> obj(&rootObject0, &REGS.sp[-1].toObject());
     POP_RETURN_VALUE();
     MOZ_ASSERT(REGS.stackDepth() == 0);
     if (!GeneratorObject::initialSuspend(cx, obj, REGS.fp(), REGS.pc))
         goto error;
     goto successful_return_continuation;
 }
 
 CASE(JSOP_YIELD)
 {
     MOZ_ASSERT(!cx->isExceptionPending());
     MOZ_ASSERT(REGS.fp()->isNonEvalFunctionFrame());
-    RootedObject& obj = rootObject0;
-    obj = &REGS.sp[-1].toObject();
+    ReservedRooted<JSObject*> obj(&rootObject0, &REGS.sp[-1].toObject());
     if (!GeneratorObject::normalSuspend(cx, obj, REGS.fp(), REGS.pc,
                                         REGS.spForStackDepth(0), REGS.stackDepth() - 2))
     {
         goto error;
     }
 
     REGS.sp--;
     POP_RETURN_VALUE();
 
     goto successful_return_continuation;
 }
 
 CASE(JSOP_RESUME)
 {
-    RootedObject& gen = rootObject0;
-    RootedValue& val = rootValue0;
-    val = REGS.sp[-1];
-    gen = &REGS.sp[-2].toObject();
-    // popInlineFrame expects there to be an additional value on the stack to
-    // pop off, so leave "gen" on the stack.
-
-    GeneratorObject::ResumeKind resumeKind = GeneratorObject::getResumeKind(REGS.pc);
-    bool ok = GeneratorObject::resume(cx, activation, gen, val, resumeKind);
-    SET_SCRIPT(REGS.fp()->script());
-    if (!ok)
-        goto error;
-
+    {
+        ReservedRooted<JSObject*> gen(&rootObject0, &REGS.sp[-2].toObject());
+        ReservedRooted<Value> val(&rootValue0, REGS.sp[-1]);
+        // popInlineFrame expects there to be an additional value on the stack
+        // to pop off, so leave "gen" on the stack.
+
+        GeneratorObject::ResumeKind resumeKind = GeneratorObject::getResumeKind(REGS.pc);
+        bool ok = GeneratorObject::resume(cx, activation, gen, val, resumeKind);
+        SET_SCRIPT(REGS.fp()->script());
+        if (!ok)
+            goto error;
+    }
     ADVANCE_AND_DISPATCH(0);
 }
 
 CASE(JSOP_DEBUGAFTERYIELD)
 {
     // No-op in the interpreter, as GeneratorObject::resume takes care of
     // fixing up InterpreterFrames.
     MOZ_ASSERT_IF(REGS.fp()->script()->isDebuggee(), REGS.fp()->isDebuggee());
 }
 END_CASE(JSOP_DEBUGAFTERYIELD)
 
 CASE(JSOP_FINALYIELDRVAL)
 {
-    RootedObject& gen = rootObject0;
-    gen = &REGS.sp[-1].toObject();
+    ReservedRooted<JSObject*> gen(&rootObject0, &REGS.sp[-1].toObject());
     REGS.sp--;
 
     if (!GeneratorObject::finalSuspend(cx, gen)) {
         interpReturnOK = false;
         goto return_continuation;
     }
 
     goto successful_return_continuation;
 }
 
 CASE(JSOP_ARRAYPUSH)
 {
-    RootedObject& obj = rootObject0;
-    obj = &REGS.sp[-1].toObject();
+    ReservedRooted<JSObject*> obj(&rootObject0, &REGS.sp[-1].toObject());
     if (!NewbornArrayPush(cx, obj, REGS.sp[-2]))
         goto error;
     REGS.sp -= 2;
 }
 END_CASE(JSOP_ARRAYPUSH)
 
 CASE(JSOP_CLASSHERITAGE)
 {
-    RootedValue& val = rootValue0;
-    val = REGS.sp[-1];
-
-    RootedValue& objProto = rootValue1;
-    RootedObject& funcProto = rootObject0;
+    ReservedRooted<Value> val(&rootValue0, REGS.sp[-1]);
+
+    ReservedRooted<Value> objProto(&rootValue1);
+    ReservedRooted<JSObject*> funcProto(&rootObject0);
     if (val.isNull()) {
-        objProto.setNull();
+        objProto = NullValue();
         if (!GetBuiltinPrototype(cx, JSProto_Function, &funcProto))
             goto error;
     } else {
         if (!val.isObject() || !val.toObject().isConstructor()) {
             ReportIsNotFunction(cx, val, 0, CONSTRUCT);
             goto error;
         }
 
@@ -3922,57 +3886,53 @@ CASE(JSOP_CLASSHERITAGE)
 
     REGS.sp[-1].setObject(*funcProto);
     PUSH_COPY(objProto);
 }
 END_CASE(JSOP_CLASSHERITAGE)
 
 CASE(JSOP_FUNWITHPROTO)
 {
-    RootedObject& proto = rootObject1;
-    proto = &REGS.sp[-1].toObject();
+    ReservedRooted<JSObject*> proto(&rootObject1, &REGS.sp[-1].toObject());
 
     /* Load the specified function object literal. */
-    RootedFunction& fun = rootFunction0;
-    fun = script->getFunction(GET_UINT32_INDEX(REGS.pc));
+    ReservedRooted<JSFunction*> fun(&rootFunction0, script->getFunction(GET_UINT32_INDEX(REGS.pc)));
 
     JSObject* obj = CloneFunctionObjectIfNotSingleton(cx, fun, REGS.fp()->scopeChain(),
                                                       proto, GenericObject);
     if (!obj)
         goto error;
 
     REGS.sp[-1].setObject(*obj);
 }
 END_CASE(JSOP_FUNWITHPROTO)
 
 CASE(JSOP_OBJWITHPROTO)
 {
-    RootedObject& proto = rootObject0;
-    proto = REGS.sp[-1].toObjectOrNull();
+    ReservedRooted<JSObject*> proto(&rootObject0, REGS.sp[-1].toObjectOrNull());
 
     JSObject* obj = NewObjectWithGivenProto<PlainObject>(cx, proto);
     if (!obj)
         goto error;
 
     REGS.sp[-1].setObject(*obj);
 }
 END_CASE(JSOP_OBJWITHPROTO)
 
 CASE(JSOP_INITHOMEOBJECT)
 {
     unsigned skipOver = GET_UINT8(REGS.pc);
     MOZ_ASSERT(REGS.stackDepth() >= skipOver + 2);
 
     /* Load the function to be initialized */
-    RootedFunction& func = rootFunction0;
-    func = &REGS.sp[-1].toObject().as<JSFunction>();
+    ReservedRooted<JSFunction*> func(&rootFunction0, &REGS.sp[-1].toObject().as<JSFunction>());
     MOZ_ASSERT(func->allowSuperProperty());
 
     /* Load the home object */
-    RootedNativeObject& obj = rootNativeObject0;
+    ReservedRooted<NativeObject*> obj(&rootNativeObject0);
     obj = &REGS.sp[int(-2 - skipOver)].toObject().as<NativeObject>();
     MOZ_ASSERT(obj->is<PlainObject>() || obj->is<JSFunction>());
 
     func->setExtendedSlot(FunctionExtended::METHOD_HOMEOBJECT_SLOT, ObjectValue(*obj));
 }
 END_CASE(JSOP_INITHOMEOBJECT)
 
 CASE(JSOP_SUPERBASE)
@@ -3987,20 +3947,18 @@ CASE(JSOP_SUPERBASE)
             // objects. Skip them to find what we came for.
             if (callee.isArrow())
                 continue;
 
             MOZ_ASSERT(callee.allowSuperProperty());
             MOZ_ASSERT(callee.nonLazyScript()->needsHomeObject());
             const Value& homeObjVal = callee.getExtendedSlot(FunctionExtended::METHOD_HOMEOBJECT_SLOT);
 
-            RootedObject& homeObj = rootObject0;
-            homeObj = &homeObjVal.toObject();
-
-            RootedObject& superBase = rootObject1;
+            ReservedRooted<JSObject*> homeObj(&rootObject0, &homeObjVal.toObject());
+            ReservedRooted<JSObject*> superBase(&rootObject1);
             if (!GetPrototype(cx, homeObj, &superBase))
                 goto error;
 
             if (!superBase) {
                 JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
                                      "null", "object");
                 goto error;
             }
@@ -4033,30 +3991,31 @@ DEFAULT()
 
       case ErrorReturnContinuation:
         interpReturnOK = false;
         goto return_continuation;
 
       case CatchContinuation:
         ADVANCE_AND_DISPATCH(0);
 
-      case FinallyContinuation:
+      case FinallyContinuation: {
         /*
          * Push (true, exception) pair for finally to indicate that [retsub]
          * should rethrow the exception.
          */
-        RootedValue& exception = rootValue0;
+        ReservedRooted<Value> exception(&rootValue0);
         if (!cx->getPendingException(&exception)) {
             interpReturnOK = false;
             goto return_continuation;
         }
         PUSH_BOOLEAN(true);
         PUSH_COPY(exception);
         cx->clearPendingException();
-        ADVANCE_AND_DISPATCH(0);
+      }
+      ADVANCE_AND_DISPATCH(0);
     }
 
     MOZ_CRASH("Invalid HandleError continuation");
 
   exit:
     interpReturnOK = Debugger::onLeaveFrame(cx, REGS.fp(), interpReturnOK);
 
     REGS.fp()->epilogue(cx);