The ES6 MOP (Was: New ES6 draft now available)

David Bruant bruant.d at gmail.com
Sat Nov 24 09:54:50 PST 2012


Le 24/11/2012 18:10, Allen Wirfs-Brock a écrit :
>> * [[Enumerate]], [[Keys]] and [[OwnPropertyKeys]] are very close 
>> operations
>> * So are [[PreventExtensions]]/[[Freeze]]/[[Seal]] on one side and 
>> [[IsExtensible]]/[[IsFrozen]]/[[IsSealed]]
>>
>> I'm afraid that making them distinct operations increases 
>> footgun-ness. [[HasProperty]] has been removed in favored of 
>> [[HasOwnProperty]] (which might be removed in favor of only keeping 
>> [[GetOwnProperty]], though the conclusion of the discussion was the 
>> keep both IIRC) because the former could be "robustly" composed between
>
> Yes, I agree.  In particular I think [[Enumerate]], [[Keys]], and 
> [[OwnPropertyKeys]] should be reduced to a single parameterized trap. 
> Multiple traps make it harder to create an internally consistent MOP 
> provider (eg, Proxy handler).
>
> (...)
>
>>
>> Here is an idea to uniformize the enumeration story while removing 
>> enumeration inconsistency footgun. I'll describe it in proxy trap 
>> terms. A unique trap (or internal operation)
>> keyEnumerate: () -> iterator for {propertyKey, enumerable}
>> There is this unique operation which returns an iterator not for 
>> property keys, but for an object containing a property key and a 
>> boolean signifying the enumerable boolean.
>> Using this, each userland operation would use this iterator, drain it 
>> out (unless the iteration is being aborted like by throwing in a 
>> for-in loop) and filter out based on the exact userland operation so 
>> that:
>> * All would filter out if propertyKey is ever a private symbol
>> * Object.getOwnPropertyNames would keep all other property keys 
>> regardless of the enumerable value
>> * Object.keys would filter out properties which are described as 
>> non-enumerable
>> * for-in would filter out non-enumerable and retrieve the different 
>> keyEnumerate from the prototype chain objects.
>> With this unique internal operation, an object is able to communicate 
>> its intent regarding what's enumerated and each enumeration userland 
>> operation only keeps what it's interested in.
>
> Yes, something like this.  My inclination would be to add a hint 
> parameter indicating one of the currently known variations. I think it 
> is justified as enabling the "handler" to optimize its internal work 
> of collecting the set of keys.
I'm afraid an hint parameter has the same downside than having several 
traps: offering the possibility to make different code paths for 
different userland enumeration operations. This very possibility is the 
footgun in my opinion.
I felt satisfied with the [[keyEnumerate]] internal operation, because 
as a trap, people will have to return an iterator that iterates over 
everything and the engine (not the trap) makes the call of what is to be 
actually enumerated by the different variations.
Not letting the trap know how the result will be used is the best way to 
prevent people from writing erroneous traps in my opinion.


>>
>>> • Defined all of the functions in the @Reflect module corresponding 
>>> to MOP internal operations.
>> IIRC __proto__ has been agreed to be an data property and there was 
>> no Reflect.setPrototypeOf operation in the harmony Reflect module 
>> proposal on purpose so that 'delete Object.prototype.__proto__' at 
>> any point would be enough to be sure that all [[Prototype]]-setting 
>> capabilities are removed.
>> So I think the Reflect.setPrototypeOf  should be removed.
>
> Do you want to be able to set __proto__ on DOM objects and other 
> exotic objects?
I personally don't, but for sure others do. I'm happy with the static 
inheritance mechanism we will soon have and cover the vast majority of 
current __proto__ use cases.
Adding Reflect.setPrototypeOf has exactly the same issues than an 
extractable __proto__ setter. See May meeting notes [1]:
"DH: I can predict the security bugs: the implementor just thinks about 
the normal case, but the attacker takes the accessor out, installs it on 
an object that inherits from a proxy to an object from another global 
etc. etc. and something internal breaks
MM: that's the most compelling argument I've heard. the additional 
testing surface area is much bigger"

__proto__ really is in ES6 as a de facto standard. I'm not sure it is a 
good enough reason to make setting the [[Prototype]] a first citizen of 
the language.


> If so, it needs to be part of the MOP.  "In for a penny, in for a 
> pound". If we are going to make __proto__ part of the language than we 
> have to accommodate just like any other feature.  If you want to be 
> able to disable it make deletion of Reflect.setPrototypeOf be the 
> switch.  Or define a new function for doing it.  But I don't think we 
> should be bending the MOP to accommodate idioms like deleting 
> __proto__..  Remember, the first point above.  [[SetInheritance]] is a 
> generic operation, only some objects implement via a [[Prototype]] 
> internal data property.
In practice, it's all of them except in platforms with no __proto__, no? 
Even proxies have no way to override [[SetInheritance]].
Actually, if Reflect.setPrototypeOf is added, a setPrototypeOf handler 
trap needs to be added as well. Some previous discussions about 
__proto__ and proxies occurred at the July meeting [2].

David

[1] https://mail.mozilla.org/pipermail/es-discuss/2012-May/022834.html
[2] 
http://wiki.ecmascript.org/doku.php?id=harmony:direct_proxies#discussed_during_tc39_july_2012_meeting_microsoft_redmond
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20121124/1cc0b447/attachment.html>


More information about the es-discuss mailing list