possible excessive proxy invariants for Object.keys/etc??

Allen Wirfs-Brock allen at wirfs-brock.com
Wed Nov 21 14:17:55 PST 2012

On Nov 21, 2012, at 12:27 PM, Tom Van Cutsem wrote:

> 2012/11/21 Allen Wirfs-Brock <allen at wirfs-brock.com>
> I'd be more favorably inclined towards freezing than I am towards copying.  But, as you know,  ES5 does not currently produce frozen objects in these situations. I feel uncomfortable about enforcing a frozen invariant for traps where that invariant is not provided by the corresponding ordinary object behavior.  Perhaps I could get over that or perhaps incompatibility applying that requirement to ordinary objects wouldn't break anything.
> While the arrays produced by Object.keys etc. aren't frozen, there's an implicit guarantee that those arrays will not further be modified implicitly by any other operation in the program. Of course a program can choose to explicitly mutate it. But there's no magical action-at-a-distance.
> Currently, we provide the same guarantee in the face of proxies by "defensively copying" the trap result into a fresh array. Requiring the trap result to be frozen feels OK to me, except that it doesn't play well with the current default of automatically forwarding:
> var p = Proxy(target, { })
> Object.keys(p) // error: "keys" trap did not return a frozen array
> var p = Proxy(target, { keys: function(t) { return Object.freeze(Object.keys(t)); })
> Object.keys(p) // now it works

Wait, Object.keys as now just implemented by calling the [[Keys]] internal method and it doesn't have any idea about what kind of object that internal method is being called upon.
So it must apply the same rules to both the result of ordinary object [[Keys]] and proxy [[Keys]].  Object.keys could always copy the result or always freeze its result or accept either frozen/non-frozen but all [[Keys]] invocation need to be treated equally. 

> One way to get rid of this irregularity is indeed to make Object.keys always return frozen arrays. I don't think it would hurt performance, as implementations already aren't allowed to reuse arrays returned from Object.keys (of course they can always cheat as long as they don't get caught).

But client of Object.keys are currently permitted to modify its result array.  Freezing it would break such clients.
> Regardless, freezing and testing for frozen is, itself, not a cheap operation.  It requires iterating over all the property descriptors of an object.  If we are going to build in a lot of checks of for frozen objects perhaps we should just make frozen (and possibly) sealed object level states rather than a dynamic check of all properties.  Essentially we could internally turn the [[Extensible]] internal property into a four state value:  open,non-extensible,sealed,frozen.  It would make both freezing and checking for frozen much cheaper.
> As Andreas mentioned, for normal objects, isFrozen checks can be optimized to O(1).
> For proxies, if we leave in the derived isFrozen trap, the check could also be O(1).

Are you sure about that last point?  What if the target is a Proxy or some other exotic object that is observing internal method calls on itself.  This is one of my overall concerns about some of the more complex invariant checking algorithms.  Our interoperability rules require strict ordering of all observable operations and internal method/trap calls are potentially observable. 


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

More information about the es-discuss mailing list