[ES Harmony Proxies] Fundamental trap definition

David Bruant bruant at enseirb-matmeca.fr
Wed Jan 26 09:54:27 PST 2011


Le 26/01/2011 17:45, Tom Van Cutsem a écrit :
> Ok, so Mark and I briefly discussed the implications of making
> "getPropertyDescriptor" and "getPropertyNames" derived.
>
> Here's one issue: if you try and write these traps as methods of some
> sort of "default" handler as we did for the other derived traps (see
> <http://wiki.ecmascript.org/doku.php?id=harmony:proxies#trap_defaults>) you'll
> notice that the handler, by default, has no way of referring to the
> proxy it's intercepting, hence can't get at its prototype and hence
> can't do the prototype-chain-walk.
This problem seems to have occured already for the set and get trap for
which (like your proposition at the end) has been resolved with adding
the proxy as an argument (called 'receiver' or 'proxy' depending on the
example).


> Now, from an implementation point-of-view, this isn't really a problem
> since the proxy implementation can make the connection between proxy
> and handler when the trap is called, and thus can do the
> prototype-chain-walk. It's just that you can't write it out in
> Javascript itself. Mark argues that therefore, "getPropertyDescriptor"
> and "getPropertyNames" are still fundamental.
Tell me if I'm wrong, but I think that one of proxies goal is to enable
people to write in ECMAScript what they usually can't. If we're starting
to make things that aren't writable in ECMAScript, we're kind of failing
at empowering proxy users.
I'm highly in favor of having default proxies behavior writable in
ECMAScript.


> Yet we both agree that providing a default implementation for these 2
> traps makes perfect sense. Mark suggested we could classify traps in
> two different ways: ("optional" vs "mandatory") vs ("fundamental" vs
> "derived"). In that view, getPropertyDescriptor and getPropertyNames
> would be optional and fundamental. All other traps would be either
> (fundamental & mandatory) or (derived & optional).
>
> I think that for developers, really the only distinction that matters
> is "optional" vs "mandatory". The "fundamental" vs "derived"
> distinction helps to clarify things for us spec. readers/writers.
This is a very good point.


> While writing this, I also thought of another potential solution: we
> could choose to pass the proxy as an additional argument to the traps.
> Then the default implementation of getPropertyDescriptor could be
> written in Javascript as follows (adapting David's code):
>
> // currently, this trap only takes the 'name' param
> getPropertyDescriptor: function(proxy, name) {
>     var objectToInspect;
>     var result;
>
>     for(objectToInspect = proxy; 
>         objectToInspect !== null; 
>         objectToInspect = Object.getPrototypeOf(objectToInspect))
>     {
>             result = Object.getOwnPropertyDescriptor(objectToInspect,
> name);
>             if (result !== undefined) { return result; } 
>     }
>
>     return undefined;
> }
This idea brings back the fact that getPropertyDescriptor has a fallback
if it's not explicitely defined and the fact that we're not doing
something that proxy users can't do.

However, it naturally raises the question: which trap should have the
proxy as an argument?

I think that the only reason why we haven't needed this argument so far
is that the handler object could be self sufficient (so we thought).
Indeed, all the proxy semantics was in the handler. Who'd need the proxy
as an argument when you already have all the semantics with you (through
'this' as used in the no-op forwarding example)?
But the point we were missing in that we do NOT have ALL the semantics.
Looking at the "Semantics of Proxies" page
(http://wiki.ecmascript.org/doku.php?id=harmony:proxies_semantics), we
can notice that the [[Prototype]] property isn't reified by any method.
Same for [[Class]], [[DefaultValue]] and [[HasInstance]].
There are reasons for not having a handler method that reify them, so
this is very unlikely to change. This means that in any handler method
that does not have the proxy as a argument, these things won't be
available. Meaning, that:
- we cannot reach the proxy prototype within a handler method (this is
probably the most annoying point, because it means that within your
handler code, you do not have access to the prototype you have set for
your proxy)
- there is no way within a handler method to know if the handler is used
for a proxy and function proxy
- there is no way to use instanceOf withing a handler method
- (is there a way to reach [[DefaultValue]]?)

For all these reasons, I think that the proxy should be given as an
argument to all handler methods.

This would also make the handler API more consistent with object APIs.
When you look at the API method list, you can notice that all commented
methods (surface syntax) are (obviously) using the proxy as an argument.
And certainly that all native method implementations use this argument.
The only methods that does use it in a different way than calling
another internal method are 'set' and 'get', because they need to do a
this-binding (see no-op forwarding example); something that you cannot
do with only the handler.

Cheers,

David

>
> Cheers,
> Tom
>
> 2011/1/24 Tom Van Cutsem <tomvc.be <http://tomvc.be>@gmail.com
> <http://gmail.com>>
>
>     David,
>
>     Your intuition of what constitutes a fundamental vs. a derived
>     trap is correct. Strictly speaking, the Proxy API could do with
>     just the fundamental traps. The motivation for providing the
>     optional derived traps was that overriding derived traps often
>     allows proxy implementors to implement the operation more
>     efficiently (usually with less object allocations).
>
>     As for your suggestion of turning 'getPropertyNames' and
>     'getPropertyDescriptor' into derived traps: I think you're
>     correct. We could specify getPropertyNames and
>     getPropertyDescriptor in terms of their "own" siblings + a
>     prototype-chain-walk.
>
>     I recall a discussion with Mark where this issue was briefly
>     discussed before. At that time, I was concerned by the fact that
>     you could equally well define getOwnPropertyDescriptor as the
>     derived trap, in terms of getPropertyDescriptor +
>     getOwnPropertyNames, as follows:
>
>     getOwnPropertyDescriptor: function(name) {
>      var desc = this.getPropertyDescriptor(name);
>      var ownnames = this.getOwnPropertyNames();
>      if (ownnames.indexOf(name) !== -1)
>        return desc;
>      else
>        return undefined;
>     }
>
>     But applying the principle of minimizing the amount of allocations
>     in default trap implementations, your definition is strictly the
>     better one.
>
>     I'm in favor of any change that can minimize the amount of
>     fundamental traps, so I'm in favor of adopting your change. Maybe
>     Mark still recalls reasons for why these traps were categorized as
>     fundamental traps in the first place.
>
>     Cheers,
>     Tom
>
>     2011/1/23 Brendan Eich <brendan at mozilla.com
>     <mailto:brendan at mozilla.com>>
>
>         I think David has a point. The fundamental vs. derived
>         distinction is, as I understand it, what Mark wrote recently,
>         and David's application of it is sound (we do that
>         prototype-walk with shadowing "duplicate removal" when
>         starting a for-in loop in SpiderMonkey). Tom should weigh in.
>
>         But proxies have a proto chain for instanceof and good old
>         prototype-based delegation purposes, even though their traps
>         could ignore the proto parameter. The instanceof operator will
>         not, and note how there is no getPrototypeOf trap, either.
>
>         This is a feature. I asked early on in the development of
>         proxies whether the alternative, of putting the proto-chain
>         walk "on the outside" of the proxy's handler traps was
>         considered, and Mark pointed out the obvious: that doing so is
>         strictly less flexible for cases where a proxy wants to do
>         something different.
>
>         But this greater-flexibility design decision does not mean the
>         universal, object -invariant (ignoring mutable __proto__)
>         instanceof and getPrototypeOf (and ES3's isPrototypeOf)
>         relations should be outside of a proxy's control. The
>         Proxy.create proto parameter is the way to control those
>         relations, for the life of a given proxy, and modulo property
>         lookup trap freedom to do other things than just delegate up
>         the proto chain.
>
>         /be
>
>         On Jan 23, 2011, at 8:43 AM, François REMY wrote:
>
>>         Moreover, should a Proxy really have a prototype ?
>>         I mean, what’s the point about having a prototype, since the
>>         “get” method can return everything you would like for any
>>         property ?
>>          
>>         I think getProtototypeOf should be defined as a new trap. And
>>         its default behaviour should be to return null (or
>>         Object.prototype, but I think null is the intented behavior
>>         of a proxy).
>>          
>>         On the other hand, overriding getPropertyNames should not be
>>         allowed. getPropertyNames should *always* return the
>>         concatenation of the properties returned by
>>         getOwnPropertyNames() and by
>>         getPrototypeOf(this).getPropertyNames(). It makes no sense
>>         otherwhise.
>>          
>>         I understand that we may want to redefine the way an
>>         ECMAScript Object can handle the native commands, but I’m
>>         strongly against anything that can conduce to illogical
>>         results (if there’s not an use case that justify it,
>>         naturally). The definition of getPropertyNames is clear and
>>         being able to redefine it locally seems me wrong. A proxy can
>>         modify is own behavior, not the behavior of the ES engine.
>>         Being able to have getPropertyNames and getOwnPropertyNames
>>         returning incompatible results is somewhat hurting me. Is
>>         there any reason we should allow that ? Any use case ?
>>          
>>         I’m issuing the same concerns for the “has” trap. It think it
>>         should not be a trap. It should always return hasOwn(key) ||
>>         prototype.has(key). Each one of hasOwn and prototype can be
>>         tuned by the proxy, but not the “has” itself. We’re not
>>         removing features, but we prevent bad usage of it. Whatever
>>         the user code do, the ‘has’ behavior will stay logical.
>>          
>>         Another thing I don’t quite understand is the difference
>>         between “keys” and “enumerate”. If there’s no strong
>>         difference, it should be the same trap (same logic: enumerate
>>         should return the concatenation of this.keys and
>>         this.prototype.keys). Having two different but similar traps
>>         will cause confusion. Maybe there’s a need for this
>>         difference however. I just didn’t understand why such a
>>         difference should exist, but at least I see more possible
>>         usages than the first three traps I “contested”.
>>          
>>         Anyway, if we should retain only one thing from the
>>         discussions we already have seen on the Proxy hub, is that
>>         it’s a great feature, but one that still needs some work
>>         before implementation ;-)
>>          
>>         Regards,
>>         François
>>          
>>         *From:* David Bruant <mailto:bruant at enseirb-matmeca.fr>
>>         *Sent:* Sunday, January 23, 2011 4:24 PM
>>         *To:* es-discuss <mailto:es-discuss at mozilla.org>
>>         *Cc:* Mark S. Miller <mailto:erights at google.com>
>>         *Subject:* [ES Harmony Proxies] Fundamental trap definition
>>          
>>         Hi,
>>
>>         I am wondering if getPropertyDescriptor and getPropertyNames
>>         fundamental traps shouldn't rather be derived traps since
>>         they could have a pretty straightforward default implementation.
>>         One implementation of getPropertyNames could be :
>>         --------------------------
>>         Object.getPropertyNames = function(o){
>>             var objectToInspect;
>>             var result = [];
>>
>>             for(objectToInspect = o;
>>                 objectToInspect !== null;
>>                 objectToInspect = Object.getPrototypeOf(objectToInspect))
>>             {
>>                     result =
>>         result.concat(Object.getOwnPropertyNames(objectToInspect));
>>             }
>>
>>             return result.removeDuplicates(); // the removeDuplicates
>>         method is made up, but you get the point
>>         }
>>         --------------------------
>>         This seem to fit the proposal
>>         (http://wiki.ecmascript.org/doku.php?id=harmony:extended_object_api&s=getpropertydescriptor
>>         <http://wiki.ecmascript.org/doku.php?id=harmony:extended_object_api&s=getpropertydescriptor>)
>>         expectations). This could be also the default
>>         getPropertyNames trap implementation.
>>
>>         I haven't really seen a strong definition of what fundamental
>>         traps are in the proposal. On Mark Miller's e-mail
>>         (https://mail.mozilla.org/pipermail/es-discuss/2011-January/012601.html)
>>         is written:
>>         [a trap is fundamental if] there is no coherent default
>>         behavior to fall back to that would be defined in terms of
>>         the remaining traps. (please tell me if I misinterpret what
>>         you meant)
>>         If we're going with this definition, then
>>         getPropertyDescriptor and getPropertyNames should probably be
>>         derived traps since they can clearly be defined thanks to
>>         other traps (respectively getOwnPropertyDescriptorand
>>         getOwnPropertyNamesas I showed above) which seems to be
>>         coherent fallback behavior.
>>         Instead of a formal definition, this could be a rational for
>>         deciding what is a fundamental trap and what isn't.
>>
>>         In the 5 remaining traps (getOwnPropertyDescriptor,
>>         getOwnPropertyNames, defineProperty, delete, fix), I don't
>>         see any that could be defined thanks to the others. They seem
>>         to be also the fundamental actions that one can perform on a
>>         single object:
>>         Property-wise:
>>         - create/configure a property (defineProperty)
>>         - delete a property (delete)
>>         - retrieve a property (getOwnPropertyDescriptor. Can be used
>>         to separate the create and configure cases of defineProperty)
>>         Property-set-wise:
>>         - retrieve the property set (getOwnPropertyNames. More can be
>>         found later on each property with getOwnPropertyDescriptor)
>>         - prevent further extension (and optional reconfigurations
>>         for seal and freeze)(fix)
>>         The prototype can be retrieved thanks to
>>         Object.getPrototypeOf (which cannot be trapped for
>>         consistency purposes) and all actions can be performed by
>>         climbing the prototype chain.
>>
>>         Any thoughts on the fundamental trap definition?
>>         Or on the idea of turning getPropertyDescriptor and
>>         getPropertyNames into derived traps with the suggested
>>         definition?
>>
>>         David
>>
>>         ------------------------------------------------------------------------
>>         _______________________________________________
>>         es-discuss mailing list
>>         es-discuss at mozilla.org <mailto:es-discuss at mozilla.org>
>>         https://mail.mozilla.org/listinfo/es-discuss
>>         _______________________________________________
>>         es-discuss mailing list
>>         es-discuss at mozilla.org <mailto:es-discuss at mozilla.org>
>>         https://mail.mozilla.org/listinfo/es-discuss
>
>
>         _______________________________________________
>         es-discuss mailing list
>         es-discuss at mozilla.org <mailto:es-discuss at mozilla.org>
>         https://mail.mozilla.org/listinfo/es-discuss
>
>
>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20110126/b2227401/attachment-0001.html>


More information about the es-discuss mailing list