Bug 930414 - Replace |thisObject| object op with |thisValue| and use if for modules r=shu r=smaug
authorJon Coppeard <jcoppeard@mozilla.com>
Wed, 21 Oct 2015 10:21:44 +0100
changeset 302159 c7fc7f42fddbed3298db3875c6b4e37c8fe04d67
parent 302158 75365568c45bae97207fb35d49d1f182eae7bde6
child 302160 a8986b136f8d7e2f05d02c7ebb85d8f72add135a
push idunknown
push userunknown
push dateunknown
reviewersshu, smaug
bugs930414
milestone44.0a1
Bug 930414 - Replace |thisObject| object op with |thisValue| and use if for modules r=shu r=smaug
dom/bindings/BindingUtils.cpp
dom/bindings/BindingUtils.h
dom/bindings/Codegen.py
js/public/Class.h
js/src/builtin/Eval.cpp
js/src/jit/BaselineIC.cpp
js/src/jit/CodeGenerator.cpp
js/src/jsobj.h
js/src/vm/Debugger.cpp
js/src/vm/Interpreter-inl.h
js/src/vm/Interpreter.cpp
js/src/vm/Interpreter.h
js/src/vm/ScopeObject.cpp
js/src/vm/ScopeObject.h
js/xpconnect/src/XPCWrappedNativeJSOps.cpp
js/xpconnect/src/xpcprivate.h
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -3163,10 +3163,18 @@ DeprecationWarning(JSContext* aCx, JSObj
   }
 
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(global.GetAsSupports());
   if (window && window->GetExtantDoc()) {
     window->GetExtantDoc()->WarnOnceAbout(aOperation);
   }
 }
 
+bool
+ObjectToOuterObjectValue(JSContext* cx, JS::Handle<JSObject*> obj, JS::MutableHandle<JS::Value> vp)
+{
+  JSObject* outer = JS_ObjectToOuterObject(cx, obj);
+  vp.setObject(*outer);
+  return true;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -784,16 +784,19 @@ TryToOuterize(JSContext* cx, JS::Mutable
     }
 
     rval.set(JS::ObjectValue(*obj));
   }
 
   return true;
 }
 
