Bug 1105045 - Outerize the receiver passed to proxy hooks. r=efaust. a=sylvestre
☠☠ backed out by d8d62e273a8c ☠ ☠
authorJason Orendorff <jorendorff@mozilla.com>
Fri, 07 Aug 2015 10:23:05 -0500
changeset 288810 eb7622882d750ae1923fe1160ca50ef1ae7cc425
parent 288809 a0ff23d5fad088a5a1bdc40d91065d0dcaa2ea06
child 288811 91624ae2b989b83ff3a80a5fba8b8c6fa451f5ac
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersefaust, sylvestre
bugs1105045
milestone42.0a2
Bug 1105045 - Outerize the receiver passed to proxy hooks. r=efaust. a=sylvestre
js/src/jsobj.h
js/src/proxy/Proxy.cpp
js/src/tests/ecma_6/Proxy/global-receiver.js
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -999,17 +999,18 @@ GetObjectClassName(JSContext* cx, Handle
  *
  * 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.
  */
 
 /*
- * If obj a WindowProxy, return its current inner Window. Otherwise return obj.
+ * If obj is a WindowProxy, return its current inner Window. Otherwise return
+ * obj. This function can't fail and never returns nullptr.
  *
  * GetInnerObject is called when we need a scope chain; you never want a
  * WindowProxy on a scope chain.
  *
  * It's also called in a few places where an object comes in from script, and
  * the user probably intends to operate on the Window, not the
  * WindowProxy. Object.prototype.watch and various Debugger features do
  * this. (Users can't simply pass the Window, because the Window isn't exposed
@@ -1022,16 +1023,17 @@ GetInnerObject(JSObject* obj)
         JS::AutoSuppressGCAnalysis nogc;
         return op(obj);
     }
     return 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.)
  */
 inline JSObject*
 GetOuterObject(JSContext* cx, HandleObject obj)
 {
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -260,30 +260,50 @@ Proxy::hasOwn(JSContext* cx, HandleObjec
     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     *bp = false; // default result if we refuse to perform this action
     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
     if (!policy.allowed())
         return policy.returnValue();
     return handler->hasOwn(cx, proxy, id, bp);
 }
 
+static Value
+OuterizeValue(JSContext* cx, HandleValue v)
+{
+    if (v.isObject()) {
+        RootedObject obj(cx, &v.toObject());
+        return ObjectValue(*GetOuterObject(cx, obj));
+    }
+    return v;
+}
+
 bool
-Proxy::get(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id,
+Proxy::get(JSContext* cx, HandleObject proxy, HandleObject receiver_, HandleId id,
            MutableHandleValue vp)
 {
     JS_CHECK_RECURSION(cx, return false);
     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     vp.setUndefined(); // default result if we refuse to perform this action
     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
     if (!policy.allowed())
         return policy.returnValue();
+<<<<<<< local
     bool own;
     if (!handler->hasPrototype()) {
         own = true;
     } else {
+=======
+
+    // Outerize the receiver. Proxy handlers shouldn't have to know about
+    // the Window/WindowProxy distinction.
+    RootedObject receiver(cx, GetOuterObject(cx, receiver_));
+
+    if (handler->hasPrototype()) {
+        bool own;
+>>>>>>> other
         if (!handler->hasOwn(cx, proxy, id, &own))
             return false;
     }
     if (own)
         return handler->get(cx, proxy, receiver, id, vp);
     INVOKE_ON_PROTOTYPE(cx, handler, proxy, GetProperty(cx, proto, receiver, id, vp));
 }
 
@@ -302,28 +322,32 @@ Proxy::callProp(JSContext* cx, HandleObj
             return false;
     }
 #endif
 
     return true;
 }
 
 bool
-Proxy::set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v, HandleValue receiver,
+Proxy::set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v, HandleValue receiver_,
            ObjectOpResult& result)
 {
     JS_CHECK_RECURSION(cx, return false);
     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
     if (!policy.allowed()) {
         if (!policy.returnValue())
             return false;
         return result.succeed();
     }
 
+    // Outerize the receiver. Proxy handlers shouldn't have to know about
+    // the Window/WindowProxy distinction.
+    RootedValue receiver(cx, OuterizeValue(cx, receiver_));
+
     // Special case. See the comment on BaseProxyHandler::mHasPrototype.
     if (handler->hasPrototype())
         return handler->BaseProxyHandler::set(cx, proxy, id, v, receiver, result);
 
     return handler->set(cx, proxy, id, v, receiver, result);
 }
 
 bool
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Proxy/global-receiver.js
@@ -0,0 +1,29 @@
+// The global object can be the receiver passed to the get and set traps of a Proxy.
+
+var global = this;
+var proto = Object.getPrototypeOf(global);
+var gets = 0, sets = 0;
+Object.setPrototypeOf(global, new Proxy(proto, {
+    has(t, id) {
+        return id === "bareword" || Reflect.has(t, id);
+    },
+    get(t, id, r) {
+        gets++;
+        assertEq(r, global);
+        return Reflect.get(t, id, r);
+    },
+    set(t, id, v, r) {
+        sets++;
+        assertEq(r, global);
+        return Reflect.set(t, id, v, r);
+    }
+}));
+
+assertEq(bareword, undefined);
+assertEq(gets, 1);
+
+bareword = 12;
+assertEq(sets, 1);
+assertEq(global.bareword, 12);
+
+reportCompare(0, 0);