On Nov 24, 2012, at 9:54 AM, David Bruant wrote:

> 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.

I think it is a much smaller footgun than having 3 independent traps that don't 
have to be provided as a unit.  If you have a single well specified trap, I 
expect an trap implementor to know or read the trap specification and see that 
there is a parameter that must be dealt with in a valid implementation.  In 
practice, they are likely to handle all the variations in a single loop rather 
than having three separate loops and possibly deviant loop implementations in 
three separate traps. 

Also, implementations are going to want to optimize and always produce the 
complete inherited set is going to be observable (via proxy traps as the proto 
chain is climbed).  Also note that the behavior of these traps for ordinary 
objects will be fully defined so that means that just getting "own property 
names" necessarily means that all proto and probably inherited properties must 
be observably visited and this even applies to code doing Proxy invariant 
checking that might need to access the "own property names" list.

I think a single operation with a filter "hint" is a good trap off between 
trying to minimize footgunness and allowing implementations (including via 
Proxy traps) to apply reasonable optimizations.

> 
> 
>>> 
>>>> • 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.

and it current happens....

> 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.

"in for a penny..."  If it isn't part of the MOP then implementors will just 
by-pass the the MOP for situations like the DOM and do it anyway. In that case, 
who knows if they will respect these conventions.   If we are standardizing the 
feature we might as well understand it.  If we want to make it a disable-able  
feature (per realm?? per something else??) we can probably do that.  But just 
leaving it as an idiom of manipulating Object.prototype (what if O.p is frozen 
before you the a chance to delete it?) seems like we are still trying to 
pretend that dynamic prototype mutation isn't a real feature 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?

The specifications I wrote for Symbol and Proxy objects doen't have any 
[[Prototype]] state. Some MOP handler providers (not the current Proxy 
handlers) might have other kinds of inheritance state.  Also, note that even 
with the current Proxy semantics, [[GetP]]/[[SetP]] are required to use the 
target's [[Prototype]] (if it has one ;-) for inheritance lookup.  such proxies 
can have completely different internal state that they use for their 
inheritance model.

> 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].

Absolutely, If you need to trap on GetPrototypeOf (even just for notification) 
you will also need one for SetPrototypeOf (eg, assigning to __proto__).  Same 
logic applies to both.  Proxy's may impose restrictions on both of them but you 
still will want the notification.  Non-proxy implementations of the MOP.

I see that [2] call for filtering __proto__ access in Get/Put handlers.   I 
think that dealing with it in a Get/SetInheritance handler would be a much more 
efficient way to hand this extremely rare operation.  Rather than filtering for 
it on every property access, an handler that cares only needs to worry about 
the actual Get/SetInhertiance.


> 
> 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

_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to