On Jan 3, 2013, at 12:18 AM, Tom Van Cutsem wrote:

> 2013/1/3 Allen Wirfs-Brock <[email protected]>
> 
> On Dec 31, 2012, at 3:37 AM, Tom Van Cutsem wrote:
> 
>> [...]
> 
>> 
>> 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.
> 
> I think it's perfectly obvious what it means for an exotic property to be 
> complete: that it is at least a complete data or accessor property (i.e. it 
> either has {value,writable,enumerable,configurable}, or it has 
> {get,set,enumerable,configurable} properties). It may further specify any 
> number of additional custom attributes.

You seem to be assuming that all exotic properties must be an incremental 
variation of a data property or an accessor property.  However, historically 
this hasn't been the case.  When accessor attributes were added to ES they 
weren't just a incremental extension to the attributes of a data property. If 
any new property forms are added to ES in the future, they probably also won't 
be simply extended variations of data or accessor properties.  I would hope 
that proxies could be used to prototype any such future extensions. 

As part of a general extension mechanism, I don't think we should unnecessarily 
limit how a proxy handler chooses to define the interpretation of its property 
descriptors.  It's clear to me that such limitations restrict the utility of 
proxies.  What benefit to we get from those limitations. 
> 
> More generally, I think it is backwards-compatible to extend property 
> descriptors with extra attributes, but it's backwards-incompatible to allow 
> proxies to remove some of the standard attributes from property descriptors. 
> This breaks the assumptions of existing clients.

Since we never fully defined the invariants of Object.getOwnPropertyDescriptor 
it isn't clear what assumptions existing clients should be making.  I think the 
most basic assumption that is justified is that the result of gOPD can be used 
with Object.defineProperty to define a similar property on a different object 
that is the same kind of object as the original.

Sure, a simple forwarding proxy to an ordinary object might break existing code 
if it didn't return complete property descriptors.   That's a user level bug in 
the definition of the Proxy  and can be found and corrected using user level 
testing and debugging techniques.  I don't think we should be adding additional 
runtime time overhead to every proxy [[GetOwnProperty]] call to protect against 
such bugs.  Such checks penalizes correct code and the restrictions they impose 
limits the utility of the proxies.

I believe we should only be dynamically validating invariants that are 
essential for the low level integrity and robustness or the execution 
environment.  Everything else should be treated as as a programmer bugs. 

>  
> 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.
> 
> The use of the internal PD record is fine. My problem lies only with 
> Object.getOwnPropertyDescriptor(proxy,name) exposing a non-normalized 
> descriptor object.
>  
>  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]].
> 
> It seems to me that alone would be reason enough to ban "accessor attributes".

Or for mitigating just this specific integrity hole, as I described below.

>  
> 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.
> 
> I'm uncomfortable with this. It's already exceedingly difficult (for me at 
> least) to think about the correctness of the invariant checks. Having to take 
> into account that property descriptors may be proxies with arbitrary behavior 
> introduces further complexity.

Layers of abstraction...

defineProperty and its internal helpers already have to deal with the fact the 
descriptor objects may contain inherited attribute properties or that they may 
be accessor properties. So, while we might restrict what is returned by Proxy 
[[GetOwnPropety]] we still couldn't restrict what is passed to Proxy 
[[DefineOwnProperty]].

While I think it is unnecessary (and restrictive) I could probably live with 
alternative 3 that I proposed in my last message even with the addition that 
the object produced by the handler must be an ordinary object.

>> 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.
> 
> The scenario I'm worried about assumes that the Proxy is actively trying to 
> confuse a client, so trusting the Proxy to do the right thing is not an 
> option.
>  
>  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.
> 
> Two observations:
> 1) We've been careful to make proxies transparent, and we consider it an 
> anti-pattern for clients to test whether they're using a Proxy.
> 2) Yes, the client defending itself by normalizing the descriptor explicitly 
> would help, but my point is that there is extent ES5.1 code that does not 
> currently do this because it's currently not necessary.

Except that transparency probably isn't appropriate in situations where the 
client is highly defensive.  Do we currently have a isProxy test?  It seems 
that it would be essential in such situations.

Does any such code actually exist.  Does it depend upon anything other than the 
"configurable" property?  Does such code, if it exists, work correctly with DOM 
objects in all implementations?

>  
>  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.
> 
> Because existing code can assume that "desc.configurable", "desc.writable", 
> etc. are stable values, and changing these into accessors breaks those 
> assumptions.
> 
> I think of Object.getOwnPropertyDescriptor as the high-integrity, 
> high-fidelity part of the reflection API. This is the API that 
> security-conscious code should use when it manipulates untrusted objects. It 
> can only be a high-integrity reflection API if the return value can be 
> reliably depended upon.

