Re: Notes from Monday's meeting with TC39 folks

On Thu, Oct 8, 2009 at 12:56 PM, Maciej Stachowiak <mjs@apple.com> wrote:
>
> On Oct 8, 2009, at 12:25 PM, Jonas Sicking wrote:
>
> On Wed, Oct 7, 2009 at 10:50 PM, Maciej Stachowiak <mjs@apple.com> wrote:
>
> How would you specify canvas's getImageData() with this approach? Or
>
> drawImage()? Seems like it is impossible to specify the interface correctly
>
> with the union type mechanism. It's one thing to "discourage", but if IDL
>
> can't specify the behavior of existing interfaces, then it will fail in its
>
> purpose.
>
> I definitely agree that union types + [optional] provides less
> expressiveness than overloading. For example you can't say that a
> function can only take 0 or 2 floats. Or can only take one two floats
> or an ImageData (like CanvasRenderingContext2D.createImageData).
>
> However you can always describe that in prose. We'll never have a IDL
> language that can express all the needed constraints. For example the
> ability to say that a float argument must be non-negative, or non-NaN,
> or say that if a boolean argument is missing it should default to
> 'true'. It'll always be a judgement call what to allow IDL to express,
> and what needs to be put in prose. For example the IDL for
> CanvasRenderingContext2D.drawImage might actually be easier to
> understand if it was expressed in prose. It currently has 6 overloads,
> half of which has [optional] arguments.
>
> Anything that's in prose but reasonably could be in the IDL without adding
> undue complexity is a lost opportunity. Because then we either have to
> hand-write the binding, or we have to invent our own syntax to get the IDL
> compiler to do it.
>
> One possible place to draw the line is how much you'd want a
> scripting<->C++ conversion layer to support. For example, would you
> expect such a layer to enforce all of the requirements for drawImage?
>
> I would expect it to enforce the type requirements and dispatch to the right
> internal version of the method, yes.
>
> In gecko we currently only have one drawImage function in our internal
> IDL which the conversion layer always dispatches to, that function
> then does all the checking.
>
> It's possible that other conversion layers would do things differently
> (in fact, i'd be surprised if that wasn't the case). And there's
> definitely other ways to draw the line for what goes into the IDL.
>
> What we strive for in WebKit is to have generated code do the type checking
> and conversion of incoming JS values, and then dispatch to a core
> implementation in C++ which can also be bound (via codegen from IDL) to
> other languages such as Objective-C or Python. We don't want to make the
> core methods depend on implementation details of the JavaScript engine in
> any way.

I agree this is what we should strive for. In most cases where our IDL
isn't expressive enough we have custom code that maps between
javascript calls and C++ code. Seems like basically the same design as
your autogenerated code, with the difference that you auto generate
code from IDL, whereas we hand-write it. And in a few cases we got
lazy and didn't put the custom code outside of the C++ API, but rather
inside it.

I personally don't really care that much if we go with overloaded
methods or union types. I do tend to think that once you get into this
area you generally have a messy API. For example drawImage does not
seem user friendly to me. It would have been much nicer if it could
have been written for example as:

drawImage(HTMLElement image, float dx, float dy, optional float dw,
optional float dh, optional float sx, optional float sy, optional
float sw, optional float sh);

With resonable default behavior for when the various optional
arguments are left out. This seems much more javascript-like and
friendly to web developers. As for createImageData, two separate
function names could have been used.

Another problem with drawImage as defined now is that it specifies
that TYPE_MISMATCH_ERR should be thrown if the first argument isn't
one of the types that the various overloads define for the first
argument. However this seems strange to mandate in prose given that
WebIDL technically says that there is no function that matches,
meaning that no function is called that could throw that specific
error. I.e. it should be up to WebIDL to define what to throw.

Which also begs the question, what should be thrown if
CanvasRenderingContext2D.createImageData is called with only one
argument, which is a float. Should you throw TYPE_MISMATCH_ERR, or
should you throw something indicating that too few arguments were
passed. Or something indicating that there's no such function?

But as I said, I don't really care that much about what syntax to use,
as long as everything is well defined.

