General comments resplnse (was Re: ES6 Rev13 Review: MOP-refactoring, symbols, proxies, Reflect module)

Allen Wirfs-Brock allen at
Sun Dec 30 14:03:02 PST 2012

My response to the general comments are below.  I'll respond to comments for specific sections in separate messages. 

On Dec 29, 2012, at 2:37 PM, Tom Van Cutsem wrote:

> Hi,
> I did a thorough revision of Allen's Rev12 and Rev13 drafts which incorporate (among many other things) the refactored MOP, symbols, proxies and the Reflect module in the ES6 draft.
> Below is a long list of detailed comments, both editorial and more substantial in nature.
> It's too cumbersome for me to file bugs for every individual comment. Allen, please let me know what comments are worth filing a bug for.

This that may be subject to debate I'll wait until we seem to have resolution.  Others, I'll either immediately fix or file a bug.

> Before delving into the details, two more things:
> First, thank you Allen for the great spec work. Overall I'm very happy with the way proxies have been incorporated into the ES6 draft.
> Second, if people want to comment on a particular issue, I suggest to rename the topic and fork off a separate thread. That will be more manageable, and will allow people that don't want to read through this whole list to follow the separate discussions.
> Cheers,
> Tom
> == Comments based on ES6 draft Rev. 13 (21 dec. 2012) ==
> === General remarks ===
> * [[GetP]]/[[SetP]] => rename these to just [[Get]] and [[Set]]?

That's the plan, I just wanted to stage them through this transitional name to make sure any confusion is minimized

> * [[GetInheritance]]/[[SetInheritance]] => why not [[GetProto]]/[[SetProto]]?
>   - More familiar to ECMAScript programmers
>   - No risk of confusion with function "prototype" property
>   - For ordinary objects, [[GetInheritance]] returns the value of the [[Prototype]] field

[[GetProto]]/[[GetProto]] are plausible names. I struggled with these names.  I didn't want to use [[GetPrototype]]/[[SetPrototype]] possibly implying direct access to the [[Prototype]] internal data property because not all objects have that internal property and I also want to avoid confusion with the prototype property of  constructors.

I also considered GetParent/SetParent.

The way I arrived at "Inheritance" was from the internal use cases of these MOP methods.  The primarily use case for the Get is in climbing the "inheritance chain" in [[GetP]], [[SetP]], and   [[HasProperty]].  Note that these operations don't really care what model of inheritance (prototypal, class, multiple, etc.)  is implemented by an object.  They just need to ascertain which object they should delegate the lookup when they can't directly satisfy it.  Also, within the spec. we use the term "inherited property" as the alternative to "own property".  So, from that perspective, getting/setting an object's inheritance relationship seems like an appropriately abstract description of the purpose of these MOP methods.

That's said, I'm not in love with these names so I'd be happy to use some other names if we get some consensus building around them. 

> * [[SetInheritance]]/Reflect.setPrototypeOf: I'm not sure this was agreed upon. Especially since __proto__ is currently specified as a data property. This means there is no setter that separately reifies the ability to set the prototype. Thus, it's perfectly possible to just exclude [[SetInheritance]] and Reflect.setPrototypeOf from the spec.

No, [[SetInheritence]] needs to be part of the MOP.  Certainly to define the semantics of __proto__ takes more than just saying it is a data property. Regardless of whether  it is ultimately reflected as that or as an accessor property the semantics of setting it has to be specified in some manner that likes it to the value that is retrieved by [[GetInheritance]].  Also, there are already in the spec. at least three forms of objects (ordinary, exotic symbols, exotic proxies) with different implementations of [[SetInheritance]] so an internal MOP method has already proven to be a useful specification device.

