Bug 929467 - Only make Proxy a constructor when the target is a constructor. (r=jorendorff)

This commit is contained in:
Eric Faust 2015-01-05 16:13:13 -08:00
parent 8bb1b9d23b
commit d8e7cee936
3 changed files with 36 additions and 9 deletions

View File

@ -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");

View File

@ -1111,7 +1111,16 @@ 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;
@ -1139,9 +1148,13 @@ js::proxy(JSContext *cx, unsigned argc, jsval *vp)
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;
}

View File

@ -65,11 +65,8 @@ class ScriptedDirectProxyHandler : public DirectProxyHandler {
}
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;
@ -78,7 +75,10 @@ class ScriptedDirectProxyHandler : public DirectProxyHandler {
// 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;