Re: Determining what prototype should be used for an object

On Nov 13, 2014, at 6:46 PM, Boris Zbarsky wrote:

> Background: whenever an object of some sort is created, we have to select which standard prototype should be used for it.  The exact prototype depends on the kind of object and on which global environment we want to get the standard prototype from.  From now on in this mail when I refer to the global of an object, or an object being from some global, I mean this global that the object's initial prototype came from.
> 
> The question is: what global environment to use?
> 
> In ES, the answer typically seems to be "the global of the current Realm".

(it isn't totally clear to me what you mean by the "current Realm".  In ES6 the "current Realm" is the realm of the currently executing function and potentially changes every time a function call/return occurs.  When executing the body of a factory function or constructor, the current realm is the realm of the factory/constructor). 

Not exactly.  In ES6, every function object is associated with a Realm. When an object is created by applying the new operator to a constructor function the new object instance is associated with the same realm (ie, global object) as the constructor. When an object is directly created by some sort of factory function the new object instance is associated with the realm of the factory function. In neither case does the Realm of the code that applied the the new operator or called the factory have anything to do with selecting the Realm of the new instance. 

(the above is actually conceptually true, but is glossing over how constructors actually work.  Most object aren't directly associated with a realm but the built-in prototype objects are.  Constructor function simply set the [[Prototuype]] of the new instance to the current value of the constructors `prototype `property`. Unless the `prototype` property is configurable, this will be the prototype with the same realm association as the constructor function)

(also note that a few special case exceptions to this rule exist in ES6 specifically to support web legacy compat. for example see http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype.concat step 4.c)

> But in ES it's also not common for built-in functions to create objects and keep returning them, for what it's worth.

I'm not sure what you mean here and why it is relevant. Could you elaborate.

> 
> What should happen in IDL?  Anne put together some tests at https://github.com/w3c/web-platform-tests/pull/1381 that show that for the most part in browsers today IDL-defined methods that create objects will create the object using the global environment that the "this" value for the method is associated with.  This is the global environment it initially got its prototype from, except in some cases for nodes when adoptNode is involved.

What should happen?  How about exactly following ES semantics, except in specific situations where there is a clear reason why something else needs to be done.

I don't follow what you are saying about the this value.  Are you saying that,

   someDOMFactory.apply(thisObj, args)

will set create a new object whose [[Prototype]] is obtained from thisObj's realm rather than someDOMFactory's realm.

> 
> There are some bizarre exceptions to this. For example, in Chrome doing global1.CanvasRenderingContext2D.prototype.createImageData.call(canvasRenderingContextFromGlobal2) will return an ImageData object which is instanceof global1.Object, but whose .data is instanceof global2.Object.  Similarly, Firefox has the same issue with getImageData (but not createImageData; I consider that a definite bug since getImageData/createImageData should at least match each other).  But mostly browsers are remarkably consistent here.

If you follow the ES6 rules (and your spec is sufficiently detailed) it should always be clear what every object is.  For example, everything created by createImageData would be in the same realm as createImageDate unless CreateImageDate internally called a factory in another realm or called back (via canvasRenderingContextFromGlobal2) to a global2 factory.
> 
> The choice of initial prototype mostly affects two things: instanceof checks and whether properties added to a given standard prototype show up on a given object.  

and presumably cross-origin frame proxying...
> 
> Anyway, given all that, where do we want to go?  Do we want to just accept that ES and IDL will differ on this matter?  Do we want to align the two specs with each other, at least for methods that always return a new object?  For those, creating the new object in the current Realm's global is probably workable.  Note that I do think that having createImageData return an ImageData that's not from the same global as its .data to be buggy, but that can be addressed in two ways, obviously.

There is a strong technical reason why ES6 use the the same realm as the factory.  This is essentially static scoping where the global environment of the function is captured when the function is created.  Using the realm of the caller to the factory would essentially be applying dynamic scoping rules to the determination of the global environment of the factory. Dynamic scoping is generally considered to be very undesirable.

Also note, that using any other than the ES rules (whatever they might be) makes it harder to implement these interfaces using ES,

To repeat what I said above.  Except for specific cases that have a well articulated reason for being differ, why shouldn't WebIDL simply say that ES6 semantics are strictly followed. 

> Note, also, that all of this mostly matters when people .call()/.apply() methods from one global on objects from another global, since otherwise the current Realm and the global of the this value match anyway.
> 
> Thoughts?
> 
> -Boris
> 

Received on Friday, 14 November 2014 16:47:26 UTC