+bool
+ObjectToOuterObjectValue(JSContext* cx, JS::Handle<JSObject*> obj, JS::MutableHandle<JS::Value> vp);
+
 // Make sure to wrap the given string value into the right compartment, as
 // needed.
 MOZ_ALWAYS_INLINE
 bool
 MaybeWrapStringValue(JSContext* cx, JS::MutableHandle<JS::Value> rval)
 {
   MOZ_ASSERT(rval.isString());
   JSString* str = rval.toString();
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -448,17 +448,17 @@ class CGDOMJSClass(CGThing):
                       nullptr, /* getProperty */
                       nullptr, /* setProperty */
                       nullptr, /* getOwnPropertyDescriptor */
                       nullptr, /* deleteProperty */
                       nullptr, /* watch */
                       nullptr, /* unwatch */
                       nullptr, /* getElements */
                       nullptr, /* enumerate */
-                      JS_ObjectToOuterObject /* thisObject */
+                      mozilla::dom::ObjectToOuterObjectValue /* thisValue */
                     }
                     """,
                     objectMoved=objectMovedHook)
         else:
             classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount
             reservedSlots = slotCount
         if self.descriptor.interface.getExtendedAttribute("NeedResolve"):
             resolveHook = RESOLVE_HOOK_NAME
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -422,16 +422,19 @@ typedef bool
                      JS::ObjectOpResult& result);
 
 typedef bool
 (* WatchOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable);
 
 typedef bool
 (* UnwatchOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id);
 
+typedef bool
+(* ThisValueOp)(JSContext* cx, JS::HandleObject obj, JS::MutableHandleValue vp);
+
 class JS_FRIEND_API(ElementAdder)
 {
   public:
     enum GetBehavior {
         // Check if the element exists before performing the Get and preserve
         // holes.
         CheckHasElemPreserveHoles,
 
@@ -641,17 +644,17 @@ struct ObjectOps
     GetPropertyOp       getProperty;
     SetPropertyOp       setProperty;
     GetOwnPropertyOp    getOwnPropertyDescriptor;
     DeletePropertyOp    deleteProperty;
     WatchOp             watch;
     UnwatchOp           unwatch;
     GetElementsOp       getElements;
     JSNewEnumerateOp    enumerate;
-    ObjectOp            thisObject;
+    ThisValueOp         thisValue;
 };
 
 #define JS_NULL_OBJECT_OPS                                                    \
     {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,  \
      nullptr, nullptr, nullptr, nullptr}
 
 } // namespace js
 
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -269,20 +269,18 @@ EvalKernel(JSContext* cx, const CallArgs
         // the eval code to use.
         if (!ComputeThis(cx, caller))
             return false;
         thisv = caller.thisValue();
     } else {
         MOZ_ASSERT(args.callee().global() == scopeobj->as<ClonedBlockObject>().global());
 
         // Use the global as 'this', modulo outerization.
-        JSObject* thisobj = GetThisObject(cx, scopeobj);
-        if (!thisobj)
+        if (!GetThisValue(cx, scopeobj, &thisv))
             return false;
-        thisv = ObjectValue(*thisobj);
     }
 
     RootedLinearString linearStr(cx, str->ensureLinear(cx));
     if (!linearStr)
         return false;
 
     RootedScript callerScript(cx, caller ? caller.script() : nullptr);
     EvalJSONResult ejr = TryEvalJSON(cx, linearStr, args.rval());
@@ -436,20 +434,18 @@ js::DirectEvalStringFromIon(JSContext* c
     MOZ_ASSERT(thisValue.isObject() || thisValue.isUndefined() || thisValue.isNull());
 
     // When eval'ing strict code in a non-strict context, compute the 'this'
     // value to use from what the caller passed in. This isn't necessary if
     // the callee is not strict, as it will compute the non-strict 'this'
     // value as necessary while it executes.
     RootedValue nthisValue(cx, thisValue);
     if (!callerScript->strict() && esg.script()->strict() && !thisValue.isObject()) {
-        JSObject* obj = BoxNonStrictThis(cx, thisValue);
-        if (!obj)
+        if (!BoxNonStrictThis(cx, thisValue, &nthisValue))
             return false;
-        nthisValue = ObjectValue(*obj);
     }
 
     return ExecuteKernel(cx, esg.script(), *scopeobj, nthisValue, newTargetValue,
                          ExecuteType(DIRECT_EVAL), NullFramePtr() /* evalInFrame */, vp.address());
 }
 
 bool
 js::IndirectEval(JSContext* cx, unsigned argc, Value* vp)
@@ -515,21 +511,20 @@ js::ExecuteInGlobalAndReturnScope(JSCont
 
     // Unlike the non-syntactic scope chain API used by the subscript loader,
     // this API creates a fresh block scope each time.
     RootedObject enclosingStaticScope(cx, script->enclosingStaticScope());
     scope = ClonedBlockObject::createNonSyntactic(cx, enclosingStaticScope, scope);
     if (!scope)
         return false;
 
-    JSObject* thisobj = GetThisObject(cx, global);
-    if (!thisobj)
+    RootedValue thisv(cx);
+    if (!GetThisValue(cx, global, &thisv))
         return false;
 
-    RootedValue thisv(cx, ObjectValue(*thisobj));
     RootedValue rval(cx);
     if (!ExecuteKernel(cx, script, *scope, thisv, UndefinedValue(), EXECUTE_GLOBAL,
                        NullFramePtr() /* evalInFrame */, rval.address()))
     {
         return false;
     }
 
     scopeArg.set(scope);
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -938,23 +938,17 @@ static const VMFunction DoCallNativeGett
 //
 // This_Fallback
 //
 
 static bool
 DoThisFallback(JSContext* cx, ICThis_Fallback* stub, HandleValue thisv, MutableHandleValue ret)
 {
     FallbackICSpew(cx, stub, "This");
-
-    JSObject* thisObj = BoxNonStrictThis(cx, thisv);
-    if (!thisObj)
-        return false;
-
-    ret.setObject(*thisObj);
-    return true;
+    return BoxNonStrictThis(cx, thisv, ret);
 }
 
 typedef bool (*DoThisFallbackFn)(JSContext*, ICThis_Fallback*, HandleValue, MutableHandleValue);
 static const VMFunction DoThisFallbackInfo = FunctionInfo<DoThisFallbackFn>(DoThisFallback, TailCall);
 
 bool
 ICThis_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
 {
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -5084,32 +5084,30 @@ CodeGenerator::visitReturnFromCtor(LRetu
     masm.bind(&valueIsObject);
     Register payload = masm.extractObject(value, output);
     if (payload != output)
         masm.movePtr(payload, output);
 
     masm.bind(&end);
 }
 
-typedef JSObject* (*BoxNonStrictThisFn)(JSContext*, HandleValue);
+typedef bool (*BoxNonStrictThisFn)(JSContext*, HandleValue, MutableHandleValue);
 static const VMFunction BoxNonStrictThisInfo = FunctionInfo<BoxNonStrictThisFn>(BoxNonStrictThis);
 
 void
 CodeGenerator::visitComputeThis(LComputeThis* lir)
 {
     ValueOperand value = ToValue(lir, LComputeThis::ValueIndex);
     Register output = ToRegister(lir->output());
 
-    OutOfLineCode* ool = oolCallVM(BoxNonStrictThisInfo, lir, ArgList(value),
-                                   StoreRegisterTo(output));
+    OutOfLineCode* ool = oolCallVM(BoxNonStrictThisInfo, lir, ArgList(value), StoreValueTo(value));
 
     masm.branchTestObject(Assembler::NotEqual, value, ool->entry());
+    masm.bind(ool->rejoin());
     masm.unboxObject(value, output);
-
-    masm.bind(ool->rejoin());
 }
 
 void
 CodeGenerator::visitLoadArrowThis(LLoadArrowThis* lir)
 {
     Register callee = ToRegister(lir->callee());
     ValueOperand output = ToOutValue(lir);
     masm.loadValue(Address(callee, FunctionExtended::offsetOfArrowThisSlot()), output);
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1033,17 +1033,17 @@ ToPrimitive(JSContext* cx, JSType prefer
  * <windows.h> with that name.)
  */
 extern const char*
 GetObjectClassName(JSContext* cx, HandleObject obj);
 
 /*
  * Inner and outer objects
  *
- * GetInnerObject and GetOuterObject (and also GetThisObject, somewhat) have to
+ * GetInnerObject and GetOuterObject (and also GetThisValue, somewhat) have to
  * do with Windows and WindowProxies. There's a screwy invariant that actual
  * Window objects (the global objects of web pages) are never directly exposed
  * to script. Instead we often substitute a WindowProxy.
  *
  * As a result, we have calls to these three "substitute-this-object-for-that-
  * object" functions sprinkled at apparently arbitrary (but actually *very*
  * carefully and nervously selected) places throughout the engine and indeed
  * the universe.
@@ -1073,43 +1073,45 @@ GetInnerObject(JSObject* obj)
 }
 
 /*
  * If obj is a Window object, return the WindowProxy. Otherwise return obj.
  * This function can't fail; it never sets an exception or returns nullptr.
  *
  * This must be called before passing an object to script, if the object might
  * be a Window. (But usually those cases involve scope objects, and for those,
- * it is better to call GetThisObject instead.)
+ * it is better to call GetThisValue instead.)
  */
 inline JSObject*
 GetOuterObject(JSContext* cx, HandleObject obj)
 {
     if (ObjectOp op = obj->getClass()->ext.outerObject)
         return op(cx, obj);
     return obj;
 }
 
 /*
  * Return an object that may be used as `this` in place of obj. For most
  * objects this just returns obj.
  *
  * Some JSObjects shouldn't be exposed directly to script. This includes (at
  * least) DynamicWithObjects and Window objects. However, since both of those
  * can be on scope chains, we sometimes would expose those as `this` if we
- * were not so vigilant about calling GetThisObject where appropriate.
+ * were not so vigilant about calling GetThisValue where appropriate.
  *
  * See comments at ComputeImplicitThis.
  */
-inline JSObject*
-GetThisObject(JSContext* cx, HandleObject obj)
+inline bool
+GetThisValue(JSContext* cx, HandleObject obj, MutableHandleValue vp)
 {
-    if (ObjectOp op = obj->getOps()->thisObject)
-        return op(cx, obj);
-    return obj;
+    if (ThisValueOp op = obj->getOps()->thisValue)
+        return op(cx, obj, vp);
+
+    vp.setObject(*obj);
+    return true;
 }
 
 
 /* * */
 
 typedef JSObject* (*ClassInitializerOp)(JSContext* cx, JS::HandleObject obj);
 
 /* Fast access to builtin constructors and prototypes. */
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -6710,20 +6710,18 @@ DebuggerGenericEval(JSContext* cx, const
         if (!env)
             return false;
     } else {
         /*
          * Use the global lexical scope as 'this'. If the global is an inner
          * object, it should have a thisObject hook that returns the
          * appropriate outer object.
          */
-        JSObject* thisObj = GetThisObject(cx, scope);
-        if (!thisObj)
-            return false;
-        thisv = ObjectValue(*thisObj);
+        if (!GetThisValue(cx, scope, &thisv))
+            return false;
         env = scope;
     }
 
     /* If evalWithBindings, create the inner environment. */
     if (evalWithBindings) {
         RootedPlainObject nenv(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr));
         if (!nenv)
             return false;
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -58,21 +58,21 @@ ComputeThis(JSContext* cx, AbstractFrame
          * this, we always wrap a function's |this| before pushing an eval frame, and should
          * thus never see an unwrapped primitive in a non-strict eval function frame. Null
          * and undefined |this| values will unwrap to the same object in the function and
          * eval frames, so are not required to be wrapped.
          */
         MOZ_ASSERT_IF(frame.isEvalFrame(), thisv.isUndefined() || thisv.isNull());
     }
 
-    JSObject* thisObj = BoxNonStrictThis(cx, thisv);
-    if (!thisObj)
+    RootedValue result(cx);
+    if (!BoxNonStrictThis(cx, thisv, &result))
         return false;
 
-    frame.thisValue().setObject(*thisObj);
+    frame.thisValue() = result;
     return true;
 }
 
 /*
  * Every possible consumer of MagicValue(JS_OPTIMIZED_ARGUMENTS) (as determined
  * by ScriptAnalysis::needsArgsObj) must check for these magic values and, when
  * one is received, act as if the value were the function's ArgumentsObject.
  * Additionally, it is possible that, after 'arguments' was copied into a
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -79,34 +79,37 @@ LooseEqualityOp(JSContext* cx, Interpret
     if (!LooselyEqual(cx, lval, rval, &cond))
         return false;
     cond = (cond == Eq);
     regs.sp--;
     regs.sp[-1].setBoolean(cond);
     return true;
 }
 
-JSObject*
-js::BoxNonStrictThis(JSContext* cx, HandleValue thisv)
+bool
+js::BoxNonStrictThis(JSContext* cx, HandleValue thisv, MutableHandleValue vp)
 {
     /*
      * Check for SynthesizeFrame poisoning and fast constructors which
      * didn't check their callee properly.
      */
     MOZ_ASSERT(!thisv.isMagic());
 
     if (thisv.isNullOrUndefined()) {
         Rooted<GlobalObject*> global(cx, cx->global());
-        return GetThisObject(cx, global);
+        return GetThisValue(cx, global, vp);
     }
 
-    if (thisv.isObject())
-        return &thisv.toObject();
-
-    return PrimitiveToObject(cx, thisv);
+    if (thisv.isObject()) {
+        vp.set(thisv);
+        return true;
+    }
+
+    vp.setObject(*PrimitiveToObject(cx, thisv));
+    return true;
 }
 
 /*
  * ECMA requires "the global object", but in embeddings such as the browser,
  * which have multiple top-level objects (windows, frames, etc. in the DOM),
  * we prefer fun's parent.  An example that causes this code to run:
  *
  *   // in window w1
@@ -128,22 +131,17 @@ js::BoxNonStrictThis(JSContext* cx, cons
      */
     MOZ_ASSERT(!call.thisv().isMagic());
 
 #ifdef DEBUG
     JSFunction* fun = call.callee().is<JSFunction>() ? &call.callee().as<JSFunction>() : nullptr;
     MOZ_ASSERT_IF(fun && fun->isInterpreted(), !fun->strict());
 #endif
 
-    JSObject* thisObj = BoxNonStrictThis(cx, call.thisv());
-    if (!thisObj)
-        return false;
-
-    call.setThis(ObjectValue(*thisObj));
-    return true;
+    return BoxNonStrictThis(cx, call.thisv(), call.mutableThisv());
 }
 
 #if JS_HAS_NO_SUCH_METHOD
 
 static const uint32_t JSSLOT_FOUND_FUNCTION = 0;
 static const uint32_t JSSLOT_SAVED_ID = 1;
 
 static const Class js_NoSuchMethodClass = {
@@ -824,20 +822,18 @@ js::Invoke(JSContext* cx, const Value& t
          * |this| already.  But don't do that if fval is a DOM function.
          */
         if (!fval.isObject() || !fval.toObject().is<JSFunction>() ||
             !fval.toObject().as<JSFunction>().isNative() ||
             !fval.toObject().as<JSFunction>().jitInfo() ||
             fval.toObject().as<JSFunction>().jitInfo()->needsOuterizedThisObject())
         {
             RootedObject thisObj(cx, &args.thisv().toObject());
-            JSObject* thisp = GetThisObject(cx, thisObj);
-            if (!thisp)
+            if (!GetThisValue(cx, thisObj, args.mutableThisv()))
                 return false;
-            args.setThis(ObjectValue(*thisp));
         }
     }
 
     if (!Invoke(cx, args))
         return false;
 
     rval.set(args.rval());
     return true;
@@ -1024,30 +1020,21 @@ js::Execute(JSContext* cx, HandleScript 
 #ifdef DEBUG
     JSObject* s = scopeChain;
     do {
         assertSameCompartment(cx, s);
         MOZ_ASSERT_IF(!s->enclosingScope(), s->is<GlobalObject>());
     } while ((s = s->enclosingScope()));
 #endif
 
-    ExecuteType type;
-    Value thisv;
-    if (script->module()) {
-        type = EXECUTE_MODULE;
-        thisv = UndefinedValue();
-    } else {
-        type = EXECUTE_GLOBAL;
-
-        /* Use the scope chain as 'this', modulo outerization. */
-        JSObject* thisObj = GetThisObject(cx, scopeChain);
-        if (!thisObj)
-            return false;
-        thisv = ObjectValue(*thisObj);
-    }
+    ExecuteType type = script->module() ? EXECUTE_MODULE : EXECUTE_GLOBAL;
+
+    RootedValue thisv(cx);
+    if (!GetThisValue(cx, scopeChain, &thisv))
+        return false;
 
     return ExecuteKernel(cx, script, *scopeChain, thisv, NullValue(), type,
                          NullFramePtr() /* evalInFrame */, rval);
 }
 
 bool
 js::HasInstance(JSContext* cx, HandleObject obj, HandleValue v, bool* bp)
 {
@@ -1604,17 +1591,17 @@ JS_STATIC_ASSERT(JSOP_IFNE == JSOP_IFEQ 
  * |this| value, undefined, if either of these conditions hold:
  *
  * 1. The nominal |this|, obj, is a global object.
  *
  * 2. The nominal |this|, obj, has one of Block, Call, or DeclEnv class (this
  *    is what IsCacheableNonGlobalScope tests). Such objects-as-scopes must be
  *    censored with undefined.
  *
- * Otherwise, we bind |this| to GetThisObject(cx, obj). Only names inside
+ * Otherwise, we bind |this| to the result of GetThisValue(). Only names inside
  * |with| statements and embedding-specific scope objects fall into this
  * category.
  *
  * If the callee is a strict mode function, then code implementing JSOP_THIS
  * in the interpreter and JITs will leave undefined as |this|. If funval is a
  * function not in strict mode, JSOP_THIS code replaces undefined with funval's
  * global.
  *
@@ -1627,22 +1614,17 @@ ComputeImplicitThis(JSContext* cx, Handl
     vp.setUndefined();
 
     if (IsGlobalLexicalScope(obj))
         return true;
 
     if (IsCacheableNonGlobalScope(obj))
         return true;
 
-    JSObject* nobj = GetThisObject(cx, obj);
-    if (!nobj)
-        return false;
-
-    vp.setObject(*nobj);
-    return true;
+    return GetThisValue(cx, obj, vp);
 }
 
 static MOZ_ALWAYS_INLINE bool
 AddOperation(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res)
 {
     if (lhs.isInt32() && rhs.isInt32()) {
         int32_t l = lhs.toInt32(), r = rhs.toInt32();
         int32_t t;
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -26,18 +26,18 @@ class ScopeIter;
  * For a given |call|, convert null/undefined |this| into the global object for
  * the callee and replace other primitives with boxed versions. This assumes
  * that call.callee() is not strict mode code. This is the special/slow case of
  * ComputeThis.
  */
 extern bool
 BoxNonStrictThis(JSContext* cx, const CallReceiver& call);
 
-extern JSObject*
-BoxNonStrictThis(JSContext* cx, HandleValue thisv);
+extern bool
+BoxNonStrictThis(JSContext* cx, HandleValue thisv, MutableHandleValue vp);
 
 /*
  * Ensure that fp->thisValue() is the correct value of |this| for the scripted
  * call represented by |fp|. ComputeThis is necessary because fp->thisValue()
  * may be set to 'undefined' when 'this' should really be the global object (as
  * an optimization to avoid global-this computation).
  */
 inline bool
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -339,17 +339,17 @@ const Class ModuleEnvironmentObject::cla
         ModuleEnvironmentObject::hasProperty,
         ModuleEnvironmentObject::getProperty,
         ModuleEnvironmentObject::setProperty,
         ModuleEnvironmentObject::getOwnPropertyDescriptor,
         ModuleEnvironmentObject::deleteProperty,
         nullptr, nullptr,                                    /* watch/unwatch */
         nullptr,                                             /* getElements */
         ModuleEnvironmentObject::enumerate,
-        nullptr                                              /* thisObject */
+        ModuleEnvironmentObject::thisValue
     }
 };
 
 /* static */ ModuleEnvironmentObject*
 ModuleEnvironmentObject::create(ExclusiveContext* cx, HandleModuleObject module)
 {
     RootedScript script(cx, module->script());
     RootedShape shape(cx, script->bindings.callObjShape());
@@ -514,16 +514,23 @@ ModuleEnvironmentObject::enumerate(JSCon
 
     for (Shape::Range<NoGC> r(self->lastProperty()); !r.empty(); r.popFront())
         properties.infallibleAppend(r.front().propid());
 
     MOZ_ASSERT(properties.length() == count);
     return true;
 }
 
+/* static */ bool
+ModuleEnvironmentObject::thisValue(JSContext* cx, HandleObject obj, MutableHandleValue vp)
+{
+    vp.setUndefined();
+    return true;
+}
+
 /*****************************************************************************/
 
 const Class DeclEnvObject::class_ = {
     js_Object_str,
     JSCLASS_HAS_RESERVED_SLOTS(DeclEnvObject::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Object)
 };
 
@@ -621,23 +628,23 @@ DynamicWithObject::create(JSContext* cx,
 
     Rooted<TaggedProto> proto(cx, TaggedProto(staticWith));
     Rooted<DynamicWithObject*> obj(cx);
     obj = NewObjectWithGivenTaggedProto<DynamicWithObject>(cx, proto, GenericObject,
                                                            BaseShape::DELEGATE);
     if (!obj)
         return nullptr;
 
-    JSObject* thisp = GetThisObject(cx, object);
-    if (!thisp)
+    RootedValue thisv(cx);
+    if (!GetThisValue(cx, object, &thisv))
         return nullptr;
 
     obj->setEnclosingScope(enclosing);
     obj->setFixedSlot(OBJECT_SLOT, ObjectValue(*object));
-    obj->setFixedSlot(THIS_SLOT, ObjectValue(*thisp));
+    obj->setFixedSlot(THIS_SLOT, thisv);
     obj->setFixedSlot(KIND_SLOT, Int32Value(kind));
 
     return obj;
 }
 
 static bool
 with_LookupProperty(JSContext* cx, HandleObject obj, HandleId id,
                     MutableHandleObject objp, MutableHandleShape propp)
@@ -693,20 +700,21 @@ with_GetOwnPropertyDescriptor(JSContext*
 
 static bool
 with_DeleteProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result)
 {
     RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
     return DeleteProperty(cx, actual, id, result);
 }
 
-static JSObject*
-with_ThisObject(JSContext* cx, HandleObject obj)
+static bool
+with_ThisValue(JSContext* cx, HandleObject obj, MutableHandleValue vp)
 {
-    return &obj->as<DynamicWithObject>().withThis();
+    vp.set(obj->as<DynamicWithObject>().withThis());
+    return true;
 }
 
 const Class StaticWithObject::class_ = {
     "WithTemplate",
     JSCLASS_HAS_RESERVED_SLOTS(StaticWithObject::RESERVED_SLOTS) |
     JSCLASS_IS_ANONYMOUS
 };
 
@@ -734,17 +742,17 @@ const Class DynamicWithObject::class_ = 
         with_HasProperty,
         with_GetProperty,
         with_SetProperty,
         with_GetOwnPropertyDescriptor,
         with_DeleteProperty,
         nullptr, nullptr,    /* watch/unwatch */
         nullptr,             /* getElements */
         nullptr,             /* enumerate (native enumeration of target doesn't work) */
-        with_ThisObject,
+        with_ThisValue,
     }
 };
 
 /* static */ StaticEvalObject*
 StaticEvalObject::create(JSContext* cx, HandleObject enclosing)
 {
     StaticEvalObject* obj =
         NewObjectWithNullTaggedProto<StaticEvalObject>(cx, TenuredObject, BaseShape::DELEGATE);
@@ -1000,27 +1008,27 @@ StaticBlockObject::addVar(ExclusiveConte
                                              /* setter = */ nullptr,
                                              slot,
                                              propFlags,
                                              /* attrs = */ 0,
                                              entry,
                                              /* allowDictionary = */ false);
 }
 
-static JSObject*
-block_ThisObject(JSContext* cx, HandleObject obj)
+static bool
+block_ThisValue(JSContext* cx, HandleObject obj, MutableHandleValue vp)
 {
     // No other block objects should ever get passed to the 'this' object
     // hook except the global lexical scope and non-syntactic ones.
     MOZ_ASSERT(obj->as<ClonedBlockObject>().isGlobal() ||
                !obj->as<ClonedBlockObject>().isSyntactic());
     MOZ_ASSERT_IF(obj->as<ClonedBlockObject>().isGlobal(),
                   obj->enclosingScope() == cx->global());
     RootedObject enclosing(cx, obj->enclosingScope());
-    return GetThisObject(cx, enclosing);
+    return GetThisValue(cx, enclosing, vp);
 }
 
 const Class BlockObject::class_ = {
     "Block",
     JSCLASS_HAS_RESERVED_SLOTS(BlockObject::RESERVED_SLOTS) |
     JSCLASS_IS_ANONYMOUS,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
@@ -1042,17 +1050,17 @@ const Class BlockObject::class_ = {
         nullptr,          /* hasProperty */
         nullptr,          /* getProperty */
         nullptr,          /* setProperty */
         nullptr,          /* getOwnPropertyDescriptor */
         nullptr,          /* deleteProperty */
         nullptr, nullptr, /* watch/unwatch */
         nullptr,          /* getElements */
         nullptr,          /* enumerate (native enumeration of target doesn't work) */
-        block_ThisObject,
+        block_ThisValue,
     }
 };
 
 template<XDRMode mode>
 bool
 js::XDRStaticBlockObject(XDRState<mode>* xdr, HandleObject enclosingScope,
                          MutableHandle<StaticBlockObject*> objp)
 {
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -398,16 +398,17 @@ class ModuleEnvironmentObject : public C
     static bool setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
                             HandleValue receiver, JS::ObjectOpResult& result);
     static bool getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
                                          MutableHandle<JSPropertyDescriptor> desc);
     static bool deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
                                ObjectOpResult& result);
     static bool enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
                           bool enumerableOnly);
+    static bool thisValue(JSContext* cx, HandleObject obj, MutableHandleValue vp);
 };
 
 typedef Rooted<ModuleEnvironmentObject*> RootedModuleEnvironmentObject;
 typedef Handle<ModuleEnvironmentObject*> HandleModuleEnvironmentObject;
 typedef MutableHandle<ModuleEnvironmentObject*> MutableHandleModuleEnvironmentObject;
 
 class DeclEnvObject : public ScopeObject
 {
@@ -671,18 +672,20 @@ class DynamicWithObject : public NestedS
     }
 
     /* Return the 'o' in 'with (o)'. */
     JSObject& object() const {
         return getReservedSlot(OBJECT_SLOT).toObject();
     }
 
     /* Return object for the 'this' class hook. */
-    JSObject& withThis() const {
-        return getReservedSlot(THIS_SLOT).toObject();
+    Value withThis() const {
+        Value thisValue = getReservedSlot(THIS_SLOT);
+        MOZ_ASSERT(thisValue.isObject());
+        return thisValue;
     }
 
     /*
      * Return whether this object is a syntactic with object.  If not, this is a
      * With object we inserted between the outermost syntactic scope and the
      * global object to wrap the scope chain someone explicitly passed via JSAPI
      * to CompileFunction or script evaluation.
      */
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -660,17 +660,17 @@ const XPCWrappedNativeJSClass XPC_WN_NoH
         nullptr, // hasProperty
         nullptr, // getProperty
         nullptr, // setProperty
         nullptr, // getOwnPropertyDescriptor
         nullptr, // deleteProperty
         nullptr, nullptr, // watch/unwatch
         nullptr, // getElements
         nullptr, // enumerate
-        XPC_WN_JSOp_ThisObject,
+        XPC_WN_JSOp_ThisValue,
     }
   }
 };
 
 
 /***************************************************************************/
 
 static bool
@@ -922,20 +922,20 @@ XPC_WN_JSOp_Enumerate(JSContext* cx, Han
 
     bool retval = true;
     nsresult rv = si->GetCallback()->NewEnumerate(wrapper, cx, obj, properties, &retval);
     if (NS_FAILED(rv))
         return Throw(rv, cx);
     return retval;
 }
 
-JSObject*
-XPC_WN_JSOp_ThisObject(JSContext* cx, HandleObject obj)
+bool
+XPC_WN_JSOp_ThisValue(JSContext* cx, HandleObject obj, MutableHandleValue vp)
 {
-    return JS_ObjectToOuterObject(cx, obj);
+    return mozilla::dom::ObjectToOuterObjectValue(cx, obj, vp);
 }
 
 /***************************************************************************/
 
 // static
 XPCNativeScriptableInfo*
 XPCNativeScriptableInfo::Construct(const XPCNativeScriptableCreateInfo* sci)
 {
@@ -1031,17 +1031,17 @@ XPCNativeScriptableShared::PopulateJSCla
     if (mFlags.WantFinalize())
         mJSClass.base.finalize = XPC_WN_Helper_Finalize;
     else
         mJSClass.base.finalize = XPC_WN_NoHelper_Finalize;
 
     js::ObjectOps* ops = &mJSClass.base.ops;
     if (mFlags.WantNewEnumerate())
         ops->enumerate = XPC_WN_JSOp_Enumerate;
-    ops->thisObject = XPC_WN_JSOp_ThisObject;
+    ops->thisValue = XPC_WN_JSOp_ThisValue;
 
 
     if (mFlags.WantCall())
         mJSClass.base.call = XPC_WN_Helper_Call;
     if (mFlags.WantConstruct())
         mJSClass.base.construct = XPC_WN_Helper_Construct;
 
     if (mFlags.WantHasInstance())
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -939,48 +939,48 @@ extern const js::Class XPC_WN_Tearoff_JS
 extern const js::Class XPC_WN_NoHelper_Proto_JSClass;
 
 extern bool
 XPC_WN_CallMethod(JSContext* cx, unsigned argc, JS::Value* vp);
 
 extern bool
 XPC_WN_GetterSetter(JSContext* cx, unsigned argc, JS::Value* vp);
 
-extern JSObject*
-XPC_WN_JSOp_ThisObject(JSContext* cx, JS::HandleObject obj);
+extern bool
+XPC_WN_JSOp_ThisValue(JSContext* cx, JS::HandleObject obj, JS::MutableHandleValue vp);
 
 // Macros to initialize Object or Function like XPC_WN classes
 #define XPC_WN_WithCall_ObjectOps                                             \
     {                                                                         \
         nullptr, /* lookupProperty */                                         \
         nullptr, /* defineProperty */                                         \
         nullptr, /* hasProperty */                                            \
         nullptr, /* getProperty    */                                         \
         nullptr, /* setProperty    */                                         \
         nullptr, /* getOwnPropertyDescriptor */                               \
         nullptr, /* deleteProperty */                                         \
         nullptr, nullptr, /* watch/unwatch */                                 \
         nullptr, /* getElements */                                            \
         nullptr, /* enumerate */                                              \
-        XPC_WN_JSOp_ThisObject,                                               \
+        XPC_WN_JSOp_ThisValue,                                               \
     }
 
 #define XPC_WN_NoCall_ObjectOps                                               \
     {                                                                         \
         nullptr, /* lookupProperty */                                         \
         nullptr, /* defineProperty */                                         \
         nullptr, /* hasProperty */                                            \
         nullptr, /* getProperty    */                                         \
         nullptr, /* setProperty    */                                         \
         nullptr, /* getOwnPropertyDescriptor */                               \
         nullptr, /* deleteProperty */                                         \
         nullptr, nullptr, /* watch/unwatch */                                 \
         nullptr, /* getElements */                                            \
         nullptr, /* enumerate */                                              \
-        XPC_WN_JSOp_ThisObject,                                               \
+        XPC_WN_JSOp_ThisValue,                                               \
     }
 
 // Maybe this macro should check for class->enumerate ==
 // XPC_WN_Shared_Proto_Enumerate or something rather than checking for
 // 4 classes?
 static inline bool IS_PROTO_CLASS(const js::Class* clazz)
 {
     return clazz == &XPC_WN_NoMods_WithCall_Proto_JSClass ||