W3C home > Mailing lists > Public > public-script-coord@w3.org > October to December 2014

Re: Determining what prototype should be used for an object

From: Boris Zbarsky <bzbarsky@mit.edu>
Date: Fri, 14 Nov 2014 13:04:10 -0500
Message-ID: <5466441A.2020802@mit.edu>
To: Allen Wirfs-Brock <allen@wirfs-brock.com>
CC: "public-script-coord@w3.org" <public-script-coord@w3.org>
On 11/14/14, 11:46 AM, Allen Wirfs-Brock wrote:
> (it isn't totally clear to me what you mean by the "current Realm".

I mean the ES6 meaning.

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

This covers cases when objects are created with constructors.  However, 
lots of objects in the ES spec are created with direct 
ObjectCreate/ArrayCreate bits that explicitly pass in the proto to use, 
and often use one of the standard built-in prototypes for the current Realm.

Web IDL at the moment (somewhat implicitly) has a similar creation 
mechanism, whereby when an object is initially created you need to pass 
in the appropriate standard prototype from some Realm.  The question is 
what Realm.

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

Sure.  Consider the HTMLCanvasElement.prototype.getContext API. This is 
explicitly documented to have behavior that's described by something 
like the following pseudocode, with "arg" being the single argument to 
getContext:

   if (!this.[[CurrentContextType]]) {
     this.[[CurrentContextType]] = arg;
     this.[[CachedContext]] = InternalCreateContext(arg);
   } else if (this.[[CurrentContextType]] != arg) {
     // return null or throw, depending on this.[[CurrentContextType]]
   }
   return this.[[CachedContext]];

The point being that the first call with a given argument caches the 
value to be returned and this value is returned every time after that.

Now it seems to me to be a bit weird for the prototype of the return 
value to depend on who has previously called this method and whether 
they called it directly or via .call/.apply.  So it makes some sense to 
define the return value's initial prototype in a way that's independent 
of the Realm of the getContext function, and instead in some way tied to 
the HTMLCanvasElement object itself.

The other quirk here is that APIs like this are fairly common in 
browsers in practice, since lots of objects are lazily created in 
implementation terms, even if in spec terms many of them are expected to 
exist for the lifetime of the global (unlike this case, where the spec 
requires lazy creation).  So browsers have to have ways of lazily 
creating objects while making it look like they existed since the Realm 
was created (and hence have some canonical proto from that Realm at the 
point when you _actually_ create them).

I suspect the combination of these two things is what leads to the 
current browser behavior, fwiw.

Anyway, back to the "lazily create and cache" issue, I'm not aware, at 
first glance of ES6 APIs that do that, but I didn't look very carefully.

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

This is tautological, in some sense.  The ES semantics for doing an 
ObjectCreate require passing in a prototype; the question is what prototype.

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

Yes, fairly consistently across browsers.  See Anne's testcases.

> If you follow the ES6 rules (and your spec is sufficiently detailed)

It's not.  I'm trying to make it sufficiently detailed, but I want to 
make sure we all agree on the desired end result before I formalize 
something everyone refuses to implement.

> For example, everything
> created by createImageData would be in the same realm as createImageDate

This matches exactly 0 implementations as far as I can tell.  That is 
the crux of the matter.

> unless CreateImageDate internally called a factory in another realm

That would basically be a spec device for getting the currently observed 
behavior, no?  The question is what we should actually do here.

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

Not really.  The only objects that are visible cross-origin are Window 
and Location instances, and both exist primordially in spec terms so 
have pinned-down proto chains and whatnot.  Since you can't observably 
create instances of these objects, there's no issue here.

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

Sure.  The above text about getContext and lazy creation in general 
should indicate that there are equally strong technical reasons for the 
DOM behavior in some cases.

>   Using the realm of the caller to the factory

No one is proposing that.  The only realms under consideration are the 
realm of the factory and, in a world where some objects have explicit 
realms associated with them, which afaict Web IDL objects in browsers 
do, the realm of the "this" object.

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

Sure.  I'm aware of this.

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

Because that doesn't pin down the behavior, so far, because object 
creation is not described in sufficient detail.  A lot of objects 
involved are not even constructible, so are created ... somehow.  And 
I'd like to specify the how.

-Boris
Received on Friday, 14 November 2014 18:04:39 UTC

This archive was generated by hypermail 2.4.0 : Friday, 17 January 2020 17:14:23 UTC