On Sep 11, 2013, at 11:02 AM, Brendan Eich wrote:
>> Tom Van Cutsem <mailto:[email protected]>
>> September 11, 2013 7:08 AM
>> 1) The [[Invoke]] operation is meant to primarily trap method calls
>> generated by user-code of the form |obj.m(...args)|. This is by far the most
>> common case, and I believe we should not extend [[Invoke]] with additional
>> arguments that are irrelevant to this primary use case. Rather than having a
>> boolean argument, better to statically separate the cases into two separate
>> internal methods.
>
> +a bazillion. Srsly!
I'm not sold. Two internal methods means there must be two proxy traps that
must always be implemented as a pair. It would simply be wrong for a handler
to implement the invoke trap and not also implement the invokeConditional
trap. And inheriting from DelegatingHandler or ForwardHandler doesn't help
because if either invoke or invokeConditional is over-ridden then then other
also needs to be over-ridden.
Having to implement two coordinated traps, in what is probably going to be a
relatively rate use case (needing to over-ride [[Invoke]]), sounds to me like a
probable footgun. On the other-hand, having a single trap with an additional
argument more explicitly communicates that a handler needs to deal with both
the conditional and unconditional situations.
obj.m(...arg) is clearly the (dynamically) dominant use case for [[Invoke]].
But, besides it, there are actually statically more places in the ES6 spec
where [[InvokeConditional]] would be appropriate than where [[Invoke]] is
appropriate (6 to 3). And that isn't counting the implementation to the MOP
calls on Proxy objects which would also be appropriate for
[[InvokeConditional]] (but more on that below).
The dominant ordinary object use case for [Invoke]] is going to inlined and
optimized by implementations so an optional extra argument that isn't used in
by obj.m(...arg) will make absolutely no difference. Neither is it likely to
make a difference in the built-ins that need to use conditional invokes. For
unconditional [[Invoke]] on proxy objects the conditional argument basically
adds only a single (not taken) conditional.
The real cost seems to be the authoring experience. Two coordinated traps is
going to be harder to author and more error prone than a single trap with a
parameter.
>
>> 2) There are a number of places in the spec where [[Invoke]] should be
>> called conditionally, if we know for sure the property is callable.
>>
>> Currently the pattern for this is [[Get]]+[[Call]]. We cannot refactor to
>> [[Has]] + [[Invoke]] in general, because [[Has]] will return true also for
>> non-callable values.
>
> This is the toString/valueOf legacy.
There are currently 6 such places (not counting the Proxy trap invocators):
3 in ToPrimitive (ie toSrting/valueOf)
1 in instanceof to access @@hasInstance (legacy conpat. for missing
@@hasInstance)
1 in [ ].toString to conditionally invoke 'join' method
1 in JSON.stringify conditionally invoke 'toJSON'
>
>> If we believe these are call-sites where it is worth avoiding the allocation
>> of a function, then having an additional internal method like [[GetMethod]]
>> or [[InvokeConditional]] makes sense, but I doubt it's worth the added
>> complexity.
>
> We have zero evidence based on legacy (i.e. what's deployed).
The consistency we are looking for is that all method invocations through a
proxy go through an invoke trap so the handler can enforce consistent method
invocation semantics.
I think the simplest solution is the extra parameter on invoke. If I can't
sell that, my next preference is [[InvokeFunction]] as I described in an
earlier message:
I beginning to think that the best solution is to add a [[InvokeFunction]]
internal method/trap. It's just like [[Invoke]] except that it assumes that
the [[Get]] step has already been performed (the function is passed in rather
than the property key). This still allows the handler to intercede in mapping
the this value or other arguments. In fact, perhaps, we could simply replace
[[Invoke]] with [[InvokeFunction]] and turn all the current [[Invoke]] calls
into [[Get]]+[[InvokeFunction]]. This would also address the conditional
[[Invoke]] issues being discussed in this thread.
[[InvokeFunction]] requires use of [[Get]] to access a method so it
reintroduces the possibility that a handler might have to cons up a function. I
suspect this is rare which is the conclusion we came to when [[Invoke]] was
originally considered long ago.
>
>> 3) For proxy trap invocations I maintain we are still better off with
>> [[Get]] + [[Call]] to keep double-lifting as simple as possible.
>
> Sorry if I missed it: what complicates things if we use [[Invoke]] to support
> double-lifting?
however proxy trap invocation should really be [[Invoke]] for semantics
consistency. It really is a method invocation and the meta-meta-level handler
should be given the opportunity to apply any method invocation policy it may
have. Either a conditional [[Invoke]] or [[Get]]+[[InvokeFunction]] would
supply adequate semantics.
Either some form of conditional [[Invoke]] or a [[InvokeFunction]] trap would
still support double-lifting via a single trap. In the case of conditional
[[Invoke]], it is that trap that would be implemented at the the
meta-meta-level. With [[InvokeFunction]] it is the [[Get]] trap that would be
implemented.
Allen
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss