Invoke trap (was: May 21, 22, 23 TC39 Meeting Notes)

Tom Van Cutsem tomvc.be at gmail.com
Sun Jun 9 08:37:26 PDT 2013


Hi David,

2013/6/7 David Bruant <bruant.d at gmail.com>

> Le 02/06/2013 09:46, Rick Waldron a écrit :
>
>> 4.4 Proxies
>>
>> Proxy Invoke Trap and wrong |this|-binding on built-in methods
>>
>> AWB: with current default behavior of “get”, “Caretaker” will break on
>> built-ins such as Date, because the |this| binding is by default set to the
>> proxy, so the Date built-in method will not find the correct private state.
>> ARB: Same issue with binary methods
>> ...
>> STH: We should add invoke trap but not change the object model
>> MM: Pleasant to have. Separate from private state.
>> AWB: used to think this was an issue with proxies, but convinced that
>> it’s an API issue: we need to provide default handlers that do the right
>> thing, and which users can subclass. In particular, want a handler that, on
>> forwarding, rebinds |this| to the target.
>> STH: If you want to proxy a Date method the underlying `this` needs to be
>> a non wrapped Date object.
>>
> Doesn't this break encapsulation?
>

No it doesn't (more below).


> var d = new Date();
> var pd = new Proxy(d, {
> invoke: function(target, name, thisArg, args){
> return target[name].apply(thisArg, args)
> }
> })
>
> var pt = pd.getTime(); // calls the invoke trap
>

This will call "invoke", which forwards to the real Date.prototype.getTime
but with thisArg still bound to pd (the proxy). Hence this method will fail
with a "error: not a Date" exception. The proposed solution is to use a
ForwardingHandler [1] in such cases.


>
> // in a mixed-trusted context with access to pd:
> var ppd = new Proxy(pd, {
> invoke: function(target, name, thisArg, args){
> // does the actual unwrapped date object leak through thisArg?
> }
> });
>
> var ppt = ppd.getTime();
>

For this method call, thisArg inside the invoke trap will be bound to ppd,
not the the real wrapped Date object.


> In any case, what is the following supposed to do:
>
> Date.prototype.getTime.call( new Proxy(new Date(), handler) )
>

This will still crash with an "error: not a Date" exception. Even when
using a ForwardingHandler.


>
> I imagine we would want this to work too. Is this going through the invoke
> trap as well? The question stands for all new "class-specific" (Map, Set,
> etc.) methods that would be dot-called with a proxy as first argument.
>

dot-called methods will not trigger the "invoke" trap.


> If the problem being solved is making Date (and Map, Set, etc) built-in
> methods works on wrapped objects, I feel the invoke trap is only a partial
> solution.
>

It is indeed. Combined with the ForwardingHandler, the invoke() trap allows
us to transparently forward |pd.getTime()| but not
|Date.prototype.getTime.call(pd).|


> I intuit (but have no proof) that for Date.prototype.getTime.call to work
> on proxies, there is a need for class-specific traps.
> * Object properties have specific traps to intermediate access
> (has/get/set/defineProperty/**keys, etc.),
> * [[Prototype]] has specific traps to intermediate access
> (get/setPrototypeOf)
> * [[Extensible]] has a specific trap to intermediate access
> (preventExtensions)
> * [[Call]] has a specific trap to intermediate access (apply)
> * [[Construct]] has a specific trap to intermediate access (construct)
> * Some other things that are somewhat internal (@@iterator, @@class, etc.)
> are exposed as symbols and are mediated via object properties-related trap
>
> The pattern I see is that a good share of object internal properties
> (ES5.1 - 8.6.2 - Table 8 and Table 9) have traps dedicated to mediate
> access.
>
> The case of functions is interesting. There are traps that are only
> relevant when the target is a function. In that regard, adding traps that
> are specific to accessing the Date internal [[Time]] value or specific
> traps to mediate access to [[MapData]] isn't any different than the current
> specific traps that mediate [[Call]] or [[Construct]]
>

I think your analysis is mostly right: if we want to make e.g.
Date.prototype.getTime.call(proxy) work, then the easiest way to fit that
into the current design would be to add Date-specific traps (and
Map-specific, Set-specific, Regexp-specific, etc. traps)

The question is whether there's a strong need for intercepting these
operations. It implies a pretty strong growth of the handler API. And it's
not sufficiently general-purpose to also work for exotics that are defined
outside of ES6 (e.g. DOM objects). For full transparency across isolated
object graphs, I think membranes are still the way to go.

Cheers,
Tom

[1] http://wiki.ecmascript.org/doku.php?id=harmony:virtual_object_api
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20130609/84d24e31/attachment.html>


More information about the es-discuss mailing list