Skip to content
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ attached to a field called `idlType`:
Where the fields are as follows:

* `type`: String indicating where this type is used. Can be `null` if not applicable.
* `generic`: String indicating the generic type (e.g. "Promise", "sequence").
* `generic`: String indicating the generic type (e.g. "Promise", "sequence", "async iterable").
* `idlType`: String indicating the type name, or array of subtypes if the type is
generic or a union.
* `nullable`: `true` if the type is nullable.
Expand Down
4 changes: 3 additions & 1 deletion lib/productions/attribute.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ export class Attribute extends Base {
yield* this.extAttrs.validate(defs);
yield* this.idlType.validate(defs);

if (["sequence", "record"].includes(this.idlType.generic)) {
if (
["async iterable", "sequence", "record"].includes(this.idlType.generic)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per whatwg/webidl#1489 this PR should be updated.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And per #1500 it should be updated a little more, to use the token async_sequence instead.

) {
const message = `Attributes cannot accept ${this.idlType.generic} types.`;
yield validationError(
this.tokens.name,
Expand Down
13 changes: 13 additions & 0 deletions lib/productions/callback.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
unescape,
autoParenter,
} from "./helpers.js";
import { validationError } from "../error.js";

export class CallbackFunction extends Base {
/**
Expand Down Expand Up @@ -44,6 +45,18 @@ export class CallbackFunction extends Base {

*validate(defs) {
yield* this.extAttrs.validate(defs);
for (const arg of this.arguments) {
yield* arg.validate(defs);
if (arg.idlType.generic === "async iterable") {
const message = `Callback function arguments can not be ${arg.idlType.generic} types.`;
yield validationError(
arg.tokens.name,
arg,
"operation-return-invalid-type",
message,
);
}
}
yield* this.idlType.validate(defs);
}

Expand Down
14 changes: 12 additions & 2 deletions lib/productions/iterable.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,19 @@ export class IterableLike extends Base {
}
}

tokens.termination =
tokeniser.consume(";") ||
tokens.termination = tokeniser.consume(";");
if (
!tokens.termination &&
tokens.async &&
tokens.base.value === "iterable"
) {
// Instead of a missing semicolon, this could be an async iterable return
// type for a regular operation. Let's bail out early in that case.
tokeniser.unconsume(start_position);
return;
} else if (!tokens.termination) {
tokeniser.error(`Missing semicolon after ${type} declaration`);
}

return ret.this;
}
Expand Down
14 changes: 14 additions & 0 deletions lib/productions/type.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ import { ExtendedAttributes } from "./extended-attributes.js";
* @param {string} typeName
*/
function generic_type(tokeniser, typeName) {
const prevPosition = tokeniser.position;
const base = tokeniser.consume(
"async",
"FrozenArray",
"ObservableArray",
"Promise",
Expand All @@ -29,6 +31,14 @@ function generic_type(tokeniser, typeName) {
const ret = autoParenter(
new Type({ source: tokeniser.source, tokens: { base } }),
);
if (base.value === "async") {
ret.tokens.async = base;
ret.tokens.base = tokeniser.consume("iterable");
if (!ret.tokens.base) {
tokeniser.unconsume(prevPosition); // unconsume the "async" token
return;
}
}
ret.tokens.open =
tokeniser.consume("<") ||
tokeniser.error(`No opening bracket after ${base.value}`);
Expand All @@ -42,6 +52,7 @@ function generic_type(tokeniser, typeName) {
ret.subtype.push(subtype);
break;
}
case "async":
case "sequence":
case "FrozenArray":
case "ObservableArray": {
Expand Down Expand Up @@ -169,6 +180,8 @@ export class Type extends Base {

get generic() {
if (this.subtype.length && this.tokens.base) {
if (this.tokens.base.value === "iterable" && this.tokens.async)
return "async iterable";
return this.tokens.base.value;
}
return "";
Expand Down Expand Up @@ -259,6 +272,7 @@ for more information.`;
const type_body = () => {
if (this.union || this.generic) {
return w.ts.wrap([
w.token(this.tokens.async, w.ts.generic),
w.token(this.tokens.base, w.ts.generic),
w.token(this.tokens.open),
...this.subtype.map((t) => t.write(w)),
Expand Down
15 changes: 9 additions & 6 deletions test/invalid/baseline/invalid-attribute.txt
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
(attr-invalid-type) Validation error at line 3 in invalid-attribute.webidl, inside `interface sequenceAsAttribute -> attribute invalid`:
attribute sequence<short> invalid;
^ Attributes cannot accept sequence types.
(attr-invalid-type) Validation error at line 9 in invalid-attribute.webidl, inside `interface recordAsAttribute -> attribute invalid`:
(attr-invalid-type) Validation error at line 9 in invalid-attribute.webidl, inside `interface asyncIterableAsAttribute -> attribute invalid`:
async iterable<short> invalid;
^ Attributes cannot accept async iterable types.
(attr-invalid-type) Validation error at line 15 in invalid-attribute.webidl, inside `interface recordAsAttribute -> attribute invalid`:
<DOMString, DOMString> invalid;
^ Attributes cannot accept record types.
(attr-invalid-type) Validation error at line 17 in invalid-attribute.webidl, inside `interface dictionaryAsAttribute -> attribute dict`:
(attr-invalid-type) Validation error at line 23 in invalid-attribute.webidl, inside `interface dictionaryAsAttribute -> attribute dict`:
attribute Dict dict;
^ Attributes cannot accept dictionary types.
(attr-invalid-type) Validation error at line 18 in invalid-attribute.webidl, inside `interface dictionaryAsAttribute -> attribute dictUnion`:
(attr-invalid-type) Validation error at line 24 in invalid-attribute.webidl, inside `interface dictionaryAsAttribute -> attribute dictUnion`:
attribute (Dict or boolean) dictUnion
^ Attributes cannot accept dictionary types.
(attr-invalid-type) Validation error at line 28 in invalid-attribute.webidl, inside `interface EnforceRangeInReadonlyAttribute -> attribute readOnlyAttr1`:
(attr-invalid-type) Validation error at line 34 in invalid-attribute.webidl, inside `interface EnforceRangeInReadonlyAttribute -> attribute readOnlyAttr1`:
readonly attribute [EnforceRange] long readOnlyAttr1;
^ Readonly attributes cannot accept [EnforceRange] extended attribute.
(attr-invalid-type) Validation error at line 29 in invalid-attribute.webidl, inside `interface EnforceRangeInReadonlyAttribute -> attribute readOnlyAttr2`:
(attr-invalid-type) Validation error at line 35 in invalid-attribute.webidl, inside `interface EnforceRangeInReadonlyAttribute -> attribute readOnlyAttr2`:
readonly attribute [EnforceRange] GPUInt32In readOnlyAttr2;
^ Readonly attributes cannot accept [EnforceRange] extended attribute.
(attr-invalid-type) Validation error at line 30 in invalid-attribute.webidl, inside `interface EnforceRangeInReadonlyAttribute -> attribute readOnlyAttr2`:
(attr-invalid-type) Validation error at line 36 in invalid-attribute.webidl, inside `interface EnforceRangeInReadonlyAttribute -> attribute readOnlyAttr2`:
readonly attribute GPUInt32 readOnlyAttr2;
^ Readonly attributes cannot accept [EnforceRange] extended attribute.
3 changes: 3 additions & 0 deletions test/invalid/baseline/invalid-callback-argument.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(operation-return-invalid-type) Validation error at line 1 in invalid-callback-argument.webidl, inside `callback DoSomething -> argument bool`:
async iterable<DOMString> bool);
^ Callback function arguments can not be async iterable types.
6 changes: 6 additions & 0 deletions test/invalid/idl/invalid-attribute.webidl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ interface sequenceAsAttribute {
attribute (sequence<short> or boolean) invalidUnion; // TODO
};

[Exposed=Window]
interface asyncIterableAsAttribute {
attribute async iterable<short> invalid;
attribute (async iterable<short> or boolean) invalidUnion; // TODO
};

[Exposed=Window]
interface recordAsAttribute {
attribute record<DOMString, DOMString> invalid;
Expand Down
1 change: 1 addition & 0 deletions test/invalid/idl/invalid-callback-argument.webidl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
callback DoSomething = Promise<DOMString> (async iterable<DOMString> bool);
200 changes: 200 additions & 0 deletions test/syntax/baseline/async-iterable-type.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
[
{
"type": "interface",
"name": "Canvas",
"inheritance": null,
"members": [
{
"type": "operation",
"name": "drawPolygonAsync",
"idlType": {
"type": "return-type",
"extAttrs": [],
"generic": "Promise",
"nullable": false,
"union": false,
"idlType": [
{
"type": "return-type",
"extAttrs": [],
"generic": "",
"nullable": false,
"union": false,
"idlType": "undefined"
}
]
},
"arguments": [
{
"type": "argument",
"name": "coordinates",
"extAttrs": [],
"idlType": {
"type": "argument-type",
"extAttrs": [],
"generic": "async iterable",
"nullable": false,
"union": false,
"idlType": [
{
"type": "argument-type",
"extAttrs": [],
"generic": "",
"nullable": false,
"union": false,
"idlType": "float"
}
]
},
"default": null,
"optional": false,
"variadic": false
}
],
"extAttrs": [],
"special": ""
}
],
"extAttrs": [],
"partial": false
},
{
"type": "interface",
"name": "I",
"inheritance": null,
"members": [
{
"type": "operation",
"name": "f1",
"idlType": {
"type": "return-type",
"extAttrs": [],
"generic": "Promise",
"nullable": false,
"union": false,
"idlType": [
{
"type": "return-type",
"extAttrs": [],
"generic": "",
"nullable": false,
"union": false,
"idlType": "undefined"
}
]
},
"arguments": [
{
"type": "argument",
"name": "arg",
"extAttrs": [],
"idlType": {
"type": "argument-type",
"extAttrs": [],
"generic": "async iterable",
"nullable": false,
"union": false,
"idlType": [
{
"type": "argument-type",
"extAttrs": [
{
"type": "extended-attribute",
"name": "XAttr",
"rhs": null,
"arguments": []
}
],
"generic": "",
"nullable": false,
"union": false,
"idlType": "float"
}
]
},
"default": null,
"optional": false,
"variadic": false
}
],
"extAttrs": [],
"special": ""
}
],
"extAttrs": [],
"partial": false
},
{
"type": "interface",
"name": "asyncIterableReturn",
"inheritance": null,
"members": [
{
"type": "operation",
"name": "stream",
"idlType": {
"type": "return-type",
"extAttrs": [],
"generic": "async iterable",
"nullable": false,
"union": false,
"idlType": [
{
"type": "return-type",
"extAttrs": [],
"generic": "",
"nullable": false,
"union": false,
"idlType": "short"
}
]
},
"arguments": [
{
"type": "argument",
"name": "foo",
"extAttrs": [],
"idlType": {
"type": "argument-type",
"extAttrs": [],
"generic": "async iterable",
"nullable": false,
"union": false,
"idlType": [
{
"type": "argument-type",
"extAttrs": [],
"generic": "",
"nullable": false,
"union": false,
"idlType": "short"
}
]
},
"default": null,
"optional": false,
"variadic": false
}
],
"extAttrs": [],
"special": ""
}
],
"extAttrs": [
{
"type": "extended-attribute",
"name": "Exposed",
"rhs": {
"type": "identifier",
"value": "Window"
},
"arguments": []
}
],
"partial": false
},
{
"type": "eof",
"value": ""
}
]
12 changes: 12 additions & 0 deletions test/syntax/idl/async-iterable-type.webidl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
interface Canvas {
Promise<undefined> drawPolygonAsync(async iterable<float> coordinates);
};

interface I {
Promise<undefined> f1(async iterable<[XAttr] float> arg);
};

[Exposed=Window]
interface asyncIterableReturn {
async iterable<short> stream(async iterable<short> foo);
};