Whether or not it is reflected as a Proxy trap is a separate matter.  When this was discussed recently on es-discuss, the point was made that even if [[SetInhertance]] intersession by a trap is disallowed (ie, a trap can't prevent [[SetInheritance]] with the same value from being automatically invoked on the target) it is still important to get a notification that [[SetInheritiance]] has occurred. This is similar, to other traps whose handler behaviors are restricted but were still deemed useful from a notification perspective.  I believe that the conclusion of that discussion we seemed to have agreement that [[GetInhertiance]] should be handled in this same manner.

Reflect.setPrototypeOf is not essential and simply there to provide a point of further discussion. I think it is desirable that the Reflect.* interface be an accurate reflection of the internal MOP.  Any and all, restrictions that are applied to __prototype__ can also be applied to Reflect.setPrototypeOf.  In fact, the specification of it can be essentially:

  function setPrototypeOf(target, parent) {
    target.__proto__ = parent

I'm also of the school that if we are going to accept a mutable inheritance chain as a standard part of the language then we might as well embrace it rather than treating is as the black sheep that we try to pretend isn't really there. 

> * I'm a bit uncomfortable with the removal of property descriptor normalization in the getOwnPropertyDescriptor/defineProperty traps. Especially for getOwnPropertyDescriptor I think it's a breaking change w.r.t. ES5.1.

To summarize, the [[GetOwnProperty]] internal method of proxy objects is now (in the spec draft) specified to take the descriptor object returned from the  getOwnPropertyDescriptor trap and to package it up as an internal property descriptor record that is populated in the normal manner from the descriptor object.  The process of creating this PD record performs the same error checks that are performed (in ES5.1) by Object.defineOwnProperty.  In addition, the PD record now captures a reference to the original descriptor object.  If that PD record is subsequently used in a context where a descriptor object is required, the original captured descriptor object is used.  This is essential for expressing the semantics of Object.getOwnPropertyDescriptor in terms of the MOP while still enabling Proxies to extend of domain of property attributes.  Object.getOwnPropertyDescriptor(obj) calls [[GetOwnProperty]] on obj which yields a PD record.  It then calls the abstraction operation FromPropertyDescriptor to produce the descriptor object that is its return value.  If obj is an ordinary object, the PD record produced by ordinary [[GetOwnProperty]] does not have a captured descriptor object so FromPropertyDescriptor cons up a new descriptor object that is a direct reflection of the PD record.  If obj is a proxy then [[GetOwnProperty]] will return a PD record that captures the descriptor object that was returned by the trap.  FromPropertyDescriptor simply returns that captured descriptor object which becomes the value of the original Object.getOwnPropertyDescriptor call.

This permits things like:

Object.defineOwnProperty(pObj1,"foo", {method: func});  //define property on a proxy-based object, that have "method" properties
console.log (Object.getOwnPropertyDescriptor(pObj1,"foo").method);    //we can retrieve the value of the method attribute (if the proxy supports it)

Object.defineOwnProperty(pObj2,"foo",Object.getOwnProperty(pObj1, "foo"));  //copy a method properry from pObj1 to pObj2

If descriptor object with extended attributes is applied to an ordinary object, it is always first internally converted to a PD record.  PD records only contain fields for the ordinary attributes, and any operations upon ordinary objects will have no visibility  of  the extended attributes.

The only breaking change (relative to ES 5.1) possibility I see must start with the assumption that  ES5.1 property attributes are the final definition of property descriptor objects and that additional property attributes can never be added to the language (by the spec., not just via proxies) using any of the pre-existing ES5.1 APIs.  That seems quite unreasonable and was certainly not the intent when we introduced the reflection API into ES5.  In practice, ES code that manipulates property descriptors looking for specific attribute properties just won't see any extended attributes. Any code that iterates over all descriptor object properties  (for example, to display them in a debugger)  will see them and treated them however the code is currently written, perhaps ignoring them, perhaps signaling an error, perhaps just processing then as a key/value pair.  In any case, the result will be exactly the same as if somebody manually constructs  such a descriptor object (such as in the first line of the example above) and passes it into that same code today.

More information about the es-discuss mailing list