diff --git a/README.md b/README.md index cef60fac..7f488b06 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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. diff --git a/lib/productions/attribute.js b/lib/productions/attribute.js index 0dab2516..0c203a0b 100644 --- a/lib/productions/attribute.js +++ b/lib/productions/attribute.js @@ -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, diff --git a/lib/productions/callback.js b/lib/productions/callback.js index 56dba106..316a19f1 100644 --- a/lib/productions/callback.js +++ b/lib/productions/callback.js @@ -5,6 +5,7 @@ import { unescape, autoParenter, } from "./helpers.js"; +import { validationError } from "../error.js"; export class CallbackFunction extends Base { /** @@ -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); } diff --git a/lib/productions/operation.js b/lib/productions/operation.js index f4c561be..bec24a00 100644 --- a/lib/productions/operation.js +++ b/lib/productions/operation.js @@ -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) { diff --git a/lib/productions/type.js b/lib/productions/type.js index d4d64165..393743d0 100644 --- a/lib/productions/type.js +++ b/lib/productions/type.js @@ -20,6 +20,7 @@ function generic_type(tokeniser, typeName) { "FrozenArray", "ObservableArray", "Promise", + "async_sequence", "sequence", "record", ); @@ -42,6 +43,7 @@ function generic_type(tokeniser, typeName) { ret.subtype.push(subtype); break; } + case "async_sequence": case "sequence": case "FrozenArray": case "ObservableArray": { diff --git a/lib/tokeniser.js b/lib/tokeniser.js index 7c787a1a..a934c52a 100644 --- a/lib/tokeniser.js +++ b/lib/tokeniser.js @@ -73,6 +73,7 @@ const nonRegexTerminals = [ "ObservableArray", "Promise", "async_iterable", + "async_sequence", "bigint", "boolean", "byte", diff --git a/test/invalid/baseline/async-sequence-const.txt b/test/invalid/baseline/async-sequence-const.txt new file mode 100644 index 00000000..9cb28ef3 --- /dev/null +++ b/test/invalid/baseline/async-sequence-const.txt @@ -0,0 +1,3 @@ +Syntax error at line 3 in async-sequence-const.webidl, since `interface AsyncSequence`: + const async_sequence attribute invalid`: + attribute async_sequence 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 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 bool); + ^ async_sequence types cannot be returned as a callback argument. diff --git a/test/invalid/baseline/async-sequence-two-params.txt b/test/invalid/baseline/async-sequence-two-params.txt new file mode 100644 index 00000000..b14f1242 --- /dev/null +++ b/test/invalid/baseline/async-sequence-two-params.txt @@ -0,0 +1,3 @@ +Syntax error at line 3 in async-sequence-two-params.webidl, since `interface AsyncSequence`: + async_sequence foo( + ^ Missing closing bracket after async_sequence diff --git a/test/invalid/idl/async-sequence-const.webidl b/test/invalid/idl/async-sequence-const.webidl new file mode 100644 index 00000000..8d48f57d --- /dev/null +++ b/test/invalid/idl/async-sequence-const.webidl @@ -0,0 +1,4 @@ +[Exposed=Window] +interface AsyncSequence { + const async_sequence ASYNC_SEQUENCE = 0; +}; diff --git a/test/invalid/idl/async-sequence-idl-to-js.webidl b/test/invalid/idl/async-sequence-idl-to-js.webidl new file mode 100644 index 00000000..4e558581 --- /dev/null +++ b/test/invalid/idl/async-sequence-idl-to-js.webidl @@ -0,0 +1,9 @@ +[Exposed=Window] +interface asyncIterableAsAttribute { + attribute async_sequence invalid; + attribute (async_sequence or boolean) invalidUnion; // TODO + async_sequence invalidOp(); +}; + +callback DoSomething = Promise (async_sequence bool); + diff --git a/test/invalid/idl/async-sequence-two-params.webidl b/test/invalid/idl/async-sequence-two-params.webidl new file mode 100644 index 00000000..472b556d --- /dev/null +++ b/test/invalid/idl/async-sequence-two-params.webidl @@ -0,0 +1,4 @@ +[Exposed=Window] +interface AsyncSequence { + async_sequence foo(); +}; diff --git a/test/syntax/baseline/async-sequence.json b/test/syntax/baseline/async-sequence.json new file mode 100644 index 00000000..6978e1fd --- /dev/null +++ b/test/syntax/baseline/async-sequence.json @@ -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": "" + } +] diff --git a/test/syntax/idl/async-sequence.webidl b/test/syntax/idl/async-sequence.webidl new file mode 100644 index 00000000..6c5405a8 --- /dev/null +++ b/test/syntax/idl/async-sequence.webidl @@ -0,0 +1,7 @@ +interface Canvas { + Promise drawPolygonAsync(async_sequence coordinates); +}; + +interface I { + Promise f1(async_sequence<[XAttr] float> arg); +};