Consider me as representing the programmers whose primarily interest in Proxies 
is for extending ES via metaprogramming.  From that perspective I think of 
O.gOPD  (along with Object.defineProperty) as the primary user facing APIs that 
allow regular programmers to configure a wide range of exotic objects that I 
might invent.  I don't want to be restricted to making all my properties look 
like some variant of existing data properties or accessor properties.  Nor do I 
want to arbitrary transformations made to property descriptor I may choose to 
define.  I'm perfectly happy for place into the hands of security-conscious 
coder the tools they need to detect and even reject my exotic objects. If my 
uses are incompatible with your high integrity code, I'll just run my code 
somewhere else.  I want to be sure that you are enabled to create what ever 
short of high integrity environment you want but please don't place 
restrictions on what I can do when I'm not in your environment

>> 
>> 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.
> 
> I don't see how existing ES5.1 code manipulating a property descriptor with 
> *extra* custom attributes would break, if it only ever touches the standard 
> attributes (which should be most of the code manipulating descriptors out 
> there).

Code that for-ins and switch dispatches over a property descriptor will see 
extra attributes. 

> 
> In the draft Proxy spec on the wiki, custom attributes are simply copied onto 
> the normalized descriptor as part of the normalization process.
> It's worth noting that they are always copied as data properties. I think 
> this makes for a good guideline: property descriptors are objects with just 
> data properties.

I'm concerned about two things:
   1) copying is expensive and will almost always be unnecessary
   2) You are assuming that you can meaningfully turn my exotic properties into 
either data descriptors or accessor descriptors and that when you hand them 
back to me I won't care.  Here's an example where I would care.  I add a kind 
of exotic method property whose descriptor has a "method" property.  Methods 
are never enumerable or writable.  I want to throw an error with somebody codes:
     Object.defineProperty(pObj, "myMethod", {enumerable: true, writable: 
true,method:func, configurable: true});  //this throws because enumerable or 
writable methods aren't allowed
Instead they should have said:
     Object.defineProperty(pObj, "myMethod", {method:func, configurable: true});

If you normalize as described in the wiki, then this will fail:
   Object.defineProperty(pObj2,"myMethod", 
Object.getOwnPropertyDescriptor(pObj, "myMethod"));

> 
> If ECMAScript would have had an actual record value type, I'm confident 
> property descriptors would have been reified as such. Since we only have 
> objects, descriptors were reified as objects, but that brings with it 
> features (mutability, behavior, ...) that seem to get in the way here.

Perhaps, but I fine with using objects. For example, some people have made 
reasonable use of property descriptor objects that are populated via 
inheritance.

>> 
>> 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.
> 
> If we make it clear that property descriptors are records (i.e. contain only 
> data properties), and normalization entails:
> 1) ensure all standard attributes are present and refer to values of the 
> appropriate type
> 2) copy any custom properties
> 
> Then I think the contract is clear and custom descriptors will round-trip 
> fine.

see above


>> 
>> 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.
> 
> I would have agreed to this if it weren't for the fact that there is probably 
> ES5.1 code out there that already tries to be correct in the presence of 
> untrusted code, and doesn't know about or use this primitive. I hope MarkM 
> can point at some relevant sources.

I'd guess that there probably isn't much or any such code in the wild.  If we 
can determine that then perhaps we don't really have a significant issue here.

>  
> 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. 
> 
> This still violates the ES5.1 assumption that all descriptors returned by 
> getOwnPropertyDescriptor are guaranteed to be complete.

Again, I don't see why this is an issue.  It is either a case of the Proxy 
handler implementor is intentionally redefining what "complete" means for their 
exotic properties or it is a run of the milll bug in the handler.  I don't see 
why such bugs is of much concern to us since missing attributes default to 
their "high integrity" state when used to define a new ordinary object property.

>  
> 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.
> 
> I won't deny there's a performance tax here, either by verifying that the 
> descriptor is really a "record" (i.e. has only data properties), or by making 
> an explicit copy. Like all other invariant checks, I agree they will be 
> wasteful for "benign" proxies.
> 
> Then again, let's place things in perspective: when will this become a perf 
> issue? When querying a large number of proxies for their own property 
> descriptors. But then any heap containing a large number of proxies probably 
> already has other perf issues to worry about?

My experience as a language runtime implementor is that all non-essential 
validity check at low levels of a runtime are undesirable.  You simply never 
know which language features are going to be used by someone in some 
performance critical situation so it is good practice to push any non-essential 
checks to higher levels where they can be avoid. 

I wouldn't want to be in the position where some part of the DOM or other 
interesting native library can't, in practice, be self hosted because of 
performance reasons that trace to unnecessary copying of descriptor objects.  I 
don't know that it will occur, but my experience suggests that it could.

To wrap up, I think the record-like invariant check would be acceptable as we 
are already inspecting the descriptor object to build the corresponding PD 
record.  I'm strongly \ opposed to any mandated object copying or normalization 
of exotic descriptor objects to include missing data/accessor property 
attributes.

Allen

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

Reply via email to