B.3.1 The __proto__ pseudo property

Allen Wirfs-Brock allen at wirfs-brock.com
Tue Apr 23 06:30:58 PDT 2013


Mark, below what you refer to as [[SetProtoype]] is essentially the [[SetInhertiance]] MOP operations in the current spec. draft there is also a [[GetInheritance]].  It is called Get/SetInheritance because it doesn't necessarily manipulate the [[Prototype]] of the object it is invoked upon (eg, if it is a Proxy) and for exotic objects property inheritance isn't constrained to use [[Prototype]].

On Apr 23, 2013, at 5:11 AM, Mark S. Miller wrote:

> Ok, I have read more messages on this thread and looked at some of the supporting material that has been pointed at. The notes from the last meeting record a conversation before I arrived, and I'm not quite clear what it says was agreed on. In any case, I think the primary goals should be and seem to have been
> 
> * minimize magic
> * maximize security
> * codify something everyone can agree to implement
> 
> The first two goals generally align well anyway. I think this is best served by something that seems at least close to what was agreed on:
> 
> 
> * The syntax that we've already agreed to on this thread: {__proto__: ....} is special syntax that initialized the [[Prototype]]. No need for anything even as mildly imperative as [[SetPrototype]]. 

The semantics of the syntax still should be specified in terms of the MOP as it's in the ordinary object MOP internal methods that we specify their semantics 

> 
> * { [ "__proto__" ]: .... } is not special in any way, and creates a normal property named "__proto__".

I don't believe this is legal. Didn't we agree w to support [ ] property keys that evaluate to symbols.

> 
> * Every object with a potentially mutable [[Prototype]] must be identified with a realm of origin. (Practically this will be "any object", which is good because that is what Weak References will need anyway.)

> 
> * In the initial state of a normal realm, Object.prototype.__proto__ is an accessor property with the descriptor (making up names for the internal functions -- don't take the names seriously):
> 
> { getter: [[ProtoGetter]], setter: [[ProtoSetter]], enumerable: false, configurable: true }
> 
> * In this initial state, Object.getOwnPropertyDescriptor(Object.prototype, '__proto__') returns the above descriptor. No magic.
> 
> * In this initial state, Object.getOwnPropertyNames(Object.prototype) returns a list which includes the string "__proto__". No magic.
> 
> * Likewise for all other reflective operations, including "in". No magic.

So, getOwnPropertyKeys(Object.prototype) is expected to yield "__proto__"
> 
> 
> * The behavior of [[ProtoGetter]] is approximately 
> 
>     function [[ProtoGetter]] () { return Object.getPrototypeOf(this); }
  
it would actually be specified in terns of [[GetInheritance]] MOP operations

> 
> except of course that it uses the internal function rather than the current binding of Object.getPrototypeOf. Just like Object.getPrototypeOf, this behavior is independent of Realm. It is also independent of whether [[ProtoGetter]] is invoked *as* an accessor or invoked otherwise, for example by using Function.prototype.call.
> 
> 
> * The behavior of [[ProtoSetter]] is approximately
> 
>     function [[ProtoSetter]] (newValue) {
>         if ([[GetRealm]](this) !== [[GetRealm]]([[ProtoSetter]])) {
>             throw new TypeError(....); // or should this be RangeError ?
>         }
>         this.[[SetPrototype]](newValue);
>     }

In the past there were other restrictions that have been suggested.  For example, not allowing:
   Object.prototype.__proto__ = notNull;
to do what the above a=names suggest.

Regardless, what is so special about the [[ProtoSetter]] operation that it needs to be restricted in this way?  It's just a capability and you know how to control access to capabilities.  You also know how to protect objects from having their [[Prototype]] mutated.  If I have any object, that inherits from a different realm's Object.prototype I can navigate to its constructor property which gives me access to that other realm's, Object.create, Object[[@@create], and all the other Object.* functions.  Why isn't being able to find and  apply some other realms Object.free just as scary as finding its [[ProtoSetter]]?

Allen





> 
> This behavior is independent of whether [[ProtoSetter]] is invoked *as* an accessor or invoked otherwise, for example by using Function.prototype.call.
> 
> 
> * Normal objects have a [[SetPrototype]] method like
> 
>     function [[SetPrototype]] (newValue) {
>         // normal checks for proto acceptability
>         // * either null or an object
>         // * would not create an inheritance cycle
>         this.[[Prototype]] = newValue;
>     }
> 
> 
> 
> 
> ======== Warning: The rest of this is half baked ============
> 
> * Direct proxies have a [[SetPrototype]] method that invokes the handler's "setPrototype" trap. It is the *handler's* responsibility, not the proxy's, to set the target's [[Prototype]] to newValue. Once the handler returns to the proxy, the proxy checks if target.[[Prototype]] === newValue. If not, it throws. This enforces that a handler can only reflect the mutation of [[Prototype]] transparently if it already has setter which is the capability to do so.
> 



More information about the es-discuss mailing list