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

RE: Thoughts on ES5 binding modifications (was: RE: Java bindings generated from Web IDL)

From: Travis Leithead <travil@microsoft.com>
Date: Wed, 3 Nov 2010 20:00:35 +0000
To: "Olli@pettay.fi" <Olli@pettay.fi>
CC: Cameron McCormack <cam@mcc.id.au>, "Mark S. Miller" <erights@google.com>, Shiki Okasaka <shiki@google.com>, "public-script-coord@w3.org" <public-script-coord@w3.org>, Adrian Bateman <adrianba@microsoft.com>
Message-ID: <9768D477C67135458BF978A45BCF9B3821DAD8@TK5EX14MBXW604.wingroup.windeploy.ntdev.microsoft.com>


From: Olli.Pettay@helsinki.fi] Sent: Sunday, October 31, 2010 2:59 PM
> On 10/21/2010 11:50 PM, Travis Leithead wrote:
> > OK, looking forward to a discussion around this. In IE9, we are mixing 
> > in interfaces (and inherited interfaces of mixin interfaces) onto the 
> > "implements" target's prototype.
> >
> > For example:
> >
> > [NoInterfaceObject] interface EventTarget { void addEventListener(..); 
> > };
> >
> > Window implements EventTarget; Node implements EventTarget; 
> > XMLHttpRequest implements EventTarget;
> >
> > The above would create three copies of the addEventListener API on 
> > each of Window.prototype, Node.prototype, and 
> > XMLHttpRequest.prototype. We favored this simplicity over the desire 
> > to have a single addEventListener definition that could be configured 
> > to affect all implementing interfaces. The extra redundancy in API 
> > definitions hasn't posed a problem for us yet :)
>
> Why couldn't Window, Node and XHR just (effectively) inherit EventTarget so that .prototype.prototype had > addEventListener (only one copy)?

This is certainly an option. However, then you have to answer the question: where in the prototype hierarchy should it be inserted? Consider your options for statically assigning it:
* Prototype of Node.prototype 
** This works for all Nodes types, which is correct, but it means that Window and XHR and Node must all inherit from this interface. This is one approach but involves re-working these existing objects to inherit from EventTarget -- this seems potentially more disruptive to existing code and could have compatibility implications
* Prototype of each of the first-level inheriting interfaces of Node
** This also breaks the established inheritance of DOM L3 Core; but furthermore, it doesn't allow Window or XHR to share that prototype without also inheriting from Node which is not correct
* Prototype of any other interface descending from Node
** Same as the previous problems, but requires unholy modification of natural prototype chains for inherited interfaces--I don't see how this would work without creating multiple instances of the EventTarget prototype, which defeats the primary objective.
** If the current WebIDL spec text is implemented statically, this is what would be required, since a single instance of the "mixin prototype object" could not have multiple [[Prototype]] pointers unless you really start deviating from ES5, but that's counter to my primary goal of alignment with ES5.

Those are the "static" options I see (other than the behavior that I previously described as implemented in IE9). I favor the static cases over any dynamic cases, because prototype chains can be set up before scripts run and are not required to be modified at all during script execution (not modified by the UA anyway...). This keeps things fast and clean.

The dynamic cases are more interesting, and one of these options are what I believe is currently speced in WebIDL: inserting a "mixin prototype object" between an object instance and its prototype, e.g., [correct me if I'm wrong]

var div = document.createElement('div');
EventTarget.prototype === Object.getPrototypeOf(div) // If this interface object and interface prototype object existed
HTMLDivElement.prototype === Object.getPrototypeOf(Object.getPrototypeOf(div))

see: http://dev.w3.org/2006/webapi/WebIDL/#PrototypeRoot


To support such a scheme, we'd need to dynamically create and setup the mixin prototype object whenever instances are created, which is a lot of extra processing considering that all we have to do now is just 'new' [conceptually] the instance of the given HTMLDivElement.

Again, I maintain that this extra complexity is not needed, and that the cases where you want a single definition that is shared among implementing interfaces is really worth the trouble. In the EventTarget case, you have 3 copies instead of 1. That's not such a big deal I think. It becomes more interesting when implementing SVG's OM, and trying to decipher what the WebIDL binding for SVG 1.1 OM should be, since the SVG spec makes heavy use of multiple inheritance syntax. We applied our same "implements" logic to reformulate much of the SVG spec into what I believe the original authors intended (where they were using multiple inheritance for a spec convenience I believe, not as an intended implementation pattern. For example:

see: http://www.w3.org/TR/SVG/struct.html#InterfaceSVGSVGElement


interface SVGSVGElement : SVGElement,
                          SVGTests,
                          SVGLangSpace,
                          SVGExternalResourcesRequired,
                          SVGStylable,
                          SVGLocatable,
                          SVGFitToViewBox,
                          SVGZoomAndPan,
                          DocumentEvent,
                          ViewCSS,
                          DocumentCSS {
   // ...
};

In IE9, the JavaScript binding looks like this:

interface SVGSVGElement : SVGElement {
   // ...
};

SVGSVGElement implements SVGTests;
SVGSVGElement implements SVGLangSpace;
//SVGSVGElement implements SVGExternalResourcesRequired; (not implemented)
SVGSVGElement implements SVGStylable;
SVGSVGElement implements SVGLocatable;
SVGSVGElement implements SVGFitToViewBox;
SVGSVGElement implements SVGZoomAndPan;
SVGSVGElement implements DocumentEvent;
SVGSVGElement implements ViewCSS;
//SVGSVGElement implements DocumentCSS; (not implemented)
// ...

This allows us to have a sane JavaScript binding using an inheritance model (only single inheritance allowed for JavaScript bindings; translated to [[Prototype]] property), and the WebIDL "implements" model (where APIs are essentially copied onto the SVGSVGElement prototype and other prototypes they mix into). I don't think there is much interest in having a single entry-point for defining the SVGLocatable APIs, for example; and their mix-in pattern is so irregular that figuring out where to put the [PrototypeRoot] attribute would be a real pain.

I hope this helps clarify my position,

-Travis
Received on Wednesday, 3 November 2010 20:01:14 UTC

This archive was generated by hypermail 2.3.1 : Wednesday, 8 May 2013 19:30:03 UTC