Do we really need the [[HasOwnProperty]] internal method and hasOwn trap

Allen Wirfs-Brock allen at wirfs-brock.com
Tue Nov 13 10:28:49 PST 2012


On Nov 13, 2012, at 1:44 AM, David Bruant wrote:

> Le 12/11/2012 19:17, Allen Wirfs-Brock a écrit :
>> On Nov 12, 2012, at 2:21 AM, Brandon Benvie wrote:
>>> Shouldn't it be the reverse, based on the removal of getPropertyDescriptor/Names? The proxy controls what i's [[Prototype]] is which indirectly directs how non-own lookups proceed. The functionality of `has` can (and usually should) be derived from proto-walking hasOwn until true or null.
>> yes, that would be a good fix for this particular problem.  [[HasOwnProperty]] could remain as an internal method and HasProperty could be defined as an abstraction operation that uses the [[Prototype]] internal accessor and [[HasOwnProperty]].
> Indeed. Otherwise, what happens with Object.prototype.hasOwnProperty.call(new Proxy({}, handler), 'a') would be unclear.

O.p.hasOwnProperty can be respecified in terms of [[GetOwnProperty]]

> 
>> Another potential consistency issue is between [[HasOwnProperty]] and [[GetOwnProperty]].  That could be eliminated by combining them into one operation:
>> 
>> [[GetOwnPropety]](name, descriptorNeeded) -> descriptor | boolean | undefined
>> 
>> If descriptorNeeded is true it acts as the current [[GetOwnProperty]]. If descriptorNeeded is false it acts as the current [[HasOwnProperty]].
>> 
>> At the handler-level it makes it impossible to override "hasOwn"without also overriding "getOwnPropertyDescriptor"
> The boolean isn't needed. It's possible to get rid of the [[hasOwnProperty]] internal method and only keep [[GetOwnPropety]](name).
> * "Object.prototype.hasOwnProperty.call(o, name)" would be defined as ToBoolean(o.[[GetOwnProperty]](name))
> * "name in o" would be the same thing with proto-climbing
> Both hasOwnProperty.call and the in operator would call only the getOwnPropertyDescriptor trap, the rest would be left to implementors.

There is only so much that an implementation can do.  Each occurrence of an internal method call in the specification must dynamically dispatch through an object  is there is any possibility that the object isn't an ordinary object.  In general, internal methods calls (or at least the test for an ordinary object) can't be optimized away.
> 
> It may be weird to get rid of both hasOwn and has traps and that hasOwnProperty.call and the in operator call the getOwnPropertyDescriptor trap, but it's the cost of wanting consistency between [[GetOwnProperty]] and [[HasOwnProperty]]. I'm personally fine with this; I have no preference for one side or the other.
> 


I think there is agreement that [[HasOwnProperty]] is just an optimization of ToBoolean([[GetOwnPropertuy]]).  Its only purpose is to avoid unnecessary reification of property descriptors. If that optimization isn't important we should just eliminate [[HasOwnProperty]].

> From the spec point of view, it's possible to remove [[HasOwnProperty]] as an internal method, but to define a helper function. The idea behind doing this (apparently cosmetic) change could be to reach 1-to-1 correspondence between object internals and proxy traps.

and between other providers of exotic object implementations such as "native" DOM implementors.

Having a single MOP seems like an important goal to me. 

> 
> 
> I'd like to point out that long discussions have already occurred about invariants and trap results consistency. By essence of allowing to provide functions for handler traps, it's possible to allow arbitrary inconsistencies. For instance:
> 
>    // "temporal inconsistency"
>    var a = new WeirdObject();
>    console.log('yo' in a); // true
>    console.log('yo' in a); // false
>    console.log('yo' in a); // false
>    console.log('yo' in a); // true
> 
>    // "get/set" inconsistency
>    var b = new WeirdObject();
>    b.yo = 26;
>    console.log(b.yo); // logs "what's up es-discuss!!"
> 
> These are other inconsistencies with the expectations we currently have from objects. I can't help wondering why this inconsistency (or any other inconsistency) would be considered more or less important than the one discussed in this thread.
> IIRC, previous discussions led to the conclusion that it's impossible to get rid of all inconsistencies. The minimum consistency required was defined for security purposes and led to the invariant enforcement checks.
> I'm fine if we're discussing removing some other inconsistencies, but I'd be more interested in seeing a principled way to decide which inconsistency we're getting rid of and which we still allow. In other word, what makes an given inconsistency a bad thing?
> The previous answer we had was in essence "if it prevents defensive programming" (Tom or Mark will correct me if I'm misinterpreting or miswording it) and with the current proxy design, it's possible to offer freedom to proxies as long as it's within the boundaries of respecting non-configurable properties and non-extensible invariants.
> 
> What would be the new answer to my question?

footgun-ness

I totally agree with you.  The possibility of inconsistency is inherent in this style of metaprogramming.  There is no way we can practically prevent somebody from coding a proxy  handler that intentionally produces inconsistent results.  The lowest layers of the specification/implementations must robust in the presence of such inconsistencies. 

But we should also try to avoid making it too easy for developers to unintentionally create such inconsistencies. Proxies have a API model that (intentionally) permits handlers that only  implement of a subset of the traps.   This is good, up to a point but if the traps don't have orthogonal functionality it becomes easy to unintentionally introduce inconsistencies simply by leaving out  a handler method.  We can engineer the API to reduce the occurrence of unintended inconsistency.   One thing we can do is minimize redundant traps that are expected to produce consistent results.  We may also fine it use for to merge traps in ways that centralize such semantics in one place.  We can eliminate traps (eg [[Iterate]] by promoting the functionality out of the meta layer.  Overall, the surface area of the MOP should probably be as small as possible.

We've explored the security issues or proxies in depth.  Now is the time to explore the usability issues.  Proxies are not intended for novices but there still will be thousands of developers trying to use proxies who are neither metaprogramming experts or  have a deep understand of the ES MOP.  We should try to minimize the number of footguns and other hazards that they have to face. 

Allen




More information about the es-discuss mailing list