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

