Bug 769132 - Add a receiver argument to all the set-property APIs. r=luke
authorJeff Walden <jwalden@mit.edu>
Wed, 27 Jun 2012 20:21:39 -0700
changeset 98020 03549c72043d010e47a1cc4bf90f9c0bbbf3fbf9
parent 98019 bc305a9ac000eccb0f2e17dd16c7e04186022ada
child 98021 418f162dfd8e833858b81890aa6533f74aa9ce50
push id11323
push userjwalden@mit.edu
push dateSat, 30 Jun 2012 05:08:08 +0000
treeherdermozilla-inbound@03549c72043d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs769132
milestone16.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 769132 - Add a receiver argument to all the set-property APIs. r=luke
js/src/jsapi.cpp
js/src/jsarray.cpp
js/src/jsinterp.cpp
js/src/jsinterpinlines.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsproxy.cpp
js/src/jsreflect.cpp
js/src/jsscope.h
js/src/jsscopeinlines.h
js/src/jstypedarray.cpp
js/src/jsxml.cpp
js/src/methodjit/StubCalls-inl.h
js/src/methodjit/StubCalls.cpp
js/src/vm/ArgumentsObject.cpp
js/src/vm/Debugger.cpp
js/src/vm/ScopeObject.cpp
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4156,35 +4156,38 @@ JS_GetMethodById(JSContext *cx, JSObject
 JS_PUBLIC_API(JSBool)
 JS_GetMethod(JSContext *cx, JSObject *obj, const char *name, JSObject **objp, jsval *vp)
 {
     JSAtom *atom = js_Atomize(cx, name, strlen(name));
     return atom && JS_GetMethodById(cx, obj, AtomToId(atom), objp, vp);
 }
 
 JS_PUBLIC_API(JSBool)
-JS_SetPropertyById(JSContext *cx, JSObject *obj, jsid id_, jsval *vp)
+JS_SetPropertyById(JSContext *cx, JSObject *objArg, jsid id_, jsval *vp)
 {
     RootedId id(cx, id_);
+    Rooted<JSObject*> obj(cx, objArg);
 
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, id);
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING);
-    return obj->setGeneric(cx, id, vp, false);
-}
-
-JS_PUBLIC_API(JSBool)
-JS_SetElement(JSContext *cx, JSObject *obj, uint32_t index, jsval *vp)
-{
+    return obj->setGeneric(cx, obj, id, vp, false);
+}
+
+JS_PUBLIC_API(JSBool)
+JS_SetElement(JSContext *cx, JSObject *obj_, uint32_t index, jsval *vp)
+{
+    RootedObject obj(cx, obj_);
+
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, *vp);
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING);
-    return obj->setElement(cx, index, vp, false);
+    return obj->setElement(cx, obj, index, vp, false);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetProperty(JSContext *cx, JSObject *obj_, const char *name, jsval *vp)
 {
     RootedObject obj(cx, obj_);
     JSAtom *atom = js_Atomize(cx, name, strlen(name));
     return atom && JS_SetPropertyById(cx, obj, AtomToId(atom), vp);
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -481,17 +481,17 @@ SetArrayElement(JSContext *cx, HandleObj
 
     RootedId id(cx);
 
     if (!IndexToId(cx, obj, index, NULL, id.address(), JS_TRUE))
         return JS_FALSE;
     JS_ASSERT(!JSID_IS_VOID(id));
 
     RootedValue tmp(cx, v);
-    return obj->setGeneric(cx, id, tmp.address(), true);
+    return obj->setGeneric(cx, obj, id, tmp.address(), true);
 }
 
 /*
  * Delete the element |index| from |obj|. If |strict|, do a strict
  * deletion: throw if the property is not configurable.
  *
  * - Return 1 if the deletion succeeds (that is, ES5's [[Delete]] would
  *   return true)
@@ -544,22 +544,23 @@ SetOrDeleteArrayElement(JSContext *cx, H
     if (hole) {
         JS_ASSERT(v.isUndefined());
         return DeleteArrayElement(cx, obj, index, true) >= 0;
     }
     return SetArrayElement(cx, obj, index, v);
 }
 
 JSBool
-js_SetLengthProperty(JSContext *cx, JSObject *obj, double length)
+js_SetLengthProperty(JSContext *cx, JSObject *objArg, double length)
 {
     Value v = NumberValue(length);
 
     /* We don't support read-only array length yet. */
-    return obj->setProperty(cx, cx->runtime->atomState.lengthAtom, &v, false);
+    Rooted<JSObject*> obj(cx, objArg);
+    return obj->setProperty(cx, obj, cx->runtime->atomState.lengthAtom, &v, false);
 }
 
 /*
  * Since SpiderMonkey supports cross-class prototype-based delegation, we have
  * to be careful about the length getter and setter being called on an object
  * not of Array class. For the getter, we search obj's prototype chain for the
  * array that caused this getter to be invoked. In the setter case to overcome
  * the JSPROP_SHARED attribute, we must define a shadowing length property.
@@ -869,17 +870,17 @@ array_typeOf(JSContext *cx, HandleObject
 
 static JSBool
 array_setGeneric(JSContext *cx, HandleObject obj, HandleId id, Value *vp, JSBool strict)
 {
     if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom))
         return array_length_setter(cx, obj, id, strict, vp);
 
     if (!obj->isDenseArray())
-        return baseops::SetPropertyHelper(cx, obj, id, 0, vp, strict);
+        return baseops::SetPropertyHelper(cx, obj, obj, id, 0, vp, strict);
 
     do {
         uint32_t i;
         if (!js_IdIsIndex(id, &i))
             break;
         if (js_PrototypeHasIndexedProperties(cx, obj))
             break;
 
@@ -894,17 +895,17 @@ array_setGeneric(JSContext *cx, HandleOb
         if (i >= obj->getArrayLength())
             obj->setDenseArrayLength(i + 1);
         obj->setDenseArrayElementWithType(cx, i, *vp);
         return true;
     } while (false);
 
     if (!JSObject::makeDenseArraySlow(cx, obj))
         return false;
-    return baseops::SetPropertyHelper(cx, obj, id, 0, vp, strict);
+    return baseops::SetPropertyHelper(cx, obj, obj, id, 0, vp, strict);
 }
 
 static JSBool
 array_setProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, Value *vp, JSBool strict)
 {
     Rooted<jsid> id(cx, NameToId(name));
     return array_setGeneric(cx, obj, id, vp, strict);
 }
@@ -912,17 +913,17 @@ array_setProperty(JSContext *cx, HandleO
 static JSBool
 array_setElement(JSContext *cx, HandleObject obj, uint32_t index, Value *vp, JSBool strict)
 {
     RootedId id(cx);
     if (!IndexToId(cx, index, id.address()))
         return false;
 
     if (!obj->isDenseArray())
-        return baseops::SetPropertyHelper(cx, obj, id, 0, vp, strict);
+        return baseops::SetPropertyHelper(cx, obj, obj, id, 0, vp, strict);
 
     do {
         /*
          * UINT32_MAX is not an array index and must not affect the length
          * property, so specifically reject it.
          */
         if (index == UINT32_MAX)
             break;
