On Dec 20, 2012, at 12:07 PM, Tom Van Cutsem wrote: > 2012/12/20 Allen Wirfs-Brock <[email protected]> > > On Dec 20, 2012, at 2:21 AM, Tom Van Cutsem wrote: >> If target.baz is a writable data property >> proxy.baz = 42 will eventually call Object.defineProperty(proxy, 'baz', >> {value:42}) >> (and *not* Object.defineProperty(proxy, 'baz', >> {value:42,enumerable:true,writable:true,configurable:true}) as it did >> previously) >> This behavior is consistent with the method and accessor case: in all cases, >> the target delegates back to the proxy. > > This is actually essential to maintaining ES5 default semantics because > {value:42} and {value: 42, enumerable:true,writable:true,configurable:true} > have quite different meanings to the ordinary [[DefineOwnProperty]]. > > Indeed. And this is what caused the issue I reported in the OP. > > > It is equally important that if target.baz does not exist that > [[DefineOwnProperty]] is called on proxy with with the full {value: 42, > enumerable:true,writable:true,configurable:true} descriptor to ensure that > the new property gets created using the "crated by assignment" attributes > rather than the [[DefineOwnProperty]] defaults. > > Yes, this is the case. >> One thing that I learned from all this is that it's simpler to think of the >> proxy as *delegating* (as opposed to forwarding) to its target by default. >> And under the semantics of invoke = get + apply, that is actually the >> simplest option. > > Yes, this is the best way I have found to think about them and I've been > patiently waiting for you to see the light :-) However, a corollary is that > most internal method calls within derived internal methods/traps need to > delegate back to the original "receiver". In some cases, we don't provide > the original receiver as an extra argument, so it isn't available to do this. > > A quick scan of the ordinary internal method suggests that we may have this > problem for [[Delete]], [[Enumerate]], [[Keys]], and [[GetOwnProertyKeys]]. > > More generally, I would argue that all Proxy traps (and the corresponding > Reflect functions) potentially need access to the original "receiver". We > don't know what somebody is going to do in such traps and they well need to > call back to the original receiver for the same sort of consistency issues we > have encountered with [[SetP]]. this is particularly apparent if you think > multiple levels of proxies chained through their target slots. > > I'm not sure I follow. In my understanding, the original Receiver is only > needed for traps that involve prototype-chain walking and are thus > |this|-sensitive. That would be just [[GetP]] and [[SetP]]. One can make the > case (David has done so in the past) for [[HasProperty]] and [[Enumerate]] > since they also walk the proto-chain, although it's not strictly necessary as > the language currently does not make these operations |this|-sensitive.
The proxy target delegation chain is also this-sensitive when it invokes internal methods. For example, in the revised [[SetP]] step 5 it is important that the [[DefineOwnProperty]] calls (in 5.e..ii and indirectly in 5.f.i are made on Receiver and not O. Let's take a simple case, the [[Keys]] internal method. It needs to do a [[GetOwnProperty]] call for each property to determine whether or not it is enumerable. If [[Keys]] is invoked on a Proxy and automatically delegated to the target, should the [[GetOwnProperty]] call be made to the target or to the original receiver (the proxy)? If it is invoked on the target, we will get a list of what the target thinks are its enumerable properties. The proxy, itself, might have a different idea of which of its properties are enumerable. Since the original operation was invoked on the proxy, we presumably want [[Keys]] to tells us what that object (the proxy) thinks are its enumerable own properties not what the target actually has as enumerable own properties. The latter could actually be leaking a secret that the proxy is trying to keep. Here is another way to think about it. Both the ES prototype chain and the ES proxy target chain can be view as examples of Lieberman style delegation, but at different abstraction levels. Prototype delegation is about delegation of ES functions. The self-calls take place at the level of ES functions and the implementation layer is careful to pass the correct this values to inherited (ie delegated) functions. The mechanisms for describing the semantics (and perhaps implementing it) uses the ES internal methods. But, for prototype delegation purposes, the internal methods do not make true self-calls. Historically, ES internal methods are not inherited/delegated along the prototype chain. This is why the Receiver needs to be passed as an explicit parameter to the internal methods that are responsible for implementing ES function level self-call semantics. For proxies, things are flipped. The delegation isn't at the level of ES functions, instead it is at the level of internal methods. The self-calls that are of interest are not to ES level methods but to internal methods. That's why [[Keys]] really should be doing a self-call of [[GetOwnProperty]] through the original receiver rather than to the same target object that fielded the [[Keys]] call. Similar situations exist within [[Delete]]. If you step back a bit and just think about the concepts of Lieberman delegation and self-calls without worry about the specific of the proxies or the ES MOP I think you will come to see that delegated target calls naturally should self-call back to the original object. That's what Lieberman style delegation is all about. Or, here's another way to look at it. You need real self-calls that go back to the original receiver whenever you have an interface with interdependent operations (some of which may be "derived" and other may be "fundamental") and where you permit individual operations to be selectively delegated. Otherwise, leaf objects won't present a "self"-consistent set of operations. This problem goes away, if you do not allow delegation at the individual operation level, but instead only permit either all operations or no operations of the interface to be over-ridden/delegated. This "no individual over-rides" solution is essentially what the ES spec. historically applied to internal methods. But direct proxies now allow over-rides at the individual internal method granularity. This exposes the sorts of inconsistencies I'm describing. Allen > > Cheers, > Tom
_______________________________________________ es-discuss mailing list [email protected] https://mail.mozilla.org/listinfo/es-discuss