> With regards to how the undefined value should be coerced to a string,
> Jonas said (paraphrasing):
>  A lot of people have argued that we should always use the String
>  constructor so undefined gets converted to "undefined" (and similarly
>  for null).  I see logic in that too.  What makes the least amount of
>  sense is what we do in Gecko, where null comes "" and undefined
>  becomes "undefined".  I think we should recommend converting both to
>  the empty string and seeing what people say.  We can use union types
>  to handled undefined differently if necessary.  I think IE generally
>  stringifies both null and undefined.
>
> Many of the DOM Core and DOM Events interfaces specifically mandate treating
> null the same as the empty string, and in at least some cases this works
> interoperably.
>
> I think changing the behavior for undefined could be a compatibility risk.
>
> I'd be very interested to gather data here. I suspect that *in
> general* null and undefined are rarely passed to these functions, so I
> think we're a little free to play around with behavior here. There are
> exceptions of course, for example I suspect treating null as "null" in
> the namespaceURI argument for setAttributeNS would break the web.
>
> I would expect they are passed accidentally often enough to matter. Because
> any way you can abuse an API, some Web content is doing it, as long as it
> seems to work for their purposes.
>
> While Gecko by default treats null as the empty string, I was under
> the impression that most (all?) other major UAs by default treat null
> as "null". And that for example WebKit used IDL extended attributes
> for when to treat null as "". (This is one of the reasons I suspect
> people don't generally depend on behavior here.)
>
> That's right, but over time we've changed more and more places in our IDL to
> treat null as "". Our plan is to eventually flip the default.

Interesting. Could you give an example of where you had to do this, and why?

> As for undefined, I think UAs fairly consistently treat that as "undefined".
>
> Yes, in almost all places I think. I did find a few methods where WebKit
> will treat undefined as an empty string, I am not sure how many of these are
> essential however. Examples include DOMImplemenation.createDocument(),
> Window.prompt(), setCustomValidity() on form controls.

Are these exceptions consistent across UAs? If not, do you intend to
change this in WebKit?

> Later discussion, assuming that there was no Undefined type in the IDL,
> suggested that the Null IDL type could be specified as either null or
> undefined in ES, but when returned from the implementation to ES would
> always be denoted using null.
> Since null and undefined are often treated differently, this seems like a
> bad idea.
>
> Really? And how do you mean "treated differently". For strings, see
> discussion above, too many unknowns at this point for me to give a
> firm answer. For objects, it seems like at least gecko treats
> undefined as null, need to check other UAs.
>
> I mean when passed as strings. As numbers, undefined converts to NaN and
> null converts to 0. I haven't done testing or study of object parameters
> yet.

So strings are discussed above.
Number arguments seem like they can always be converted using the
Number constructor, that'll give the result you mention.
Booleans I imagine both null and undefined should be converted to
false. Basically use the Boolean constructor for any value.
That leaves objects. Here I would argue that null and undefined should
be treated the same.

> Yuck. Many DOM APIs can return null, having to annotate all of them seems
> like a lot of work for low return.
>
> I think we currently have a problem where lots of functions take
> objects, but do not define behavior when null is passed in. I'd like
> to fix this by making it explicit in the IDL when null is an
> acceptable in-value, and in WebIDL define what exception is thrown if
> null is passed in where not allowed. Just like WebIDL should describe
> what exception to pass to a function that takes a Node but is passed a
> string.
>
> In cases where DOM APIs already throw an exception when passed null for an
> object parameter, is it consistently the same exception?

Well, the big problem is that most specs don't mention what to do for
null values. And I couldn't find any that clearly says that a specific
exception should be thrown. A generous reading of the traversal spec
could interpret INVALID_NODE_TYPE_ERR as being the exception to throw,
but that's far from clear. HTML5 does actually explicitly say that
TYPE_MISMATCH_ERR is thrown for null values in some cases.

As for implementations, I don't know. Gecko I think fairly
consistently throws a specific gecko-specific event (INVALID_POINTER).
So I suspect other implementations aren't consistent with that.

I think the goal here should be that a consistent exception is thrown.
Which is another reason to let this be defined by WebIDL.

> Those of us who were able to stay until the end of the meeting (all but
> Allen) were happy with a few initial recommendations, but I
> unfortunately don’t have a note of them.  I believe they included:
>
>  * Not allowing callable objects in future APIs
>
> If it's allowed, it's allowed - at best we can say it SHOULD NOT be used in
> new APIs. I don't think it would be appropriate to grandfather in specific
> interfaces from other specs by name, and a requirement that doesn't name the
> specific interfaces would have no teeth.
>
> Gecko only supports callable on the document.all collection. I'm fine
> with attempting to remove that and see how much people complain. I've
> never heard anyone ask for callability on other collections.
>
> Microsoft is apparently looking into if they can remove callable.
>
> I'd say lets not put callable into the specifications. UAs are always
> free to implement additional features that aren't in the spec,
> everyone already does. Once we've tried to remove callable and it
> turns out to break the web we can always add it to the spec then.
>
> I'd rather we remove it from the spec after we determine that it can safely
> be removed and that everyone is on board with this. Just leaving it
> undefined is a cop-out.

This seems like more work spec authors. Additionally can make it
harder to remove the callability if specs says it should be there.
Granted, keeping callable in WebIDL isn't a problem there, keeping it
in HTML5 would be though (which I think is the only spec that
currently uses callable).

> What I think makes more sense in the short term is to move callability to
> supplemental interfaces in the Obsolete section, as HTML5 does for IDL
> attributes that reflect obsolete content attributes. That puts it out of
> view of authors, but makes the implementation behavior well-defined.
> And finally, I don't think callability on random objects is even that bad.
> It doesn't violate deep design assumptions of ECMAScript for objects to be
> callable without being a Function. There's even cases where it might be a
> reasonable API (it does seem a bit icky for collections, admittedly). It
> doesn't get in your way if you don't use it or know about it, unlike
> property catchalls. It doesn't seem like its worth a huge effort to try to
> purge it from the platform.

Once there's an API that wants to use it, for actual good, we can talk ;)

/ Jonas

Received on Thursday, 8 October 2009 21:36:07 UTC