I wrote up a draft strawman for making this change: < http://wiki.ecmascript.org/doku.php?id=strawman:proxy_drop_receiver>.
2011/4/28 Tom Van Cutsem <[email protected]> > Hi, > > I think Sean is correct: under the ES5 semantics, the "receiver" argument > to "get" and "set" is not required, because "get" and "set" are not supposed > to be invoked on a proxy acting as a prototype. In both cases, property > lookup is indeed via [[GetProperty]]. That means "get" will only ever be > invoked with |receiver === proxy|. > > Let me try to reconstruct how we ended up with the current API. > > When Mark and I sketched out the initial API, we reasoned that the > [[GetProperty]] internal method for Objects would never be called on a > Proxy. All the algorithms where this method was invoked ([[Get]], [[Has]], > [[Put]]) would all be replaced by calling a trap instead. Also, this was > before Object.getPropertyDescriptor was proposed, and before missing traps > (such as 'has') would fall back on derived traps. I even think there was no > 'getPropertyDescriptor' trap at that point in the API. > > So, with [[GetProperty]] bypassed, the only sensible traps the engine could > invoke during property lookup when encountering a proxy in a prototype chain > were [[Has]] to query the proxy, and then [[Get]] or [[Set]] to perform the > access/assignment. > > I just noticed, a remaining artifact of that design can still be seen in > the description of [[GetProperty]] for proxies: < > http://wiki.ecmascript.org/doku.php?id=harmony:proxies_semantics#detailed_semantics_of_behavior_for_object_and_function_proxies>. > I think [[GetProperty]] for a Proxy should now simply invoke the > "getPropertyDescriptor" trap. > > > That said, I also had a look at my test suite for proxies and studied the > behavior on the latest tracemonkey version. > > First, there was already a test for delegated property access, but only for > function properties, not for accessor properties: < > http://hg.ecmascript.org/tests/harmony/file/b99e5976db56/proxies/TestCases/invokeDelegator.js > > > > I just extended this test case to also check whether, in the function > returned by "get", |receiver === this|. This is indeed the case, so proxy > writers could use the nested |this| instead of |receiver| to access the > delegating object. So even without a "receiver" argument, proxies would be > able to transparently forward "method invocations" (function property > access) without affecting the |this|-binding. > > Triggered by this discussion, I added a test for delegated accessor > property access: < > http://hg.ecmascript.org/tests/harmony/file/b99e5976db56/proxies/TestCases/delegatedAccessor.js > > > > With the getPropertyDescriptor trap now in place, it turns out this trap is > indeed all one needs for correct transparent forwarding of access/assignment > to accessor properties. > > This test also revealed an interesting asymmetry in the current tracemonkey > behavior: > |delegator.foo| will call the 'get' trap of the proxy if it's implemented. > This behavior does require 'get' to take the 'receiver' as argument, > otherwise it can't transparently forward the access. However, |delegator.foo > = 24| does not appear to call the 'set' trap, even when provided, but > instead always calls 'getPropertyDescriptor'. > > Long story short: > - with the 'getPropertyDescriptor' trap in place, this seems to be the > preferred trap to be invoked on a proxy on the prototype chain during > property lookup/assignment (following ES5 semantics) > - Hence, 'get' and 'set' will never be called in a situation where > |receiver !== proxy| > - Hence, "receiver" is, strictly speaking, unnecessary. > > Cheers, > Tom > > 2011/4/28 David Bruant <[email protected]> > >> Le 28/04/2011 10:32, Andreas Gal a écrit : >> >> On Apr 28, 2011, at 1:26 AM, David Bruant wrote: >>> >>> Le 27/04/2011 23:09, Sean Eagan a écrit : >>>> >>>>> As explained before, the existing ES5 semantics would cause the proxy's >>>>> "getPropertyDescriptor" trap to be called thus obtaining any "getter" >>>>> / "setter" that the proxy wants. The |this| binding of this "getter" >>>>> / "setter" will then be set to the "receiver" by ES5 8.12.3 step 13 >>>>> for a "getter" or ES5 section 8.12.5 step 5.b for a "setter". The >>>>> proxy's "get" / "set" trap would not get called, and thus would not >>>>> need the "receiver" arguments. >>>>> >>>> Interesting. What you're saying is that if a proxy in on the prototype >>>> chain, then its "set" and "get" traps are never called by a get or set on >>>> the base object. >>>> >>>> In any case, in any example we could write, the |this| binding is >>>> correct not because of the get/set trap on the prototype chain, but because >>>> of the |this| binding that is performed at the own layer level. >>>> >>>> I think you're right on removing the receiver argument. >>>> It should be noted that on that page [1] there is a consensus that a >>>> receiver argument should be added to all proto-climbing traps. I don't >>>> think >>>> we've had the explanation of this point yet. If I recall correctly, this >>>> necessity was raised by Andreas Gal (CC'ed). SpiderMonkey-related bug [2] >>>> >>> Actually we are still going back and forth on this. Proto-climbing might >>> be the wrong selector here. "Can call a getter or setter" seems to be the >>> better category, and that would be only get and set. get and set definitely >>> do need the receiver though. If you have a proxy on the proto chain and you >>> don't find what you are looking for in the direct object, the next step is >>> invoking get on the prototype (proxy), and you need the proper receiver if >>> you have to call a getter (the direct object, not the proxy). >>> >> Sean's point is that, as per ES5, in the case you are describing the >> prototype climbing doesn't occur with the get/set trap on the prototype >> object, but rather with getPropertyDescriptor (ES5 - 8.12.3 step 8 (which is >> the first step of the algorithm. Wrong numbering)). The |this| binding then >> occurs during the get/set call of the base object. >> >> According to ES5, the following example should throw an error: >> --- >> var handler = { >> has: function (name) { >> return name == 'foo'; >> }, >> get: function (rcvr, name) { >> if (name != 'foo') >> return undefined; >> print(proxy !== rcvr); >> return "bye"; >> }, >> }; >> >> var proxy = Proxy.create(handler); >> var c = Object.create(proxy); >> print(c.foo); >> --- >> As per ES5, "c.foo" should call c.[[Get]] (1) which should call >> c.[[GetProperty]] (2). This call should fail at finding "foo" at the own >> layer and should recursively call proxy.[[GetProperty]] (3) (not >> proxy.[[Get]] ! The example should throw an error, because handler doesn't >> have a getPropertyDescriptor trap to reify proxy.[[GetProperty]]). When the >> property descriptor is found and returned from (3), it is returned to (2) >> then to the c.[[Get]] call (1). If the property descriptor happened to be an >> accessor descriptor, then the |this| binding should be perfomed at the end >> of (1) where c.[[Get]] is called and where there is no need to leak the c >> object (misnumbered 11-13 steps of ES5 - 8.12.3). >> >> As a consequence of the call sequence I have described, Sean's point is >> that there is actually no need for a receiver argument even in get and set >> traps. >> >> Cheers, >> David >> > >
_______________________________________________ es-discuss mailing list [email protected] https://mail.mozilla.org/listinfo/es-discuss