@@ -940,17 +941,17 @@ array_setElement(JSContext *cx, HandleOb
         if (index >= obj->getArrayLength())
             obj->setDenseArrayLength(index + 1);
         obj->setDenseArrayElementWithType(cx, index, *vp);
         return true;
     } while (false);
 
     if (!JSObject::makeDenseArraySlow(cx, obj))
         return false;
-    return baseops::SetPropertyHelper(cx, obj, id, 0, vp, strict);
+    return baseops::SetPropertyHelper(cx, obj, obj, id, 0, vp, strict);
 }
 
 static JSBool
 array_setSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid, Value *vp, JSBool strict)
 {
     Rooted<jsid> id(cx, SPECIALID_TO_JSID(sid));
     return array_setGeneric(cx, obj, id, vp, strict);
 }
@@ -1793,17 +1794,17 @@ InitArrayElements(JSContext *cx, HandleO
 
     JS_ASSERT(start == MAX_ARRAY_INDEX + 1);
     RootedValue value(cx);
     RootedId id(cx);
     Value idval = DoubleValue(MAX_ARRAY_INDEX + 1);
     do {
         value = *vector++;
         if (!ValueToId(cx, idval, id.address()) ||
-            !obj->setGeneric(cx, id, value.address(), true)) {
+            !obj->setGeneric(cx, obj, id, value.address(), true)) {
             return false;
         }
         idval.getDoubleRef() += 1;
     } while (vector != end);
 
     return true;
 }
 
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -2410,17 +2410,17 @@ BEGIN_CASE(JSOP_ENUMELEM)
     RootedObject &obj = rootObject0;
     RootedValue &rval = rootValue0;
 
     /* Funky: the value to set is under the [obj, id] pair. */
     FETCH_OBJECT(cx, -2, obj);
     RootedId &id = rootId0;
     FETCH_ELEMENT_ID(obj, -1, id);
     rval = regs.sp[-3];
-    if (!obj->setGeneric(cx, id, rval.address(), script->strictModeCode))
+    if (!obj->setGeneric(cx, obj, id, rval.address(), script->strictModeCode))
         goto error;
     regs.sp -= 3;
 }
 END_CASE(JSOP_ENUMELEM)
 
 BEGIN_CASE(JSOP_EVAL)
 {
     CallArgs args = CallArgsFromSp(GET_ARGC(regs.pc), regs.sp);
@@ -2960,17 +2960,17 @@ BEGIN_CASE(JSOP_DEFFUN)
         /*
          * Non-global properties, and global properties which we aren't simply
          * redefining, must be set.  First, this preserves their attributes.
          * Second, this will produce warnings and/or errors as necessary if the
          * specified Call object property is not writable (const).
          */
 
         /* Step 5f. */
-        if (!parent->setProperty(cx, name, rval.address(), script->strictModeCode))
+        if (!parent->setProperty(cx, parent, name, rval.address(), script->strictModeCode))
             goto error;
     } while (false);
 }
 END_CASE(JSOP_DEFFUN)
 
 BEGIN_CASE(JSOP_LAMBDA)
 {
     /* Load the specified function object literal. */
@@ -3167,17 +3167,17 @@ BEGIN_CASE(JSOP_INITPROP)
 
     RootedId &id = rootId0;
 
     PropertyName *name;
     LOAD_NAME(0, name);
     id = NameToId(name);
 
     if (JS_UNLIKELY(name == cx->runtime->atomState.protoAtom)
-        ? !baseops::SetPropertyHelper(cx, obj, id, 0, &rval, script->strictModeCode)
+        ? !baseops::SetPropertyHelper(cx, obj, obj, id, 0, &rval, script->strictModeCode)
         : !DefineNativeProperty(cx, obj, id, rval, NULL, NULL,
                                 JSPROP_ENUMERATE, 0, 0, 0)) {
         goto error;
     }
 
     regs.sp--;
 }
 END_CASE(JSOP_INITPROP);
@@ -3486,21 +3486,21 @@ BEGIN_CASE(JSOP_BINDXMLNAME)
     PUSH_COPY(IdToValue(id));
 }
 END_CASE(JSOP_BINDXMLNAME)
 
 BEGIN_CASE(JSOP_SETXMLNAME)
 {
     JS_ASSERT(!script->strictModeCode);
 
-    JSObject *obj = &regs.sp[-3].toObject();
+    Rooted<JSObject*> obj(cx, &regs.sp[-3].toObject());
     Value rval = regs.sp[-1];
     RootedId &id = rootId0;
     FETCH_ELEMENT_ID(obj, -2, id);
-    if (!obj->setGeneric(cx, id, &rval, script->strictModeCode))
+    if (!obj->setGeneric(cx, obj, id, &rval, script->strictModeCode))
         goto error;
     rval = regs.sp[-1];
     regs.sp -= 2;
     regs.sp[-1] = rval;
 }
 END_CASE(JSOP_SETXMLNAME)
 
 BEGIN_CASE(JSOP_CALLXMLNAME)
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -323,17 +323,17 @@ SetPropertyOperation(JSContext *cx, jsby
 #endif
 
             if (shape->hasDefaultSetter() && shape->hasSlot()) {
                 /* Fast path for, e.g., plain Object instance properties. */
                 obj->nativeSetSlotWithType(cx, shape, rval);
             } else {
                 RootedValue rref(cx, rval);
                 bool strict = cx->stack.currentScript()->strictModeCode;
-                if (!js_NativeSet(cx, obj, shape, false, strict, rref.address()))
+                if (!js_NativeSet(cx, obj, obj, shape, false, strict, rref.address()))
                     return false;
             }
             return true;
         }
 
         GET_NAME_FROM_BYTECODE(cx->stack.currentScript(), pc, 0, name);
     }
 
