On Sep 23, 2013, at 1:29 AM, Tom Van Cutsem wrote:
> 2013/9/21 Allen Wirfs-Brock <[email protected]>
>
> ...
>
> More concretely, existing code expects that
> obj.valueOf()
> is equivalent to
> obj.valueOf.call()
>
> but if obj is a transparent forwarding proxy on a map instance (or most other
> built-ins) the first form will work as expected and the second form will
> throw unless something like [[InvokeFunction]] is used within F.p.call.
>
> Or if obj is a transparent forwarding proxy done right, i.e. whose "get" trap
> returns a wrapper function that re-binds |this| and then forwards (a membrane
> does this automatically).
I think it is unreasonable to expect anybody to get this right. An arbitrary
data property might be either a method (and hence should be wrapped) or just a
data store containing a function value that should not be wrapped. You might
use a white-list within the "get" trap to identify the actual methods that need
wrappers. But what about properties that are dynamically added to the object.
Should they be treated as data or methods? What about inherited properties?
You also need to include them in the white-list, but what happenswhen the proto
links are mutated?
>
> ...
> Using this specific example, you are saying the programmer expects the above
> to throw if unknownObject is a transparently forwarding proxy over an Array
> instance. I doubt if that is the actual expectation. I think the
> expectation is that the orginal_push will be invoked as if was the function
> retrieved via unknownObject.push. In other words, it is not trying to
> change the normal this binding semantics of obj.push(), it is only trying to
> force a local bind of obj.push to a specific value (ie, circumvent
> obj.[[Get]]("push")).
>
> Given that Array.isArray(proxyForArray) is specced to return false, I don't
> see why the above code should work transparently on proxies-for-arrays, while
> a simple Array.isArray test would fail.
>
The problem with Array.isArray is that it doesn't do a polymorphic dispatch on
the value it is test. It's current definition was adequate for its original
purpose when we didn't have proxies or formalized subclassing. It's
problematic now.
The way to fix Array.isArray is to add a @@isArray own property to all exotic
array objects when they are created and for Array.isArray to be redefined as:
Array.isArray = (array) => array[@@isArray] === true;
(Array.isArray and @@create for exotic array objects are both built-ins so we
don't need to directly expose @@isArray to ES code. Hence no forgery issues.
>
> I'm skeptical that there are many (any?) existing situations where
> F.o.call/apply is used with the failure expectation WRT transparent proxies
> that is implicit in your example. Do you know of any?
>
> For situations that really want to do a naked [[Call]] without any
> proxy-based intercession the way to do it would be
> Reflect.call(original_push,unknownObject,...args) rather than
> original_push.call.(unknownObject,...args).
>
> Indeed, except that I expressed concerns about the expectations of existing
> ES5 code, for which it is too late to opt-in to the new Reflect.apply
> primitive.
Yes, but I'm arguing that it there is little such code. Can you identify any?
What I'm trying to do is make both existing and new code would as expected in
the presence of transparent forwarding proxies (particularly those that target
built-ins)
>
>>
>> JS fundamentally decouples property lookup from method call and thus has the
>> ability to express non-polymorphic function calls. We shouldn't virtualize
>> [[Call]].
>
> Are you suggesting that we should not have a Proxy "apply" trap?
>
> No, not at all. For proxies that represent/wrap functions, it's necessary to
> virtualize [[Call]].
>
> My argument is that we should leave the [[Call]] behavior of *ordinary
> functions* alone.
Yes, and my proposal does that. It is proposing a change to F.p.call/apply not
to [[Call]] on ordinary function objects. [[Invoke]] introduces a (sometimes)
observable difference between obj.m() and obj.call("m"). That's the problem we
need to fix.
> Re-specifying Function.prototype.{apply,call} in terms of a new
> [[InvokeFunction]] MOP would mean that proxies can intercept the call
> behavior of ordinary functions.
Only when the this value explicitly passed to apply/call is a proxy. This is
exactly the behavior we need to preserved consistency between obj.m() and
obj.call("m").
> ...
>
>
> Absolutely. Proxies enable many different possible wrappers. There is no "one
> true way" of building wrappers, so obviously the default behavior will be
> wrong on some occasions.
>
> I'm willing to consider changing the behavior of ForwardingHandler.get to
> auto-wrap functions and re-bind their this-value. I think you may be right
> that for users subclassing ForwardingHandler, they probably want the
> this-rebinding to happen for them by default.
I think you should try to define it since you are arguing that such warppering
is the solution. However, as I described above, I don't think you can define a
sufficiently generic wrappering semantics.
Allen
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss