Hi Allen,
2012/11/18 Allen Wirfs-Brock <[email protected]>
The proxy spec.for Object.getOwnPropertyNames/kets/etc. seem to be doing quite
a bit more than this. They
1) always copy the array returned from the trap? Why is this necessary? Sure
the author of a trap should probably always return a fresh object but not doing
so doesn't violate the integrity of the frozen/sealed invariants? In most
cases they will provide a fresh object and copying adds unnecessary work
that is proportional to the number of names to every such call.
The copying is to ensure:
a) that the result is an Array
b) that all the elements of the result are Strings
c) to ensure the stability of the result.
You can think of a + b as implementing a type coercion of the trap result to
"Array of String". This coercion is not too dissimilar from what the
getOwnPropertyDescriptor has to do (normalization of the returned property
descriptor by creating a fresh copy).
c) on the other hand is crucial for the non-configurability/non-extensibility
checks mentioned below. It's no use checking some invariants on a data
structure if that data structure can later still be mutated.
If we don't care about any of a, b and c, then the result array wouldn't need
to be copied.
2) ensuring that the list of property keys contains no duplicates. Why is this
essential? Again, I don't see what it has to do with the integrity of the
frozen/sealed invariants. It is extra and probably unnecessary work that is at
least proportional to the number of names).
We've been going back and forth over whether or not we wanted to prevent
duplicates. I remember Andreas Gal being concerned about these kinds of issues
(that was when he was doing the first Firefox prototype for old proxies, in the
context of the enumerate() trap, which was called during a live for-in loop.
IIRC, Firefox already did de-dupe checks, as properties already enumerated in a
child object should not be re-visited when visiting a parent object)
More recently, at the last TC39 meeting in Boston, we decided to change the
return type of the enumerate() trap from Array[String] to Iterator, and in
doing so waiving the duplicate properties check. Quoting from the "Open issues"
section of
<http://wiki.ecmascript.org/doku.php?id=harmony:direct_proxies#open_issues>:
"Enumerate trap signature: consider making the enumerate() trap return an
iterator rather than an array of strings. To retain the benefits of an iterator
(no need to store collection in memory), we might need to waive the duplicate
properties check. Resolution: accepted (duplicate properties check is waived in
favor of iterator return type)"
I guess if duplicate properties are not crucial for enumeration, they're also
not crucial for Object.getOwnPropertyNames and Object.keys and can be dropped.
Mark, can you comment?
3) Every name in the list returned by the trap code is looked up on the target
to determine whether or not it exists, even if the target is extensible. Each
of those lookup is observable (the target might itself be a proxy) so,
according to the algorithm they all must be performed.
This is where we get into actual checks required to enforce
non-configurability/non-extensibility.
Granted, the ES5 spec is not clear about the invariants on getOwnPropertyNames
and keys. The currently specified invariants are a common-sense extrapolation
of the existing invariants to cover these operations.
In practice, it determines the degree of confidence that a programmer can have
in Object.getOwnPropertyNames and friends when dealing with a frozen object. If
we waive these invariant checks, then the result of those operations can never
be trusted on to reliably introspect on a frozen object's list of property
names:
Object.isFrozen(proxy) // true
Object.getOwnPropertyNames(proxy) // ['foo']
Object.getOwnPropertyNames(proxy) // [ ]
Here, the 'foo' property apparently disappeared on a frozen object.
If neither the for-in loop nor Object.getOwnPropertyNames nor Object.keys can
reliably report an object's own properties, then we've made it impossible to
reliably traverse and inspect a presumably deep-frozen object graph.
4) Every own property of the target, is observably looked up (possibly a second
time) even if the object is extensible and has no non-configurable properties.
We may be able to remove the redundancy of two lookups by restructuring the
algorithm.
There previously was some redundancy in other checks as well.
It isn't clear to me if any of this work is really necessary to ensure
integrity. After all, what can you do with any of these names other than use
them as the property key argument to some other trap/internal method such as
[[SetP]], [[DefineOwnProperty]], etc. Called on a proxy, those fundamental
operations are going to enforce the integrity invariants of the actual
properties involved so the get name checks doesn't really seem to be adding
anything essential.
Perhaps we can just get rid of all the above checking. It seems like a good
idea to me.
Alternatively, it suggests that a [[GetNonConfigurablePropertyNames]] internal
method/trap would be a useful call to have as the integrity invariants only
care about non-configurable properties. That would significantly limit the work
in the case where there are none and limit the observable trap calls to only
the non-configurable properties.
That would be one way to speed things up.
I share your concern that the current spec algorithm may be a bit too
restrictive for implementations to allow optimizations. It's very imperative.
We should ask the spec implementors to have a look at the draft spec and share
their concerns.
Cheers,
Tom
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss