Skip to content

feat: add support for parsing async_sequence<T> type #775

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jul 23, 2025
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ properties:
* `sourceName`: the source name you passed to `parse()`.
* `level`: `"error"` by default, can be `"warning"` for some validations for e.g. potential future deprecations.
* `ruleName`: Only for validations. Currently the followings are supported:
* `async-iterable-idl-to-js`: `async_iterable` types cannot be returned from the IDL to JS.
* `attr-invalid-type`: Attributes cannot have sequences, records, nor dictionaries.
* `dict-arg-default`: Optional dictionary type arguments must have a default value of `{}`.
* `dict-arg-optional`: Dictionary type arguments must be optional if the type does not include a required field.
Expand Down Expand Up @@ -236,7 +237,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_sequence").
* `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_sequence", "sequence", "record"].includes(this.idlType.generic)
) {
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_sequence") {
const message = `async_sequence types cannot be returned as a callback argument.`;
yield validationError(
arg.tokens.name,
arg,
"async-sequence-idl-to-js",
message,
);
}
}
yield* this.idlType.validate(defs);
}

Expand Down
9 changes: 9 additions & 0 deletions lib/productions/operation.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ export class Operation extends Base {
yield validationError(this.tokens.open, this, "incomplete-op", message);
}
if (this.idlType) {
if (this.idlType.generic === "async_sequence") {
const message = `async_sequence types cannot be returned by an operation.`;
yield validationError(
this.idlType.tokens.base,
this,
"async-sequence-idl-to-js",
message,
);
}
yield* this.idlType.validate(defs);
}
for (const argument of this.arguments) {
Expand Down
2 changes: 2 additions & 0 deletions lib/productions/type.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ function generic_type(tokeniser, typeName) {
"FrozenArray",
"ObservableArray",
"Promise",
"async_sequence",
"sequence",
"record",
);
Expand All @@ -42,6 +43,7 @@ function generic_type(tokeniser, typeName) {
ret.subtype.push(subtype);
break;
}
case "async_sequence":
case "sequence":
case "FrozenArray":
case "ObservableArray": {
Expand Down
1 change: 1 addition & 0 deletions lib/tokeniser.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ const nonRegexTerminals = [
"ObservableArray",
"Promise",
"async_iterable",
"async_sequence",
"bigint",
"boolean",
"byte",
Expand Down
3 changes: 3 additions & 0 deletions test/invalid/baseline/async-sequence-const.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Syntax error at line 3 in async-sequence-const.webidl, since `interface AsyncSequence`:
const async_sequence<long, short
^ Const lacks a type
9 changes: 9 additions & 0 deletions test/invalid/baseline/async-sequence-idl-to-js.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
(attr-invalid-type) Validation error at line 3 in async-sequence-idl-to-js.webidl, inside `interface asyncIterableAsAttribute -> attribute invalid`:
attribute async_sequence<short> invalid;
^ Attributes cannot accept async_sequence types.
(async-sequence-idl-to-js) Validation error at line 5 in async-sequence-idl-to-js.webidl, inside `interface asyncIterableAsAttribute -> operation invalidOp`:
async_sequence<DOMString> invalidOp
^ async_sequence types cannot be returned by an operation.
(async-sequence-idl-to-js) Validation error at line 8 in async-sequence-idl-to-js.webidl, inside `callback DoSomething -> argument bool`:
(async_sequence<DOMString> bool);
^ async_sequence types cannot be returned as a callback argument.
3 changes: 3 additions & 0 deletions test/invalid/baseline/async-sequence-two-params.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Syntax error at line 3 in async-sequence-two-params.webidl, since `interface AsyncSequence`:
async_sequence<long, short> foo(
^ Missing closing bracket after async_sequence
4 changes: 4 additions & 0 deletions test/invalid/idl/async-sequence-const.webidl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[Exposed=Window]
interface AsyncSequence {
const async_sequence<long, short> ASYNC_SEQUENCE = 0;
};
9 changes: 9 additions & 0 deletions test/invalid/idl/async-sequence-idl-to-js.webidl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[Exposed=Window]
interface asyncIterableAsAttribute {
attribute async_sequence<short> invalid;
attribute (async_sequence<short> or boolean) invalidUnion; // TODO
async_sequence<DOMString> invalidOp();
};

callback DoSomething = Promise<DOMString> (async_sequence<DOMString> bool);

4 changes: 4 additions & 0 deletions test/invalid/idl/async-sequence-two-params.webidl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[Exposed=Window]
interface AsyncSequence {
async_sequence<long, short> foo();
};
131 changes: 131 additions & 0 deletions test/syntax/baseline/async-sequence.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
[
{
"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_sequence",
"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_sequence",
"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": "eof",
"value": ""
}
]
7 changes: 7 additions & 0 deletions test/syntax/idl/async-sequence.webidl
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
interface Canvas {
Promise<undefined> drawPolygonAsync(async_sequence<float> coordinates);
};

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