Why Proxy "apply" trap limited to function only?

# Олег Галабурда (9 years ago)

I'm currently working on project that allows to manipulate objects passed from Worker. burdiuz/js-worker-interface

It creates wrapper objects that record actions applied to this object and send commands to Worker. The problem is, when request is made I cannot know what kind of object is this, so in case of function I should give ability to make calls on the wrapper object. As example, this code:

var api = new WorkerInterface('../webworker.js'); api.requestTime();

When "requestTime" was requested, it calls "get" and immediately returns new Proxy to serve function call. But at time when Proxy is created I do not know if that is function or not. Now I passed "apply" limitation by adding wrapper function and make Proxy think it works with function. burdiuz/js-worker-interface/blob/master/source/interface/proxy.js

I found in discussions why "call" trap was rejected, but didn't find anything about restricting "apply" trap to functions only.

# Caitlin Potter (9 years ago)

What exactly do you mean by “restricted to functions only”?

# Олег Галабурда (9 years ago)

As I uderstood, here, in the spec it states that if target is "function", then trap "apply" is available, to catch function invocations harmony:virtual_object_api

And that's how it behaves in Forefox -- it just ignores "apply" trap if target object is not a function.

2016-03-02 14:55 GMT+02:00 Caitlin Potter <caitpotter88 at gmail.com>:

# Claude Pache (9 years ago)

Le 2 mars 2016 à 13:21, Олег Галабурда <burdiuz at gmail.com> a écrit :

Hi!

I'm currently working on project that allows to manipulate objects passed from Worker. burdiuz/js-worker-interface, burdiuz/js-worker-interface

It creates wrapper objects that record actions applied to this object and send commands to Worker. The problem is, when request is made I cannot know what kind of object is this, so in case of function I should give ability to make calls on the wrapper object. As example, this code:

var api = new WorkerInterface('../webworker.js'); api.requestTime();

When "requestTime" was requested, it calls "get" and immediately returns new Proxy to serve function call. But at time when Proxy is created I do not know if that is function or not. Now I passed "apply" limitation by adding wrapper function and make Proxy think it works with function. burdiuz/js-worker-interface/blob/master/source/interface/proxy.js, burdiuz/js-worker-interface/blob/master/source/interface/proxy.js

If I guess correctly your issue, you would like to have something similar to:

foo.bar // not-a-function

and:

foo.bar() // a method call

? Indeed, that is not possible in JavaScript (and that limitation is not restricted to proxies).

I found in discussions why "call" trap was rejected, but didn't find anything about restricting "apply" trap to functions only.

Probably because "X is a function" is, by design, strictly equivalent to "X is callable"?

# Caitlin Potter (9 years ago)

As far as I can tell, this is a bug in Firefox / SpiderMonkey. The [[CALL]] internal method of Proxy objects does not impose any restriction on the type of the Proxy target.

The following code behaves correctly in Chromium:

var proxy =  new Proxy({}, { apply(target, thisArgument, arguments) { return Reflect.apply(Array, thisArgument, arguments); } });

proxy(1, 2, 3); // [1, 2, 3]

But as you say, this throws in Firefox, and based on the specification, this appears to be a bug. Implementations, amirite? ¯_(ツ)_/¯

# Claude Pache (9 years ago)

Le 2 mars 2016 à 15:36, Caitlin Potter <caitpotter88 at gmail.com> a écrit :

As far as I can tell, this is a bug in Firefox / SpiderMonkey. The [[CALL]] internal method of Proxy objects does not impose any restriction on the type of the Proxy target.

The following code behaves correctly in Chromium:

var proxy =  new Proxy({}, { apply(target, thisArgument, arguments) { return Reflect.apply(Array, thisArgument, arguments); } });

proxy(1, 2, 3); // [1, 2, 3]

But as you say, this throws in Firefox, and based on the specification, this appears to be a bug. Implementations, amirite? ¯_(ツ)_/¯

Rather, I'd say it is a bug in Chromium.

A Proxy should have a [[Call]] internal method if and only if its target has a [[Call]] internal method. See:

www.ecma-international.org/ecma-262/6.0/#sec-proxycreate, www.ecma-international.org/ecma-262/6.0/#sec-proxycreate step 7.

That maintains the equivalence: typeof x == "function" iff x is callable.

# Caitlin Potter (9 years ago)

You’re right. I would argue this warrants a non-normative pointer to ProxyCreate() in the proxy [[CALL]] and [[CONSTRUCT]] internal methods.

# Олег Галабурда (9 years ago)

Yes, correct. I want to be able to make a callable Proxy for non-callable objects and don't understand why Proxies are having these restrictions. As you can see I, and any other developer, can easily bypass this restriction and make a wrapper function that actually will be never called and just redirect calls made on Proxy to a method of an object. If "apply" trap will be allowed for any type of targets this will make life easier :)

2016-03-02 16:19 GMT+02:00 Claude Pache <claude.pache at gmail.com>:

# Олег Галабурда (9 years ago)

Rather, I'd say it is a bug in Chromium.

A Proxy should have a [[Call]] internal method if and only if its target has a [[Call]] internal method. See:

www.ecma-international.org/ecma-262/6.0/#sec-proxycreate step 7.

That maintains the equivalence: typeof x == "function" iff x is callable.

So, that's the restriction I'm talking about.

# Claude Pache (9 years ago)

Le 2 mars 2016 à 15:47, Caitlin Potter <caitpotter88 at gmail.com> a écrit :

You’re right. I would argue this warrants a non-normative pointer to ProxyCreate() in the proxy [[CALL]] and [[CONSTRUCT]] internal methods.

Thanks!

Bug reported here:

bugs.chromium.org/p/v8/issues/detail?id=4796, bugs.chromium.org/p/v8/issues/detail?id=4796

# Mark S. Miller (9 years ago)

We maintain the invariant that the typeof of an object is stable -- it never varies over time. An object is callable iff its typeof is 'function'. By making the callability and the typeof of the proxy dependent on its target, the two stay consistent.

On Wed, Mar 2, 2016 at 6:51 AM, Олег Галабурда <burdiuz at gmail.com> wrote:

Yes, correct. I want to be able to make a callable Proxy for non-callable objects and don't understand why Proxies are having these restrictions. As you can see I, and any other developer, can easily bypass this restriction and make a wrapper function that actually will be never called and just redirect calls made on Proxy to a method of an object.

Exactly. You are doing the right thing. This demonstrates that there is no loss of intended generality from imposing this restriction. It imposes a minor inconvenience for an uncommon case, as the price of being more straightforward for common cases.

# Isiah Meadows (9 years ago)

I thought typeof (class {}) === "function" as well. And classes, to my knowledge aren't callable.

# Kevin Smith (9 years ago)

I thought typeof (class {}) === "function" as well. And classes, to my knowledge aren't callable.

Oh they are, they just throw when you call them : )

tc39.github.io/ecma262/#sec-ecmascript-function-objects-call-thisargument-argumentslist Step 2

# Isiah Meadows (9 years ago)

Oh. I didn't realize that.