Bug 929467 - Only make Proxy a constructor when the target is a constructor. (r=jorendorff)
authorEric Faust <efaustbmo@gmail.com>
Mon, 05 Jan 2015 16:13:13 -0800
changeset 247985 7cdfef0fddb8da371663df0756119fdf1b8e6387
parent 247984 eb6f3e7f565020ba38d08675801dec139db24f18
child 247986 e9535d758389f7d75043a1665d3ebce4cf9145fa
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs929467
milestone37.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 929467 - Only make Proxy a constructor when the target is a constructor. (r=jorendorff)
js/src/jit-test/tests/proxy/testDirectProxyConstruct5.js
js/src/proxy/ScriptedDirectProxyHandler.cpp
js/src/proxy/ScriptedDirectProxyHandler.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/proxy/testDirectProxyConstruct5.js
@@ -0,0 +1,14 @@
+load(libdir + "asserts.js");
+
+// Make sure that a proxy only has a [[Construct]] if the target does
+
+var handler = {};
+var p = new Proxy(Math.sin, handler);
+var r = Proxy.revocable(Math.sin, handler).proxy;
+
+assertThrowsInstanceOf(() => new p, TypeError, "Can't use 'new' on proxy with non-constructor target");
+assertThrowsInstanceOf(() => new r, TypeError, "Can't use 'new' on proxy with non-constructor target");
+// Better throw regardless of whether we have a handler trap.
+handler.construct = (() => ({}));
+assertThrowsInstanceOf(() => new p, TypeError, "Can't use 'new' on proxy with non-constructor target");
+assertThrowsInstanceOf(() => new r, TypeError, "Can't use 'new' on proxy with non-constructor target");
--- a/js/src/proxy/ScriptedDirectProxyHandler.cpp
+++ b/js/src/proxy/ScriptedDirectProxyHandler.cpp
@@ -1106,17 +1106,26 @@ ScriptedDirectProxyHandler::construct(JS
     }
     return true;
 }
 
 bool
 ScriptedDirectProxyHandler::isCallable(JSObject *obj) const
 {
     MOZ_ASSERT(obj->as<ProxyObject>().handler() == &ScriptedDirectProxyHandler::singleton);
-    return obj->as<ProxyObject>().extra(IS_CALLABLE_EXTRA).toBoolean();
+    uint32_t callConstruct = obj->as<ProxyObject>().extra(IS_CALLCONSTRUCT_EXTRA).toPrivateUint32();
+    return !!(callConstruct & IS_CALLABLE);
+}
+
+bool
+ScriptedDirectProxyHandler::isConstructor(JSObject *obj) const
+{
+    MOZ_ASSERT(obj->as<ProxyObject>().handler() == &ScriptedDirectProxyHandler::singleton);
+    uint32_t callConstruct = obj->as<ProxyObject>().extra(IS_CALLCONSTRUCT_EXTRA).toPrivateUint32();
+    return !!(callConstruct & IS_CONSTRUCTOR);
 }
 
 const char ScriptedDirectProxyHandler::family = 0;
 const ScriptedDirectProxyHandler ScriptedDirectProxyHandler::singleton;
 
 bool
 js::proxy(JSContext *cx, unsigned argc, jsval *vp)
 {
@@ -1134,19 +1143,23 @@ js::proxy(JSContext *cx, unsigned argc, 
         return false;
     RootedValue priv(cx, ObjectValue(*target));
     JSObject *proxy_ =
         NewProxyObject(cx, &ScriptedDirectProxyHandler::singleton,
                        priv, TaggedProto::LazyProto, cx->global());
     if (!proxy_)
         return false;
     Rooted<ProxyObject*> proxy(cx, &proxy_->as<ProxyObject>());
-    bool targetIsCallable = target->isCallable(); // Can GC - don't compute it inline.
     proxy->setExtra(ScriptedDirectProxyHandler::HANDLER_EXTRA, ObjectValue(*handler));
-    proxy->setExtra(ScriptedDirectProxyHandler::IS_CALLABLE_EXTRA, BooleanValue(targetIsCallable));
+
+    // Assign [[Call]] and [[Construct]]
+    uint32_t callable = target->isCallable() ? ScriptedDirectProxyHandler::IS_CALLABLE : 0;
+    uint32_t constructor = target->isConstructor() ? ScriptedDirectProxyHandler::IS_CONSTRUCTOR : 0;
+    proxy->as<ProxyObject>().setExtra(ScriptedDirectProxyHandler::IS_CALLCONSTRUCT_EXTRA,
+                                      PrivateUint32Value(callable | constructor));
     args.rval().setObject(*proxy);
     return true;
 }
 
 static bool
 RevokeProxy(JSContext *cx, unsigned argc, Value *vp)
 {
     CallReceiver rec = CallReceiverFromVp(vp);
--- a/js/src/proxy/ScriptedDirectProxyHandler.h
+++ b/js/src/proxy/ScriptedDirectProxyHandler.h
@@ -60,30 +60,30 @@ class ScriptedDirectProxyHandler : publi
     // filter. [[GetOwnProperty]] could potentially change the enumerability of
     // the target's properties.
     virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy,
                                               AutoIdVector &props) const MOZ_OVERRIDE {
         return BaseProxyHandler::getOwnEnumerablePropertyKeys(cx, proxy, props);
     }
 
     virtual bool isCallable(JSObject *obj) const MOZ_OVERRIDE;
-    virtual bool isConstructor(JSObject *obj) const MOZ_OVERRIDE {
-        // For now we maintain the broken behavior that a scripted proxy is constructable if it's
-        // callable. See bug 929467.
-        return isCallable(obj);
-    }
+    virtual bool isConstructor(JSObject *obj) const MOZ_OVERRIDE;
+
     virtual bool isScripted() const MOZ_OVERRIDE { return true; }
 
     static const char family;
     static const ScriptedDirectProxyHandler singleton;
 
     // The "proxy extra" slot index in which the handler is stored. Revocable proxies need to set
     // this at revocation time.
     static const int HANDLER_EXTRA = 0;
-    static const int IS_CALLABLE_EXTRA = 1;
+    static const int IS_CALLCONSTRUCT_EXTRA = 1;
+    // Bitmasks for the "call/construct" slot
+    static const int IS_CALLABLE    = 1 << 0;
+    static const int IS_CONSTRUCTOR = 1 << 1;
     // The "function extended" slot index in which the revocation object is stored. Per spec, this
     // is to be cleared during the first revocation.
     static const int REVOKE_SLOT = 0;
 };
 
 bool
 proxy(JSContext *cx, unsigned argc, jsval *vp);