Reflection of global bindings
allen at wirfs-brock.com
Sat Dec 15 13:52:38 PST 2012
On Dec 15, 2012, at 12:12 PM, Brendan Eich wrote:
> Domenic Denicola wrote:
>>> From: es-discuss-bounces at mozilla.org [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Brendan Eich
>>> Sent: Saturday, December 15, 2012 14:26
>>> Reflecting var and function bindings on window (or |this| or |self| or other aliases) as configurable properties, but refusing to allow reconfiguration, is a solution, however distasteful. Allowing configuration is no good, of course.
>> As someone who's trying desperately to follow along with all these subtle issues from the sidelines... Why is allowing configuration of such properties no good?
> Becausethe only integrity guarantee in JS prior to ES5 was via var and function making non-configurable bindings. This is used especially in closures, e.g. You can't delete a var- or function-induced binding not created from eval. But it is also used at the top-level, not only by code but also by VMs that can optimize accordingly (no delete, the "slot" can't change).
> It may seem silly to reflect the global bindings as configurable but refuse configuration attempts, but that satisfies the invariant at stake. Reflecting the function and var bindings non-configurable but allowing apparent reconfiguration via WindowProxy-forwarded navigation breaks the invariant.
Property attributes were originally a specification device (that may have also existed in implementation) that were used to describe the semantics of certain ES operations. Originally there was no direct way for ES code to set or inspect (except for DontEnum) the state of attributes. For example, certain properties were specified to have a DontDelete attribute and the delete operator was specified to never delete such properties. There was no way for ES code to give an arbitrary attribute a DontDelete attribute. There was no specified guarantee that a property with that attribute wouldn't disappear (except not via the delete operator) and no specified guarantee that a property with a DontDelete attribute would always continue to have the attribute. In fact, the ES3 spec. explicitly said that the internal methods such as [[Delete]] for "host objects" could be "implemented in any manner (implicitly this would include disregarding attributes) and even gives an example of the internal methods of a host object producing inconsistent results. This is the world in which WindowProxy and other DOM behaviors originally evolved.
In ES5, we added reflection operations that allow ES code to inspect and modify the attributes of any property of any object. We also reinterpreted DontDelete as [[Configurarable]]:false. In the early drafts of what became ES5, that was all we did. The attributes still were used to describe the semantics. of various ES operations. In ES5, that set of operations expanded to include the reflection operations for modifying attributes. For example, [[Configurable]]: false prevented the Object.defineProperty operation from being used to change the attribute values of a property. However, that was as far as the original ES5 draft changes went. Host objects (and ES extensions) were still allowed to do pretty much anything they wanted.
Fairly late in the development of ES5 (by my recollection) MarkM championed the position that the property attribute values exposed via Object.getOwnPropertyDescritor were more than just specification control points. His position was that they exposed certain absolute invariants to which all objects, including host objets, must conform. This led to a small set of invariants being include in section 8.6.2 of ES5. This included the invariant that if a property can observably disappear, its [[Configurable]] internal property must observably have a value of true. MarkM also consistently emphasized that the required invariants did not imply that their converse was also an invariant. For example, the fact that a property was observably configurable did not imply that the delete operation could actually be used to remove that property from its object.
We added something and lost some by introducing those invariants. We added some certainty about whether a few specific kinds of observable state transitions are allowed or not. However, in the spec. we have essentially lost the ability to use the attribute values as control points for some of the specification algorithms. For example, if a property may be non-deletable, even though its [[Configurable]] attribute is true, then the delete operation can't use the value of the [[Configurable]] attribute to determine whether or not it is allowed to actually remove the property and whether it should return true or false as its result.
So, to me, it sounds like that to continue down this path we should really add new non-reflected properties attributes that are the real control points for the ES semantics. Eg, we may need [[RealReadOnly]], [[RealDeletable]], and [[RealReconfigurable]] attributes to describe all the states and state transitions that are actually exist within the legacy DOM (and the pure ES global declaration semantics). As these attributes would not be reflected by Object.getOwnPropertyDescriptor/Object.defineProperty they would have to set in some other internal manner when object instances are created. Tis also means that Proxy based object implementations would also need to have some mechanism for emulating these "Real" attributes.
So, it feels to me like we are circling about to the a place similar to where we were prior to ES5. We need specification/implementation layer control points (ie, hidden attributes) that are not directly observable or controllable by ES code (except for Proxy handlers). Plus now we also have a set of different reflectable attributes that have some non-obvious relationship to those hidden attributes. I'm not sure that this is actual progress.
More information about the es-discuss