David Bruant david.bruant at labri.fr
Tue Sep 6 07:35:06 PDT 2011

Le 06/09/2011 16:12, Tom Van Cutsem a écrit :
> 2011/9/3 David Bruant <david.bruant at labri.fr 
> <mailto:david.bruant at labri.fr>>
>     If i recall correctly, this proposal was on hold because of the fixed
>     properties proposal (only getPropertyDescriptor I think).
>     The idea was that if there is a getPropertyDescriptor trap, then it
>     became impossible (or it would just require some proto-climbing with
>     some performance issue?) to guarantee the invariants of
>     non-configurable
>     properties. But now that I think about it, i can't remember
>     exactly what
>     was the annoying use case.
>     Adding Tom in copy. Maybe he'll remember.
> The issue was that with the earlier fixed properties proposal (the one 
> that stopped trapping access to cached non-configurable properties), 
> the proxy would also cache inherited non-configurable property 
> descriptors, and in doing so the properties were converted into "own" 
> properties of the proxy (Object.getOwnPropertyDescriptor would find 
> them in the cache).
> In the latest design of the fixed properties proposal, inherited 
> non-configurable properties are simply no longer checked.
> So the short answer: the getPropertyDescriptor trap is no longer 
> problematic, so neither should Object.getPropertyDescriptor.
> If you want to understand why I think getPropertyDescriptor is no 
> longer problematic in the latest design, read on (apologies for the 
> lengthy explanation, this stuff is hard to formulate concisely!)
> - When the getPropertyDescriptor trap returns a non-configurable 
> property, in the absence of any other information, the caller cannot 
> know whether that property is an own or an inherited property. Concretely:
> var desc = Object.getPropertyDescriptor(obj, "foo");
> // does "desc" describe an own or inherited "foo"? Can't know at this 
> point.
> - If the property is inherited, then even if it is non-configurable, 
> it may still be shadowed later by a configurable property. Say our 
> |obj| example object inherits from |proto1|, which inherits from 
> |proto2|. Assume "foo" was defined as a non-configurable property on 
> |proto2|. Then our |desc| object above denotes a non-configurable 
> property. Now, assume "foo" is later added to |proto1| as a 
> configurable property. Re-executing the call to getPropertyDescriptor 
> will then result in "desc" denoting a configurable property. That is 
> within the bounds of ES5.1 behavior.
> If |obj| would be a proxy with a FixedHandler, the FixedHandler should 
> not complain that "foo" was previously reported as non-configurable. 
> That is why inherited descriptors returned by getPropertyDescriptor 
> are not checked for invariants.
> - Now assume a different scenario in which "foo" is an own, 
> non-configurable property of |obj|. The only way for code to find out 
> that "foo" is indeed an own, non-configurable property is to call 
> Object.getOwnPropertyDescriptor. Doing so will cause the FixedHandler 
> to mark "foo" as a fixed own property. Now, if code calls 
> Object.getPropertyDescriptor(obj, "foo"), the getPropertyDescriptor 
> trap will find "foo" marked as an own property, so it _knows_ it can't 
> be inherited and it must check the validity of the returned result. If 
> getPropertyDescriptor were to return a configurable descriptor at this 
> stage, the FixedHandler will throw.
> Thus, the following interaction could occur if |obj| were a "faulty" 
> proxy whose getPropertyDescriptor trap reports "foo" as a configurable 
> property while at the same time its getOwnPropertyDescriptor trap 
> reports it as a non-configurable property (which is inconsistent):
> > var desc1 = Object.getPropertyDescriptor(obj, "foo");
> // desc1 = { ... , configurable: true }
> > var desc2 = Object.getOwnPropertyDescriptor(obj, "foo");
> // desc2 = { ..., configurable: false }
> // (now FixedHandler knows that "foo" was observed as an own 
> non-configurable property)
> > var desc3 = Object.getPropertyDescriptor(obj, "foo");
> TypeError: can't report "foo" as configurable as it was previously 
> exposed as a non-configurable own property
> I hope this clarifies why I think getPropertyDescriptor is no longer 
> problematic.
Ok. Thank for these explanations. Leaving the liberty to 
getPropertyDescriptor until something is proven for own properties 
sounds like a good behavior.

For the sake of completeness, i'd like to say that some "composed 
invariants" will break with that model. For instance:
var o = Proxy.create({...}, null);
var d = Object.getPropertyDescriptor(o, "foo");
// d.configurable === false
var d2 = Object.getOwnPropertyDescriptor(o, "foo");
// d2.configurable === true
Based on your proposal, this scenario is possible while it shouldn't 
because o doesn't inherit from anything (o.[[Prototype]] === null).
This could be fixed when the prototype is null, but this case is the 
tree that hides the forrest. The forrest being that the 
getPropertyDescriptor can return pretty much whatever it wants as long 
as the "own layer" of the proxy has nothing to do against it. This 
allows to lie about the prototype chain.
For instance, if the prototype chain is composed of non-extensible 
objects which do not have a "foo" property, returning a descriptor with 
configurable = false is breaking invariants (or are they just 
expectations?) regarding property configurability and inheritance.

I guess i should ask the question more directly:
Should (some) current inheritance-based expectations be turn into 
invariants? Are there good arguments to do so? Or are own layer 
invariants enough?
In a way, having a non-trappable (and thus reliable) 
Object.getPrototypeOf and reliable non-configurable properties and 
non-extensibility invariants could be enough, but it's worth asking 
(adding Mark in cc).


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20110906/97d1d96e/attachment.html>

More information about the es-discuss mailing list