On Dec 31, 2012, at 3:37 AM, Tom Van Cutsem wrote:

> 2012/12/30 Allen Wirfs-Brock <[email protected]>
> 
> On Dec 29, 2012, at 2:37 PM, Tom Van Cutsem wrote:
>  > * 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.
> [...]
> 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.
> 
> Yes, I agree to all of this and I understand this is the intent of the new 
> [[Origin]] field of internal property descriptors.
> I also agree there's no problem for normal objects, which continue to always 
> cons a fresh property descriptor object.
>  
> 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. [...]
> 
> This was not the breaking change I had in mind and I agree with you that 
> adding new attributes is both useful and supported by the ES5.1 design.
> 
> Here's the breaking change I had in mind:
> 
> var propDescMap = {};
> var proxy = Proxy({}, {
>   defineProperty(target, name, desc) { propDescMap[name] = desc; return true; 
> },
>   getOwnPropertyDescriptor(target, name) { return propDescMap[name]; }
> };
> 
> // client1 adds:
> var pd1 = {
>   get configurable() { return Math.random() < 0.5; }, // return true or 
> false, randomly
>   custom: true
> };
> Object.defineProperty(proxy, "foo", pd1);
> 
> // client2 queries:
> var pd2 = Object.getOwnPropertyDescriptor(proxy, "foo");
> 
> If I understand correctly, pd2 now refers to exactly the same object as pd1, 
> preserving the custom attribute.

Note that you don't need to start with the defineProperty call using pd1 to get 
the following effects.  The same observable behavior could occur just by 
defining the getOwnPropertyDescriptor trap to directly construct and return a 
similar value for pd2.

> 
> However, this semantics also implies that:
> 1) pd2 is not a completed property descriptor, lacking a writable/enumerable 
> attribute. Only the internal property descriptor was fully completed, not the 
> [[Origin]] object.

We can't say what "complete" means for a property descriptor containing custom 
attributes.  Just like the complete PD for an ordinary accessor property 
doesn't contain a "writable" property, perhaps a PD for an exotic "custom" 
property only have "configurable" and "custom" attributes. This is all up to 
the designer of the exotic property and something they should define when they 
describe the contract of their exotic object.

Yes, the internal PD record will be completed but it is only used for a 
sequence of pseudo-code algorithm steps.  I don't currently see any place where 
the internal PD record is used in such a way that we have any problems.  Of 
course, the proxy itself should provide consistent 
Get/Set/defineProperty/getOwnPropertyDescriptor behavior for such exotic 
properties. If it doesn't its a ES programmer level bug.

> 2) pd2.configurable is not a data property, but may change randomly

So?  That's the nature of accessor properties although it's not how they are 
normally used.  Why are these values being unstable any worse than any others?

The one thingI can imagine is that a proxy author could use an "configurable" 
attribute accessor to sneak past the configurable invariant check in Proxy 
[[GetOwnProperty]]. I can imagine that we might want to normalize the 
"configurable" property (only for the situation where their is a corresponding 
target property??) of the descriptor object, but no other properties.

> 3) since pd1/pd2 refers to a mutable object, changes made by client1 will be 
> visible to client2 and vice-versa.

Again, this is the nature of programming with object references. If the Proxy 
wants to prevent this, it shouldn't store the descriptor objects -- just like 
the spec. doesn't store the PD record that is used to create a property.  If 
client2 is worried about this and doesn't trust the Proxy (in which case it 
probably shouldn't be using it...)  in can always do its own 
cloning/normalization.  Regardless, the values of pd2 can't asynchronously 
change out from under client2.  client2 is in control and presumably should be 
aware of the possible side-effects of anything it calls.

Basically, I'm saying I don't see why the "hazards" of an accessor are any 
greater here than in any other random ES code.

> 
> None of these behaviors are possible in ES5.1, so clients of 
> Object.getOwnPropertyDescriptor currently don't (need to) guard against these.

The change is the possible occurrence of exotic properties.  Regardless of any 
normalization of ES5 ordinary property attributes, existing code is not going 
to be prepared to handle exotic properties in any specific way. Even for new 
code where the client knows it could potential be accessing a proxy provided 
descriptor object, I don't know that it would be worth guarding against these 
things.  Any object returned from any function may potentially be buggy and 
violate the assumed contract of the function.  Most code doesn't and should 
actively defend itself against such potential bugs.

> 
> The issue is that in 8.5.6 Proxy [[GetOwnProperty]], only the "resultDesc" is 
> normalized, not the trapResultObj that is its [[Origin]]. The 
> FromPropertyDescriptor operation blindly returns the [[Origin]], disregarding 
> the normalized descriptor.

Yes, because I don't know what it means to "normalize" an arbitrary exotic 
property descriptor.  The primary purpose of such exotic descriptors is to 
convey from the Proxy handler to the client information about an exotic object 
and its exotic properties.  It is also used to by the client to pass exotic 
property attributes back to a handler.  Arbitrarily normalizing  an exotic 
descriptor produced by a getOwnPropertyDescriptor trap might result in turning 
into something that would be treated as an invalid exotic descriptor when 
passed back to a corresponding defineProperty trap.

> 
> To rescue the [[Origin]] design, the most straightforward fix I can come up 
> with is that FromPropertyDescriptor first normalizes the [[Origin]] object 
> before returning it (i.e. verifying that it is complete, or making it 
> complete, and ensuring the standard attributes are data properties). Even 
> then so, it's messy that these side-effects are visible to end-user code (the 
> pd1 object of client1 would get mutated-at-a-distance as a result of 
> returning it from a getOwnPropertyDescriptor trap).

Here is a list of alternative, I can envision, ordered (from my perspective) in 
decreasing desirability:

1) Provide a Reflect.normalizePD(desc) function that produces a new descriptor 
object via 
ToPropertyDescriptor(CompletePropertyDescriptor(FromPropertyDesriptor(desc),undefined)).

     Anybody who is paranoid about encountering exotic descriptors, malformed 
descriptors, or accessor properties can use it.

2) Normalized only the "configurable" property.  Make it an invariant that 
"configurable" is always present and is a data property.  Either normalize it 
to that or throw as an invariant violation like for other proxy invariants.

    This seems like the only property attribute whose setting is important for 
the integrity use cases, so like elsewhere in the proxy design only enforce 
that single invariant. 

3) Make it an invariant that all own properties of the returned data property 
are data properties.

    This gets rid of the accessor hazard.  However this puts additional extra 
checking into the proxy [[GetOwnProperty]] that is almost always going to yield 
a negative result.  In other words, you a placing a performance tax on the 
usual case with little usual benefit.  Unless there is real integrity hazard 
that can't be explicitly mitigated where it matters, we should just treat such 
situations like misuse of accessors as user level bugs.

Allen

> 
> The only other alternative I see is to create normalized copies, as specified 
> in the draft proxy spec on the wiki.
> 
> Cheers,
> Tom

_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to