Le 06/09/2011 16:12, Tom Van Cutsem a écrit :
2011/9/3 David Bruant <[email protected]
<mailto:[email protected]>>
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).
Cheers,
David
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss