[[Invoke]] and implicit method calls

Allen Wirfs-Brock allen at wirfs-brock.com
Mon Sep 23 17:18:00 PDT 2013


On Sep 23, 2013, at 3:50 PM, Brendan Eich wrote:

>> Allen Wirfs-Brock <mailto:allen at wirfs-brock.com>
>> September 23, 2013 3:06 PM
>> 
>> It's a matter of internal vs external consistency. The implementor of a Proxy handler needs to internally consistently implement the 'get', 'set', 'has', 'invoke', etc. traps in order to manifest an object that exhibits the normally expected object semantics. But there is no fool proof (I contend) way for the proxy implementor to achieve 'invoke' consistency with 'get+F.p.call
> 
> Now it depends on what you mean by "fool proof".

Takes into account dynamically added properties (particularly inherited), [[Prototype]] mutation, subclassing,etc.

>> 
>> JS programmer have been trained that:
>> 
>> obj.m(args)
>> 
>> is equivalent to:
>> 
>> let f = obj.m;
>> f.call(obj, args)
>> 
>> Sometimes they place additional code between the property access and the call. That code might make the call conditional. It might memorize f and obj in order to defer the call to an arbitrary point in the future.
>> 
>> Prior to proxies this was always valid because the semantics of both, at the MOP level were: f = obj.[[Get]]("m"); f.[[Call]](obj,args). But with proxies and [[Invoke]], this equivalent is no longer the case.
> 
> Vacuously true, and why we resisted invoke.
> 
>> And with the current definition of F.p.call/apply there is nothing that the client code can do to reestablish the consistency.
> 
> To use either F.p.call or .apply, you must do obj.[[Get]]("m"). For function objects, [[Call]] is well-specified and what F.p.{c,a} use.
> 
> For proxies, we need something akin to [[Call]]. Is that the point of [[InvokeFunction]]?

Exactly.  [[InvokeFunction]] is the Proxy this aware version of [[Call]]. 

Rather than obj.m(args) turning into obj.[[Invoke]]("m",args) where [[Invoke]] internally does [[Get]]+[[Call]] I'm suggesting that  turns into:
       f = obj.[[Get]]("m")
       obj.[[InvokeFunction]](f,args)

(I've left out the extra Receiver parameters that [[Get]], [[Invoke]], and [[InvokeFunction]] all can accept.)

[[Invoke]] would go away and the call operator would simply be implemented as [[Get]]+[[InvokeFunction]].

In addition, F.p.call/apply would also use [[InvokeFunction]] instead of [[Call]] whenever the passed this value is a Proxy.

> 
>> This can be fixed, if obj.m(args) has the semantics of [[Get]]+[[InvokeFunction]] and F.p.call/apply also uses [[InvokeFunction]] (noting that is the vast majority of cases [[InvokeFunction]] is exactly the same as [[Call]].
> 
> Ok, but then Tom asked whether function objects targeted by direct proxies having their [[Call]] overridden was a problem?

Ultimately [[InvokeFunction]] performs a [[Call]] on some function object.  [[InvokeFunction]] doesn't replace [[Call]] as the primitive function invocation hook. 

> 
>> The JS programmers expected equivalence is maintained yet transparent proxies still have an opportunity to do forward of this values.
>> 
>> BTW, it also restore left to right evaluation of the call operator which regressed when the call operator was implemented using [[Invoke]].
> 
> If you can rename [[InvokeFunction]] to [[Invoke]] so my head stops exploding, I think we have a deal. But need Tom on board.

Yes, I want to do that renaming.  But for now we are still talking about the differences between [[Invoke]] and [[InvokeFunction]] so I can't do the renaming yet.  But a key point is that only one of them ends up in the spec. and whichever it is will be named [[Invoke]].

Allen



More information about the es-discuss mailing list