Property Descriptors as script compatible representation (was: Property descriptors as ES6 Maps)
bruant.d at gmail.com
Thu Nov 1 06:03:53 PDT 2012
Le 31/10/2012 19:03, Allen Wirfs-Brock a écrit :
> Let me summarize what I think is your concern.
> In ES5, property descriptor records are a specification device that is used to transport information about object properties between factored components of the ES specification. The same information can be expressed as an ES object that is produced/consumed by Object.getOwnPropertyDescriptor and Object.defineProperty. These are raising/lowering operations that move information from the specification/implication level to the reflective ES language level. One advantage of this layering is that the conversion to/from an actual object takes place once, at a well defined point in the execution sequence and any side-effects of object access occur only at that point. Once the information is represented as an internal "record" we know that all internal consistency preconditions are satisfied and that no side-effects can be associated with access to such records.
The part about precondition is particularly interesting and I agree it
should be kept. However, it's possible to define ECMAScript contructs
that enforce such pre-conditions. My point is that even for the spec,
it's possible to define PropertyDescriptors by using ECMAScript
constructs, without necessarily creating a new spec-only type.
> You concern seems to be that a proxy traps that deal with descriptor objects have no such guarantees and in particular strange things might happen if a descriptor object is itself a proxy.
My original concern was that the defineProperty and
getOwnPropertyDescriptor traps I/O were interacting directly with
PropertyDescriptor algorithms, but after re-reading the proxy spec page
more carefully, I realized it is not the case. However, not being the
case has a cost:
The proxies_spec page  redefines some ES5 15.2.3.* built-ins.
Core of the current Object.getOwnPropertyDescriptor is :
3) Let desc be the result of calling the [[GetOwnProperty]] internal
method of O with argument name.
4) Return the result of calling FromPropertyDescriptor(desc) (8.10.4).
Core of the proposed Object.getOwnPropertyDescriptor is :
3) If O is a proxy
Return the result of calling TrapGetOwnProperty(O, name)
4) Let desc be the result of calling the [[GetOwnProperty]] internal
method of O with argument name.
5) Return the result of calling FromPropertyDescriptor(desc) (8.10.4).
If [[GetOwnProperty]] returned the same type of thing than
TrapGetOwnProperty (which returns an object), the spec 126.96.36.199
Object.getOwnPropertyDescriptor could remain the same. Also, I have the
feeling that there is duplicated logic between TrapGetOwnProperty and
FromPropertyDescriptor or unnecessary back and forth conversions between
internal PropertyDescriptor and objects.
Currently, TrapGetOwnProperty returns an object, but when there is an
actual trap, it goes through:
* NormalizeAndCompletePropertyDescriptor(Object) -> Object (step 6)
* ToCompletePropertyDescriptor(Object) -> PropertyDescriptor (step 3 of
* FromPropertyDescriptor(PropertyDescriptor) -> Object (step 4 of
We might as well get rid of the spec-only PropertyDescriptor, define an
equivalently pre-condition/invariant enforcing ES5 construct and
manipulate that both internally and in trap boundaries.
> The getOwnPropertyDescriptor trap is a lowering operation. (...)
> The defineProperty trap is a raising operation. (...)
> The other possible concern would be that a trap might directly use Object.getOwnPropertyDescriptor on a proxy object and that this might provide a bogus descriptor. But it can't. (...)
I agree with what you wrote here. Basically, the thing that prevent
potentially harmful descriptors-as-proxies to being harmful is that from
getOwnPropertyDescriptor and to defineProperty traps, the "essence" of
the descriptor is copied to a fresh object created internally.
> so, I just don't see any basis for your concern. Perhaps, you could elaborate on the nature of the problem as you perceive it.
Let's try to ASCII-art it. Here are the paths that property descriptors
take. Arrows represent a internal type conversion and/or descriptor
# Object.defineProperty on regular object:
ES Object (in user script) --> PropertyDescriptor (and stored as such)
# Object.defineProperty on proxies (as I understand its current
ES Object --> PropertyDescriptor --> ES Object (for trap argument) -->
The last arrow is not compulsory, but we can decently assume that for
most cases, the defineProperty trap will forward the operation to the target
# What I'm suggesting is to do the following in both cases:
ES Object --> ESUsablePropDesc (used for proxy trap arguments)
I think I was wrong in suggesting using raw ES6 Maps (and I think I
understand that's what Andreas meant regarding heterogeneous types). The
idea of ESUsablePropDesc is that it preserves pre-conditions and
invariants of property descriptors very much like the internal type
currently does. It can be used both internally in the spec as well as in
When forwarding the ESUsablePropDesc object to the target, there is no
need for conversion once again. The engine generated it and
enforced/normalized anything it needed already.
Potentially, when using ESUsablePropDesc descriptors directly (which
could become an option), the engine may not need to convert them,
because it already has enforced invariants/normalization, prevented
inconsistent states, etc.
The way I see it now, ESUsablePropDesc would be a regular object with a
bunch of getter/setters to enforce property descriptor invariants.
Everything would remain compatible (unless people really cared that ES5
descriptors have data properties). The proxy side of things would be
more efficient, the door would be open to make non-proxy use of
Object.defineProperty/getOwnPropertyDescriptor more efficient.
More information about the es-discuss