@@ -342,20 +342,20 @@ SetPropertyOperation(JSContext *cx, jsby
 
     JSOp op = JSOp(*pc);
 
     RootedId id(cx, NameToId(name));
     if (JS_LIKELY(!obj->getOps()->setProperty)) {
         unsigned defineHow = (op == JSOP_SETNAME)
                              ? DNP_CACHE_RESULT | DNP_UNQUALIFIED
                              : DNP_CACHE_RESULT;
-        if (!baseops::SetPropertyHelper(cx, obj, id, defineHow, rref.address(), strict))
+        if (!baseops::SetPropertyHelper(cx, obj, obj, id, defineHow, rref.address(), strict))
             return false;
     } else {
-        if (!obj->setGeneric(cx, id, rref.address(), strict))
+        if (!obj->setGeneric(cx, obj, id, rref.address(), strict))
             return false;
     }
 
     return true;
 }
 
 inline bool
 NameOperation(JSContext *cx, JSScript *script, jsbytecode *pc, Value *vp)
@@ -738,17 +738,17 @@ GetElementOperation(JSContext *cx, JSOp 
         if (!OnUnknownMethod(cx, obj, rref, res))
             return false;
     }
 #endif
     return true;
 }
 
 static JS_ALWAYS_INLINE bool
-SetObjectElementOperation(JSContext *cx, JSObject *obj, HandleId id, const Value &value, bool strict)
+SetObjectElementOperation(JSContext *cx, Handle<JSObject*> obj, HandleId id, const Value &value, bool strict)
 {
     types::TypeScript::MonitorAssign(cx, obj, id);
 
     do {
         if (obj->isDenseArray() && JSID_IS_INT(id)) {
             uint32_t length = obj->getDenseArrayInitializedLength();
             int32_t i = JSID_TO_INT(id);
             if ((uint32_t)i < length) {
@@ -767,17 +767,17 @@ SetObjectElementOperation(JSContext *cx,
 
                 if (script->hasAnalysis())
                     script->analysis()->getCode(pc).arrayWriteHole = true;
             }
         }
     } while (0);
 
     RootedValue tmp(cx, value);
-    return obj->setGeneric(cx, id, tmp.address(), strict);
+    return obj->setGeneric(cx, obj, id, tmp.address(), strict);
 }
 
 #define RELATIONAL_OP(OP)                                                     \
     JS_BEGIN_MACRO                                                            \
         RootedValue lvalRoot(cx, lhs), rvalRoot(cx, rhs);                     \
         Value &lval = lvalRoot.reference();                                   \
         Value &rval = rvalRoot.reference();                                   \
         /* Optimize for two int-tagged operands (typical loop control). */    \
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -5020,18 +5020,18 @@ js_NativeGetInline(JSContext *cx, Handle
 JSBool
 js_NativeGet(JSContext *cx, Handle<JSObject*> obj, Handle<JSObject*> pobj, const Shape *shape,
              unsigned getHow, Value *vp)
 {
     return js_NativeGetInline(cx, obj, obj, pobj, shape, getHow, vp);
 }
 
 JSBool
-js_NativeSet(JSContext *cx, Handle<JSObject*> obj, const Shape *shape, bool added, bool strict,
-             Value *vp)
+js_NativeSet(JSContext *cx, Handle<JSObject*> obj, Handle<JSObject*> receiver,
+             const Shape *shape, bool added, bool strict, Value *vp)
 {
     AddTypePropertyId(cx, obj, shape->propid(), *vp);
 
     JS_ASSERT(obj->isNative());
 
     if (shape->hasSlot()) {
         uint32_t slot = shape->slot();
 
@@ -5049,17 +5049,17 @@ js_NativeSet(JSContext *cx, Handle<JSObj
          */
         if (!shape->hasGetterValue() && shape->hasDefaultSetter())
             return js_ReportGetterOnlyAssignment(cx);
     }
 
     Rooted<const Shape *> shapeRoot(cx, shape);
 
     int32_t sample = cx->runtime->propertyRemovals;
-    if (!shapeRoot->set(cx, obj, strict, vp))
+    if (!shapeRoot->set(cx, obj, receiver, strict, vp))
         return false;
 
     /*
      * Update any slot for the shape with the value produced by the setter,
      * unless the setter deleted the shape.
      */
     if (shapeRoot->hasSlot() &&
         (JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
@@ -5273,18 +5273,18 @@ JSObject::callMethod(JSContext *cx, Hand
 {
     Value fval;
     Rooted<JSObject*> obj(cx, this);
     return GetMethod(cx, obj, id, 0, &fval) &&
            Invoke(cx, ObjectValue(*obj), fval, argc, argv, vp);
 }
 
 JSBool
-baseops::SetPropertyHelper(JSContext *cx, HandleObject obj, HandleId id, unsigned defineHow,
-                           Value *vp, JSBool strict)
+baseops::SetPropertyHelper(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id,
+                           unsigned defineHow, Value *vp, JSBool strict)
 {
     JSObject *pobj;
     JSProperty *prop;
     unsigned attrs, flags;
     int shortid;
     Class *clasp;
     PropertyOp getter;
     StrictPropertyOp setter;
@@ -5305,17 +5305,18 @@ baseops::SetPropertyHelper(JSContext *cx
         if (!pobj->isNative()) {
             if (pobj->isProxy()) {
                 AutoPropertyDescriptorRooter pd(cx);
                 if (!Proxy::getPropertyDescriptor(cx, pobj, id, true, &pd))
                     return false;
 
                 if ((pd.attrs & (JSPROP_SHARED | JSPROP_SHADOWABLE)) == JSPROP_SHARED) {
                     return !pd.setter ||
-                           CallSetter(cx, obj, id, pd.setter, pd.attrs, pd.shortid, strict, vp);
+                           CallSetter(cx, receiver, id, pd.setter, pd.attrs, pd.shortid, strict,
+                                      vp);
                 }
 
                 if (pd.attrs & JSPROP_READONLY) {
                     if (strict)
                         return JSObject::reportReadOnly(cx, id, JSREPORT_ERROR);
                     if (cx->hasStrictOption())
                         return JSObject::reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
                     return true;
@@ -5373,17 +5374,17 @@ baseops::SetPropertyHelper(JSContext *cx
              */
             if (!shape->shadowable()) {
                 if (defineHow & DNP_CACHE_RESULT)
                     JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, pobj, shape);
 
                 if (shape->hasDefaultSetter() && !shape->hasGetterValue())
                     return JS_TRUE;
 
-                return shape->set(cx, obj, strict, vp);
+                return shape->set(cx, obj, receiver, strict, vp);
             }
 
             /*
              * Preserve attrs except JSPROP_SHARED, getter, and setter when
              * shadowing any property that has no slot (is shared). We must
              * clear the shared attribute for the shadowing shape so that the
              * property in obj that it defines has a slot to retain the value
              * being set, in case the setter simply cannot operate on instances
@@ -5454,27 +5455,27 @@ baseops::SetPropertyHelper(JSContext *cx
             return JS_FALSE;
         }
         added = true;
     }
 
     if ((defineHow & DNP_CACHE_RESULT) && !added)
         JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, obj, shape);
 
-    return js_NativeSet(cx, obj, shape, added, strict, vp);
+    return js_NativeSet(cx, obj, receiver, shape, added, strict, vp);
 }
 
 JSBool
-baseops::SetElementHelper(JSContext *cx, HandleObject obj, uint32_t index, unsigned defineHow,
-                          Value *vp, JSBool strict)
+baseops::SetElementHelper(JSContext *cx, HandleObject obj, HandleObject receiver, uint32_t index,
+                          unsigned defineHow, Value *vp, JSBool strict)
 {
     RootedId id(cx);
     if (!IndexToId(cx, index, id.address()))
         return false;
-    return baseops::SetPropertyHelper(cx, obj, id, defineHow, vp, strict);
+    return baseops::SetPropertyHelper(cx, obj, receiver, id, defineHow, vp, strict);
 }
 
 JSBool
 baseops::GetAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp)
 {
     JSObject *nobj;
     JSProperty *prop;
     if (!baseops::LookupProperty(cx, obj, id, &nobj, &prop))
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -146,30 +146,30 @@ GetElement(JSContext *cx, HandleObject o
 {
     return GetElement(cx, obj, obj, index, vp);
 }
 
 extern JSBool
 GetPropertyDefault(JSContext *cx, HandleObject obj, HandleId id, const Value &def, Value *vp);
 
 extern JSBool
-SetPropertyHelper(JSContext *cx, HandleObject obj, HandleId id, unsigned defineHow,
-                  Value *vp, JSBool strict);
+SetPropertyHelper(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id,
+                  unsigned defineHow, Value *vp, JSBool strict);
 
 inline bool
-SetPropertyHelper(JSContext *cx, HandleObject obj, PropertyName *name, unsigned defineHow,
-                  Value *vp, JSBool strict)
+SetPropertyHelper(JSContext *cx, HandleObject obj, HandleObject receiver, PropertyName *name,
+                  unsigned defineHow, Value *vp, JSBool strict)
 {
     Rooted<jsid> id(cx, NameToId(name));
-    return SetPropertyHelper(cx, obj, id, defineHow, vp, strict);
+    return SetPropertyHelper(cx, obj, receiver, id, defineHow, vp, strict);
 }
 
 extern JSBool
-SetElementHelper(JSContext *cx, HandleObject obj, uint32_t index, unsigned defineHow,
-                 Value *vp, JSBool strict);
+SetElementHelper(JSContext *cx, HandleObject obj, HandleObject Receiver, uint32_t index,
+                 unsigned defineHow, Value *vp, JSBool strict);
 
 extern JSType
 TypeOf(JSContext *cx, HandleObject obj);
 
 extern JSBool
 GetAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp);
 
 extern JSBool
@@ -820,20 +820,24 @@ struct JSObject : public js::ObjectImpl
                                       js::Value *vp, bool *present);
     inline JSBool getSpecial(JSContext *cx, js::HandleObject receiver, js::SpecialId sid, js::Value *vp);
 
     inline JSBool getGeneric(JSContext *cx, js::HandleId id, js::Value *vp);
     inline JSBool getProperty(JSContext *cx, js::PropertyName *name, js::Value *vp);
     inline JSBool getElement(JSContext *cx, uint32_t index, js::Value *vp);
     inline JSBool getSpecial(JSContext *cx, js::SpecialId sid, js::Value *vp);
 
-    inline JSBool setGeneric(JSContext *cx, js::HandleId id, js::Value *vp, JSBool strict);
-    inline JSBool setProperty(JSContext *cx, js::PropertyName *name, js::Value *vp, JSBool strict);
-    inline JSBool setElement(JSContext *cx, uint32_t index, js::Value *vp, JSBool strict);
-    inline JSBool setSpecial(JSContext *cx, js::SpecialId sid, js::Value *vp, JSBool strict);
+    inline JSBool setGeneric(JSContext *cx, js::Handle<JSObject*> receiver, js::HandleId id,
+                             js::Value *vp, JSBool strict);
+    inline JSBool setProperty(JSContext *cx, js::Handle<JSObject*> receiver,
+                              js::PropertyName *name, js::Value *vp, JSBool strict);
+    inline JSBool setElement(JSContext *cx, js::Handle<JSObject*> receiver, uint32_t index,
+                             js::Value *vp, JSBool strict);
+    inline JSBool setSpecial(JSContext *cx, js::Handle<JSObject*> receiver, js::SpecialId sid,
+                             js::Value *vp, JSBool strict);
 
     JSBool nonNativeSetProperty(JSContext *cx, js::HandleId id, js::Value *vp, JSBool strict);
     JSBool nonNativeSetElement(JSContext *cx, uint32_t index, js::Value *vp, JSBool strict);
 
     inline JSBool getGenericAttributes(JSContext *cx, js::HandleId id, unsigned *attrsp);
     inline JSBool getPropertyAttributes(JSContext *cx, js::PropertyName *name, unsigned *attrsp);
     inline JSBool getElementAttributes(JSContext *cx, uint32_t index, unsigned *attrsp);
     inline JSBool getSpecialAttributes(JSContext *cx, js::SpecialId sid, unsigned *attrsp);
@@ -1231,18 +1235,18 @@ const unsigned JSGET_CACHE_RESULT = 1; /
  * scope is again locked.  But on failure, both functions return false with the
  * scope containing shape unlocked.
  */
 extern JSBool
 js_NativeGet(JSContext *cx, js::Handle<JSObject*> obj, js::Handle<JSObject*> pobj,
              const js::Shape *shape, unsigned getHow, js::Value *vp);
 
 extern JSBool
-js_NativeSet(JSContext *cx, js::Handle<JSObject*> obj, const js::Shape *shape, bool added,
-             bool strict, js::Value *vp);
+js_NativeSet(JSContext *cx, js::Handle<JSObject*> obj, js::Handle<JSObject*> receiver,
+             const js::Shape *shape, bool added, bool strict, js::Value *vp);
 
 namespace js {
 
 bool
 GetPropertyHelper(JSContext *cx, HandleObject obj, jsid id, uint32_t getHow, Value *vp);
 
 inline bool
 GetPropertyHelper(JSContext *cx, HandleObject obj, PropertyName *name, uint32_t getHow, Value *vp)
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -86,45 +86,49 @@ inline JSObject *
 JSObject::thisObject(JSContext *cx)
 {
     JSObjectOp op = getOps()->thisObject;
     js::Rooted<JSObject*> obj(cx, this);
     return op ? op(cx, obj) : this;
 }
 
 inline JSBool
-JSObject::setGeneric(JSContext *cx, js::HandleId id, js::Value *vp, JSBool strict)
+JSObject::setGeneric(JSContext *cx, js::Handle<JSObject*> receiver, js::HandleId id, js::Value *vp,
+                     JSBool strict)
 {
     if (getOps()->setGeneric)
         return nonNativeSetProperty(cx, id, vp, strict);
     js::Rooted<JSObject*> obj(cx, this);
-    return js::baseops::SetPropertyHelper(cx, obj, id, 0, vp, strict);
+    return js::baseops::SetPropertyHelper(cx, obj, receiver, id, 0, vp, strict);
 }
 
 inline JSBool
-JSObject::setProperty(JSContext *cx, js::PropertyName *name, js::Value *vp, JSBool strict)
+JSObject::setProperty(JSContext *cx, js::Handle<JSObject*> receiver, js::PropertyName *name,
+                      js::Value *vp, JSBool strict)
 {
     js::Rooted<jsid> id(cx, js::NameToId(name));
-    return setGeneric(cx, id, vp, strict);
+    return setGeneric(cx, receiver, id, vp, strict);
 }
 
 inline JSBool
-JSObject::setElement(JSContext *cx, uint32_t index, js::Value *vp, JSBool strict)
+JSObject::setElement(JSContext *cx, js::Handle<JSObject*> receiver, uint32_t index, js::Value *vp,
+                     JSBool strict)
 {
     if (getOps()->setElement)
         return nonNativeSetElement(cx, index, vp, strict);
     js::Rooted<JSObject*> obj(cx, this);
-    return js::baseops::SetElementHelper(cx, obj, index, 0, vp, strict);
+    return js::baseops::SetElementHelper(cx, obj, receiver, index, 0, vp, strict);
 }
 
 inline JSBool
-JSObject::setSpecial(JSContext *cx, js::SpecialId sid, js::Value *vp, JSBool strict)
+JSObject::setSpecial(JSContext *cx, js::HandleObject receiver, js::SpecialId sid, js::Value *vp,
+                     JSBool strict)
 {
     js::Rooted<jsid> id(cx, SPECIALID_TO_JSID(sid));
-    return setGeneric(cx, id, vp, strict);
+    return setGeneric(cx, receiver, id, vp, strict);
 }
 
 inline JSBool
 JSObject::setGenericAttributes(JSContext *cx, js::HandleId id, unsigned *attrsp)
 {
     js::types::MarkTypePropertyConfigured(cx, this, id);
     js::GenericAttributesOp op = getOps()->setGenericAttributes;
     js::Rooted<JSObject*> obj(cx, this);
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -564,21 +564,22 @@ DirectProxyHandler::get(JSContext *cx, J
                         jsid id_, Value *vp)
 {
     RootedObject receiver(cx, receiver_);
     RootedId id(cx, id_);
     return GetProxyTargetObject(proxy)->getGeneric(cx, receiver, id, vp);
 }
 
 bool
-DirectProxyHandler::set(JSContext *cx, JSObject *proxy, JSObject *receiver,
+DirectProxyHandler::set(JSContext *cx, JSObject *proxy, JSObject *receiverArg,
                         jsid id_, bool strict, Value *vp)
 {
     RootedId id(cx, id_);
-    return GetProxyTargetObject(proxy)->setGeneric(cx, id, vp, strict);
+    Rooted<JSObject*> receiver(cx, receiverArg);
+    return GetProxyTargetObject(proxy)->setGeneric(cx, receiver, id, vp, strict);
 }
 
 bool
 DirectProxyHandler::keys(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
     return GetPropertyNames(cx, GetProxyTargetObject(proxy), JSITER_OWNONLY,
                             &props);
 }
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -636,30 +636,30 @@ NodeBuilder::newNode(ASTType type, Token
 bool
 NodeBuilder::newArray(NodeVector &elts, Value *dst)
 {
     const size_t len = elts.length();
     if (len > UINT32_MAX) {
         js_ReportAllocationOverflow(cx);
         return false;
     }
-    JSObject *array = NewDenseAllocatedArray(cx, uint32_t(len));
+    Rooted<JSObject*> array(cx, NewDenseAllocatedArray(cx, uint32_t(len)));
     if (!array)
         return false;
 
     for (size_t i = 0; i < len; i++) {
         Value val = elts[i];
 
         JS_ASSERT_IF(val.isMagic(), val.whyMagic() == JS_SERIALIZE_NO_NODE);
 
         /* Represent "no node" as an array hole by not adding the value. */
         if (val.isMagic(JS_SERIALIZE_NO_NODE))
             continue;
 
-        if (!array->setElement(cx, i, &val, false))
+        if (!array->setElement(cx, array, i, &val, false))
             return false;
     }
 
     dst->setObject(*array);
     return true;
 }
 
 bool
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -696,17 +696,17 @@ struct Shape : public js::gc::Cell
 
     inline bool matches(const Shape *other) const;
     inline bool matches(const StackShape &other) const;
     inline bool matchesParamsAfterId(BaseShape *base,
                                      uint32_t aslot, unsigned aattrs, unsigned aflags,
                                      int ashortid) const;
 
     bool get(JSContext* cx, HandleObject receiver, JSObject *obj, JSObject *pobj, js::Value* vp) const;
-    bool set(JSContext* cx, HandleObject obj, bool strict, js::Value* vp) const;
+    bool set(JSContext* cx, HandleObject obj, HandleObject receiver, bool strict, js::Value* vp) const;
 
     BaseShape *base() const { return base_; }
 
     bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; }
     uint32_t slot() const { JS_ASSERT(hasSlot() && !hasMissingSlot()); return maybeSlot(); }
     uint32_t maybeSlot() const { return slotInfo & SLOT_MASK; }
 
     bool isEmptyShape() const {
--- a/js/src/jsscopeinlines.h
+++ b/js/src/jsscopeinlines.h
@@ -278,23 +278,23 @@ Shape::get(JSContext* cx, HandleObject r
     RootedId id(cx);
     if (!self->getUserId(cx, id.address()))
         return false;
 
     return CallJSPropertyOp(cx, self->getterOp(), receiver, id, vp);
 }
 
 inline bool
-Shape::set(JSContext* cx, HandleObject obj, bool strict, Value* vp) const
+Shape::set(JSContext* cx, HandleObject obj, HandleObject receiver, bool strict, Value* vp) const
 {
     JS_ASSERT_IF(hasDefaultSetter(), hasGetterValue());
 
     if (attrs & JSPROP_SETTER) {
         Value fval = setterValue();
-        return InvokeGetterOrSetter(cx, obj, fval, 1, vp, vp);
+        return InvokeGetterOrSetter(cx, receiver, fval, 1, vp, vp);
     }
 
     if (attrs & JSPROP_GETTER)
         return js_ReportGetterOnlyAssignment(cx);
 
     Rooted<const Shape *> self(cx, this);
     RootedId id(cx);
     if (!self->getUserId(cx, id.address()))
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -541,17 +541,17 @@ ArrayBufferObject::obj_setGeneric(JSCont
         //
         // otherwise __proto__ was a plain property
         // and we don't modify our prototype chain
         // since obj_getProperty will fetch it as a plain
         // property from the delegate.
 
         RootedObject oldDelegateProto(cx, delegate->getProto());
 
-        if (!baseops::SetPropertyHelper(cx, delegate, id, 0, vp, strict))
+        if (!baseops::SetPropertyHelper(cx, delegate, delegate, id, 0, vp, strict))
             return false;
 
         if (delegate->getProto() != oldDelegateProto) {
             // actual __proto__ was set and not a plain property called
             // __proto__
             if (!obj->isExtensible()) {
                 obj->reportNotExtensible(cx);
                 return false;
@@ -562,17 +562,17 @@ ArrayBufferObject::obj_setGeneric(JSCont
                 // restore delegate prototype chain
                 SetProto(cx, delegate, oldDelegateProto, true);
                 return false;
             }
         }
         return true;
     }
 
-    return baseops::SetPropertyHelper(cx, delegate, id, 0, vp, strict);
+    return baseops::SetPropertyHelper(cx, delegate, obj, id, 0, vp, strict);
 }
 
 JSBool
 ArrayBufferObject::obj_setProperty(JSContext *cx, HandleObject obj,
                                    HandlePropertyName name, Value *vp, JSBool strict)
 {
     Rooted<jsid> id(cx, NameToId(name));
     return obj_setGeneric(cx, obj, id, vp, strict);
@@ -581,17 +581,17 @@ ArrayBufferObject::obj_setProperty(JSCon
 JSBool
 ArrayBufferObject::obj_setElement(JSContext *cx, HandleObject obj,
                                   uint32_t index, Value *vp, JSBool strict)
 {
     RootedObject delegate(cx, DelegateObject(cx, obj));
     if (!delegate)
         return false;
 
-    return baseops::SetElementHelper(cx, delegate, index, 0, vp, strict);
+    return baseops::SetElementHelper(cx, delegate, obj, index, 0, vp, strict);
 }
 
 JSBool
 ArrayBufferObject::obj_setSpecial(JSContext *cx, HandleObject obj,
                                   HandleSpecialId sid, Value *vp, JSBool strict)
 {
     Rooted<jsid> id(cx, SPECIALID_TO_JSID(sid));
     return obj_setGeneric(cx, obj, id, vp, strict);
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -4212,17 +4212,17 @@ PutProperty(JSContext *cx, HandleObject 
     } else {
         /*
          * ECMA-357 9.2.1.2/9.1.1.2 qname case.
          */
         nameqn = ToXMLName(cx, IdToJsval(id), funid.address());
         if (!nameqn)
             goto bad;
         if (!JSID_IS_VOID(funid)) {
-            ok = baseops::SetPropertyHelper(cx, obj, funid, 0, vp, false);
+            ok = baseops::SetPropertyHelper(cx, obj, obj, funid, 0, vp, false);
             goto out;
         }
         nameobj = nameqn;
         roots[ID_ROOT] = OBJECT_TO_JSVAL(nameobj);
 
         if (xml->xml_class == JSXML_CLASS_LIST) {
             /*
              * Step 3 of 9.2.1.2.
@@ -6063,28 +6063,28 @@ FindInScopeNamespaces(JSContext *cx, JSX
 
 /*
  * Populate a new JS array with elements of array and place the result into
  * rval.  rval must point to a rooted location.
  */
 static bool
 NamespacesToJSArray(JSContext *cx, JSXMLArray<JSObject> *array, jsval *rval)
 {
-    JSObject *arrayobj = NewDenseEmptyArray(cx);
+    Rooted<JSObject*> arrayobj(cx, NewDenseEmptyArray(cx));
     if (!arrayobj)
         return false;
     *rval = OBJECT_TO_JSVAL(arrayobj);
 
     AutoValueRooter tvr(cx);
     for (uint32_t i = 0, n = array->length; i < n; i++) {
         JSObject *ns = XMLARRAY_MEMBER(array, i, JSObject);
         if (!ns)
             continue;
         tvr.set(ObjectValue(*ns));
-        if (!arrayobj->setElement(cx, i, tvr.addr(), false))
+        if (!arrayobj->setElement(cx, arrayobj, i, tvr.addr(), false))
             return false;
     }
     return true;
 }
 
 static JSBool
 xml_inScopeNamespaces(JSContext *cx, unsigned argc, jsval *vp)
 {
--- a/js/src/methodjit/StubCalls-inl.h
+++ b/js/src/methodjit/StubCalls-inl.h
@@ -24,29 +24,16 @@ ThrowException(VMFrame &f)
 static inline void
 ReportAtomNotDefined(JSContext *cx, JSAtom *atom)
 {
     JSAutoByteString printable;
     if (js_AtomToPrintableString(cx, atom, &printable))
         js_ReportIsNotDefined(cx, printable.ptr());
 }
 
-#define NATIVE_SET(cx,obj,shape,entry,strict,vp)                              \
-    JS_BEGIN_MACRO                                                            \
-        if (shape->hasDefaultSetter() &&                                      \
-            (shape)->hasSlot() &&                                             \
-            !(shape)->isMethod()) {                                           \
-            /* Fast path for, e.g., plain Object instance properties. */      \
-            (obj)->nativeSetSlotWithType(cx, shape, *vp);                     \
-        } else {                                                              \
-            if (!js_NativeSet(cx, obj, shape, false, strict, vp))             \
-                THROW();                                                      \
-        }                                                                     \
-    JS_END_MACRO
-
 #define NATIVE_GET(cx,obj,pobj,shape,getHow,vp,onerr)                         \
     JS_BEGIN_MACRO                                                            \
         if (shape->isDataDescriptor() && shape->hasDefaultGetter()) {         \
             /* Fast path for Object instance properties. */                   \
             JS_ASSERT((shape)->slot() != SHAPE_INVALID_SLOT ||                \
                       !shape->hasDefaultSetter());                            \
             if (((shape)->slot() != SHAPE_INVALID_SLOT))                      \
                 *(vp) = (pobj)->nativeGetSlot((shape)->slot());               \
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -120,20 +120,19 @@ stubs::SetElem(VMFrame &f)
 {
     JSContext *cx = f.cx;
     FrameRegs &regs = f.regs;
 
     Value &objval = regs.sp[-3];
     Value &idval  = regs.sp[-2];
     Value rval    = regs.sp[-1];
 
-    JSObject *obj;
     RootedId id(cx);
 
-    obj = ValueToObject(cx, objval);
+    Rooted<JSObject*> obj(cx, ValueToObject(cx, objval));
     if (!obj)
         THROW();
 
     if (!FetchElementId(f.cx, obj, idval, id.address(), &regs.sp[-2]))
         THROW();
 
     TypeScript::MonitorAssign(cx, obj, id);
 
@@ -151,17 +150,17 @@ stubs::SetElem(VMFrame &f)
                 obj->setDenseArrayElementWithType(cx, i, rval);
                 goto end_setelem;
             } else {
                 if (f.script()->hasAnalysis())
                     f.script()->analysis()->getCode(f.pc()).arrayWriteHole = true;
             }
         }
     } while (0);
-    if (!obj->setGeneric(cx, id, &rval, strict))
+    if (!obj->setGeneric(cx, obj, id, &rval, strict))
         THROW();
   end_setelem:
     /* :FIXME: Moving the assigned object into the lowest stack slot
      * is a temporary hack. What we actually want is an implementation
      * of popAfterSet() that allows popping more than one value;
      * this logic can then be handled in Compiler.cpp. */
     regs.sp[-3] = regs.sp[-1];
 }
@@ -330,17 +329,17 @@ stubs::DefFun(VMFrame &f, JSFunction *fu
                   ? JSPROP_ENUMERATE
                   : JSPROP_ENUMERATE | JSPROP_PERMANENT;
 
     /*
      * We define the function as a property of the variable object and not the
      * current scope chain even for the case of function expression statements
      * and functions defined by eval inside let or with blocks.
      */
-    JSObject *parent = &fp->varObj();
+    Rooted<JSObject*> parent(cx, &fp->varObj());
 
     /* ES5 10.5 (NB: with subsequent errata). */
     PropertyName *name = fun->atom->asPropertyName();
     JSProperty *prop = NULL;
     JSObject *pobj;
     if (!parent->lookupProperty(cx, name, &pobj, &prop))
         THROW();
 
@@ -383,17 +382,17 @@ stubs::DefFun(VMFrame &f, JSFunction *fu
         /*
          * Non-global properties, and global properties which we aren't simply
          * redefining, must be set.  First, this preserves their attributes.
          * Second, this will produce warnings and/or errors as necessary if the
          * specified Call object property is not writable (const).
          */
 
         /* Step 5f. */
-        if (!parent->setProperty(cx, name, &rval, strict))
+        if (!parent->setProperty(cx, parent, name, &rval, strict))
             THROW();
     } while (false);
 }
 
 template void JS_FASTCALL stubs::DefFun<true>(VMFrame &f, JSFunction *fun);
 template void JS_FASTCALL stubs::DefFun<false>(VMFrame &f, JSFunction *fun);
 
 #define RELATIONAL(OP)                                                        \
@@ -1030,17 +1029,17 @@ InitPropOrMethod(VMFrame &f, PropertyNam
     /* Load the object being initialized into lval/obj. */
     RootedObject obj(cx, &regs.sp[-2].toObject());
     JS_ASSERT(obj->isNative());
 
     /* Get the immediate property name into id. */
     RootedId id(cx, NameToId(name));
 
     if (JS_UNLIKELY(name == cx->runtime->atomState.protoAtom)
-        ? !baseops::SetPropertyHelper(cx, obj, id, 0, &rval, false)
+        ? !baseops::SetPropertyHelper(cx, obj, obj, id, 0, &rval, false)
         : !DefineNativeProperty(cx, obj, id, rval, NULL, NULL,
                                 JSPROP_ENUMERATE, 0, 0, 0)) {
         THROW();
     }
 }
 
 void JS_FASTCALL
 stubs::InitProp(VMFrame &f, PropertyName *name)
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -311,17 +311,17 @@ StrictArgSetter(JSContext *cx, HandleObj
     /*
      * For simplicity we use delete/set to replace the property with one
      * backed by the default Object getter and setter. Note that we rely on
      * args_delProperty to clear the corresponding reserved slot so the GC can
      * collect its value.
      */
     RootedValue value(cx);
     return baseops::DeleteGeneric(cx, argsobj, id, value.address(), strict) &&
-           baseops::SetPropertyHelper(cx, argsobj, id, 0, vp, strict);
+           baseops::SetPropertyHelper(cx, argsobj, argsobj, id, 0, vp, strict);
 }
 
 static JSBool
 strictargs_resolve(JSContext *cx, HandleObject obj, HandleId id, unsigned flags, JSObject **objp)
 {
     *objp = NULL;
 
     Rooted<StrictArgumentsObject*> argsobj(cx, &obj->asStrictArguments());
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -4485,17 +4485,17 @@ DebuggerEnv_setVariable(JSContext *cx, u
         if (!env->hasProperty(cx, id, &has))
             return false;
         if (!has) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_VARIABLE_NOT_FOUND);
             return false;
         }
 
         /* Just set the property. */
-        if (!env->setGeneric(cx, id, v.address(), true))
+        if (!env->setGeneric(cx, env, id, v.address(), true))
             return false;
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 static JSPropertySpec DebuggerEnv_properties[] = {
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -406,35 +406,39 @@ with_GetSpecial(JSContext *cx, HandleObj
 {
     Rooted<jsid> id(cx, SPECIALID_TO_JSID(sid));
     return with_GetGeneric(cx, obj, receiver, id, vp);
 }
 
 static JSBool
 with_SetGeneric(JSContext *cx, HandleObject obj, HandleId id, Value *vp, JSBool strict)
 {
-    return obj->asWith().object().setGeneric(cx, id, vp, strict);
+    Rooted<JSObject*> actual(cx, &obj->asWith().object());
+    return actual->setGeneric(cx, actual, id, vp, strict);
 }
 
 static JSBool
 with_SetProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, Value *vp, JSBool strict)
 {
-    return obj->asWith().object().setProperty(cx, name, vp, strict);
+    Rooted<JSObject*> actual(cx, &obj->asWith().object());
+    return actual->setProperty(cx, actual, name, vp, strict);
 }
 
 static JSBool
 with_SetElement(JSContext *cx, HandleObject obj, uint32_t index, Value *vp, JSBool strict)
 {
-    return obj->asWith().object().setElement(cx, index, vp, strict);
+    Rooted<JSObject*> actual(cx, &obj->asWith().object());
+    return actual->setElement(cx, actual, index, vp, strict);
 }
 
 static JSBool
 with_SetSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid, Value *vp, JSBool strict)
 {
-    return obj->asWith().object().setSpecial(cx, sid, vp, strict);
+    Rooted<JSObject*> actual(cx, &obj->asWith().object());
+    return actual->setSpecial(cx, actual, sid, vp, strict);
 }
 
 static JSBool
 with_GetGenericAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp)
 {
     return obj->asWith().object().getGenericAttributes(cx, id, attrsp);
 }
 
@@ -1337,23 +1341,23 @@ class DebugScopeProxy : public BaseProxy
         Rooted<ScopeObject*> scopeObj(cx, &scope);
         Rooted<jsid> idRoot(cx, id);
         return scope.getGeneric(cx, scopeObj, idRoot, vp);
     }
 
     bool set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id_, bool strict,
                      Value *vp) MOZ_OVERRIDE
     {
-        ScopeObject &scope = proxy->asDebugScope().scope();
+        Rooted<ScopeObject*> scope(cx, &proxy->asDebugScope().scope());
 
-        if (handleUnaliasedAccess(cx, scope, id_, SET, vp))
+        if (handleUnaliasedAccess(cx, *scope, id_, SET, vp))
             return true;
 
         Rooted<jsid> id(cx, id_);
-        return scope.setGeneric(cx, id, vp, strict);
+        return scope->setGeneric(cx, scope, id, vp, strict);
     }
 
     bool defineProperty(JSContext *cx, JSObject *proxy, jsid id, PropertyDescriptor *desc) MOZ_OVERRIDE
     {
         bool found;
         if (!has(cx, proxy, id, &found))
             return false;
         if (found)