Bug 16767 - Allow user objects for Array[] types
Allow user objects for Array[] types
Status: RESOLVED FIXED
Product: WebAppsWG
Classification: Unclassified
Component: WebIDL
unspecified
PC Windows 3.1
: P2 normal
: ---
Assigned To: Cameron McCormack
public-webapps-bugzilla
:
Depends on:
Blocks:
  Show dependency treegraph
 
Reported: 2012-04-18 07:52 UTC by Simon Pieters
Modified: 2013-06-17 01:15 UTC (History)
7 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Simon Pieters 2012-04-18 07:52:14 UTC
Why does Array[] conversion from JS value to IDL value not accept a user object like {'0':'foo', 'length':1}? (It uses the same rule as sequence<T> which throws TypeError for user objects, if I'm reading it correctly.)
Comment 1 Boris Zbarsky 2012-04-18 15:09:30 UTC
Presumably so that sequences and dictionaries are distinguishable (which they are right now; see http://dev.w3.org/2006/webapi/WebIDL/#dfn-distinguishable )?
Comment 2 Erik Arvidsson 2012-04-18 18:21:44 UTC
Yeah, this behavior is very non-javascripty.

What is the use case to be able to distinguish between a dictionary with a UInt32 length property and an Array?
Comment 3 Boris Zbarsky 2012-04-18 18:45:11 UTC
Are these objects dictionaries or arrays:

  { length: '1', '0': 'foo' }

  { length: { valueOf: function() { return 1; } }, '0': 'foo' }

?

"JavaScripty" behavior would be that if you try to use them as arrays they act like arrays.  But that's hard to reconcile with the whole "dictionaries are a hashtable" thing unless you just disallow overloads that can take a dictionary or an array altogether, right?
Comment 4 Simon Pieters 2012-04-19 10:41:52 UTC
If there's no API currently that overloads dictionary and array, maybe we can just disallow it and support objects as arrays.
Comment 5 Cameron McCormack 2012-06-20 03:21:21 UTC
Overloading between array/sequence types and dictionary types is indeed why { length: 0 } is always treated as a dictionary.  The use case I had for this was Anne's proposed DOM manipulation methods, which would have had such an overloading, but I think that API never eventuated.  So right now there is no need for overloading like this.

I think our options are:

1. Revert arrays/sequences to being indistinguishable from
   dictionary types, thereby disallowing overloading on these types, and
   allowing JS Objects to be converted to arrays/sequences.

2. Keep the two types distinguishable, but all JS Arrays and Objects to be
   used for both, and always prefer to convert to a dictionary rather than an
   array/sequence.  This wouldn't allow overloading to do anything useful, but
   it would allow a JS Object to be converted to an array/sequence when it's no
   overloaded with a dictionary type.

3. Keep the two types distinguishable, but treat all JS objects with a "length"
   property as an array/sequence, and all others as dictionaries.

4. Do nothing.

I used to dislike option 3 because I didn't want the overload resolution algorithm to poke into the value of an object in order to disambiguate the call (currently it only looks at the type of values), but I could live it with actually.
Comment 6 Boris Zbarsky 2012-06-20 03:37:41 UTC
Hmm.  The problem I see with #3 is that the poking step can run arbitrary script, right?  Is that OK?  It certainly somewhat complicates implementation, in the sense that you have to be very careful about how you optimize overload resolution in cases when this poking can happen....
Comment 7 Cameron McCormack 2012-06-20 03:41:19 UTC
That is true, if the object is a Proxy, and all we are doing is checking for the property's existence.  We could define it so that this poking is done at a defined time, and all up front before the actual overload resolution is performed, if we know that there will or might be such poking needed.

Or, we could disallow Proxies from being considered as arrays/sequences.
Comment 8 Simon Pieters 2012-06-20 06:50:08 UTC
(1) seems OK. There might not ever be an API that needs overloading of sequence and dict. If there is, we can try to solve it in a way that works well with that API then.

(2) is bad because it allows overloading but overloading would be broken.

(3) is bad because comment 6 and it makes it a property of the provided input instead of a property of the interface.

(4) is bad because comment 2.

Hence, I prefer (1).
Comment 9 Lachlan Hunt 2012-06-21 11:49:24 UTC
Options 1 and 2 seem acceptable to me and would address the problem that the arrises in Selectors API with the current definition, allowing JQuery objects with a colletion of elements to be passed as the refNodes parameter.
Comment 10 Cameron McCormack 2012-12-10 03:59:10 UTC
I believe that there is no current API that needs to have sequences and dictionaries distinguishable.  I think Simon is right and we should do (1), disallow it (again) for now, and if an API comes up where we do need it, we solve the problem then (and solve it under the assumption that people are relying on the ability to pass { "0": 123, length: 1 } to a function expecting sequence<T>.
Comment 12 Boris Zbarsky 2012-12-10 05:55:42 UTC
This is still broken because the overload resolution algorithm didn't get updated...  In particular, if I pass in a non-array object that won't actually select a sequence overload even if one is available, as far as I can tell.
Comment 13 Cameron McCormack 2012-12-10 06:04:52 UTC
Good point.  I think this should do it:

http://dev.w3.org/cvsweb/2006/webapi/WebIDL/Overview.xml.diff?r1=1.581;r2=1.582;f=h
Comment 14 Boris Zbarsky 2012-12-10 06:13:17 UTC
I'm not sure that works.  Specifically, non-callback interfaces and sequences are currently distinguishable.  But as the overload resolution algorithm is written, if I have this IDL

  void foo(long arg);
  void foo(sequence<long> arg);

and I pass in a platform object that's not a platform array object, it won't pick up the second overload, because it fails the "any other type of object" test as far as I can tell...
Comment 15 Cameron McCormack 2012-12-10 06:57:09 UTC
(In reply to comment #14)
> I'm not sure that works.  Specifically, non-callback interfaces and
> sequences are currently distinguishable.  But as the overload resolution
> algorithm is written, if I have this IDL
> 
>   void foo(long arg);
>   void foo(sequence<long> arg);
> 
> and I pass in a platform object that's not a platform array object, it won't
> pick up the second overload, because it fails the "any other type of object"
> test as far as I can tell...

You're right that "any other type object" wording is troublesome; I too eagerly merged some of the steps in there.  I think we still want platform objects that support indexed properties to be able to be passed as sequence<T> types.

How about this:

http://dev.w3.org/cvsweb/2006/webapi/WebIDL/Overview.xml.diff?r1=1.582;r2=1.584;f=h

Note that this continues to disallow native Array objects from being passed as dictionary, callback interface and callback function values.
Comment 16 Boris Zbarsky 2012-12-10 07:21:33 UTC
That's a bit better, but there are still some weird artifacts:

1)  Why is passing a platform object that does not support indexed properties not ok?  Not that I can think of use cases for it...

2)  Passing a Date or RegExp will work if your IDL looks like this:

  void foo(sequence<long> arg);

but not if the IDL looks like this:

  void foo(long arg);
  void foo(sequence<long> arg);

which is a bit confusing.
Comment 17 Cameron McCormack 2012-12-10 23:31:40 UTC
(In reply to comment #16)
> That's a bit better, but there are still some weird artifacts:
> 
> 1)  Why is passing a platform object that does not support indexed
> properties not ok?  Not that I can think of use cases for it...

As a sequence or array type?  Then it couldn't be distinguishable with those interface types.  It seems more likely to be used in an API to me than needing to distinguish a dictionary and a sequence type.

> 2)  Passing a Date or RegExp will work if your IDL looks like this:
> 
>   void foo(sequence<long> arg);
> 
> but not if the IDL looks like this:
> 
>   void foo(long arg);
>   void foo(sequence<long> arg);
> 
> which is a bit confusing.

Because the overload resolution algorithm and the type conversion algorithms aren't in sync?  I'll fix that.
Comment 18 Boris Zbarsky 2012-12-10 23:37:02 UTC
> As a sequence or array type?

Yes.

> Then it couldn't be distinguishable with those interface types.

But passing in a platform object that _does_ support indexed properties is OK.  And those are still distinguishable from sequence types.  In fact there are specs out there that use this (e.g. WebGL overloads on sequence vs typed array).

> Because the overload resolution algorithm and the type conversion algorithms
> aren't in sync?

Yes.
Comment 19 Cameron McCormack 2012-12-10 23:47:38 UTC
(In reply to comment #18)
> But passing in a platform object that _does_ support indexed properties is
> OK.  And those are still distinguishable from sequence types.  In fact there
> are specs out there that use this (e.g. WebGL overloads on sequence vs typed
> array).

Hmm, it is a bit of an oddity then that we make them distinguishable so that they can be overloaded, but we still allow a typed array object to be converted to a sequence<> type successfully in other (non-overloading) contexts.

For example take:

    void uniform1fv(WebGLUniformLocation? location, Float32Array v);
    void uniform1fv(WebGLUniformLocation? location, sequence<GLfloat> v);

What is the desired behaviour here if you pass in a say Int32Array?  Should it select the sequence<> overload, and run a bit slower?  Or should it throw?

My thought in allowing platform objects that support indexed properties to things expecting a sequence is so that e.g.

  interface A {
    void f(sequence<Node> x);
  };

  a.f(aNodeList);

would just work.

Is it maybe not worth supporting this automatically, and instead requiring the spec author to have an explicit overload to take a NodeList?

> > Because the overload resolution algorithm and the type conversion algorithms
> > aren't in sync?
> 
> Yes.

I've fixed that bit: http://dev.w3.org/cvsweb/2006/webapi/WebIDL/Overview.xml.diff?r1=1.584;r2=1.585;f=h
Comment 20 Boris Zbarsky 2012-12-11 00:55:43 UTC
> What is the desired behaviour here if you pass in a say Int32Array?  Should it
> select the sequence<> overload, and run a bit slower?  Or should it throw?

Current spec says select the sequence<> overload, and it what at least Gecko implements.  I haven't checked other browsers offhand...

> Is it maybe not worth supporting this automatically, and instead requiring the
> spec author to have an explicit overload to take a NodeList?

I have no problem with converting platform objects that have indexed properties to sequences.  I don't even have a problem with doing it for platform objects that _don't_ have indexed properties.  Given that overload resolution and union subtype conversions define an order in which you try things, I don't really see a problem with having two things that can both convert to sequence be distinguishable.  After all, we distinguish between all sorts of stuff that can convert to string!
Comment 21 Boris Zbarsky 2012-12-11 00:56:45 UTC
One other note.  From a pure implementation point of view, it's easier and faster to just treat all objects the same than to do a bunch of verification that your objects are some particular kind or not some other particular kind...  So unless there are good API reasons for such restrictions, I'd rather not have them.
Comment 22 Cameron McCormack 2012-12-11 05:29:57 UTC
OK, I withdraw my earlier held reservations about converting any kind of platform object to sequences:

  http://dev.w3.org/cvsweb/2006/webapi/WebIDL/Overview.xml.diff?r1=1.587;r2=1.589;f=h

This change:

  * makes any kind of object acceptable to be converted to sequences, array types,
    dictionaries, callback functions and callback interface types -- except for
    Date and RegExp objects, since they're only treated as their corresponding
    IDL types (or object, of course)

  * makes the corresponding change for the "convert ES value to IDL union type
    value" algorithm

  * ensures that the "convert ES value to IDL sequence value" and "... to
    dictionary value" and "... to callback function/interface value" algorithms
    all prevent a Date or RegExp object from being converted

I think this makes overload resolution and type conversion even simpler than it was before.