Re: Extended attributes vs. new types

On 1/11/17 6:11 PM, Domenic Denicola wrote:
> What exact steps would go wrong in the spec if we used [AllowShared] Uint8Array, or [EnsureUTF16] DOMString, instead of MaybeSharedUint8Array/USVString? What are the problematic Web IDL snippets, JS inputs, and spec steps that we end up at?

Consider the following IDL definitions:

   dictionary Dict {
     USVString member1;
     sequence<USVString> member2;
   };

   interface Iface {
     void foo(optional Dict arg);
     void bar((Iface or USVString) arg);
     void baz(sequence<USVString> arg);
     void something(record<DOMString, USVString> arg);
     attribute USVString attr;
   };

How would we handle these using "[EnsureUTF16] DOMString" instead?  It 
seems to me like we'd need the following:

1)  The ability to specify [EnsureUTF16] on dictionary members.
2)  The ability to specify [EnsureUTF16] on attributes.
3)  The ability to specify [EnsureUTF16] on operation attributes.
4)  Steps in 
https://heycam.github.io/webidl/#create-sequence-from-iterable to 
propagate through the [EnsureUTF16] to the element conversions.
5)  Steps in https://heycam.github.io/webidl/#es-to-record to propagate 
through the [EnsureUTF16] to conversions.  For the special case of 
USVString there's the additional complication of the key and value maybe 
wanting different behavior, but that's not a problem for [AllowShared].

It's hard to say whether https://heycam.github.io/webidl/#es-to-union to 
union would need changes too.  It would depend on exactly how the 
phrasing went for the [EnsureUTF16] thing.

If we add new parametrized types they would need to know about 
[EnsureUTF16] too.  If we add more types parametrized on more than one 
type (a la record), things get pretty annoying.

This is all on the spec end.  On the implementation end, you obviously 
have to explicitly thread the [EnsureUTF16] thing and any other such 
flags through sequence conversions, record conversions, and union 
conversions.  It makes things somewhat more complicated.

> Why is this different than things like [Clamp] or [EnforceRange]?

It's not.

[Clamp] and [EnforceRange] have (1), (2), (3) above.  They don't have 
(4) or (5).  If you want to pass a sequence of clamped integers, you're 
out of luck.

If you want to have a clamped integer in a union, you're out of luck 
too, because, for example, https://heycam.github.io/webidl/#Clamp says:

   The [Clamp] extended attribute must not appear on a read only
   attribute, or an attribute, operation argument or dictionary member
   that is not of an integer type.

> Does [Clamp] also break down when trying to use it with union types?

Well, it's not allowed to be specified on union types at the moment.

If it were allowed, then in terms of the spec I think it would be OK, 
since you could make the argument that the value being converted in 
https://heycam.github.io/webidl/#abstract-opdef-converttoint is the 
value that was passed for the union type, and hence "V is being passed 
as an operation argument annotated with the [Clamp] extended attribute" 
or one of the other two conditions in step 6 is true.

In terms of implementation, unions would need to know to pass through 
the "I was tagged with [Clamp] down to all their conversions".  Note 
that you can't just build this into the union itself, because the same 
union can be used in both [Clamp] and non-[Clamp] situations, right? 
Consider:

   typedef (long or USVString) MyUnion;

   interface Iface {
     void foo([Clamp] MyUnion arg);
     void bar(MyUnion arg);
   };

A binding generator would presumably generate a single instance of 
MyUnion, which would have to have conditional behavior based on whether 
it's used in a [Clamp] or non-[Clamp] context.  And also based on 
whether it's used in [EnforceRange].  And [EnsureUTF16], if that were a 
thing.

> What spec surgery would be involved in fixing this whole situation?

I think the above more or less describes it?

> I guess the idea would be to make the extended attributes apply to the types instead of the arguments, but how exactly would that help?

At that point, how is that really different from just having a different 
type?  Unless you can combine the extended attributes, of course.  But 
if they're mutually exclusive (and [Clamp] and [EnforceRange] are 
specified as mutually exclusive), there is no real semantic difference 
between that and a new type...

There _can_ be the argument that you can apply [Clamp] to a bunch of 
different types, so you would get a sort of "type explosion" if you 
created separate types for all of those.  So it might make sense to have 
[Clamp] that attaches directly to integer types, from that point of 
view.  That would not have the "have to thread this information through" 
problem I describe above, of course, nor the "hey, we're parametrized 
over two types, which one should be clamped?" problem.  Nor the "any 
time we add a new parametrized thing we have to teach it about all these 
frobs" problem.

I hope that helps; happy to try to clarify anything above that's still 
being confusing.

-Boris

Received on Thursday, 12 January 2017 18:55:37 UTC