diff --git a/testing/web-platform/tests/resources/idlharness.js b/testing/web-platform/tests/resources/idlharness.js index e7a3abbec447..f68ad1dd7d81 100644 --- a/testing/web-platform/tests/resources/idlharness.js +++ b/testing/web-platform/tests/resources/idlharness.js @@ -1507,6 +1507,17 @@ IdlInterface.prototype.test = function() this.test_members(); }; +// This supports both Constructor extended attributes and constructor +// operations until all idl fragments have been updated. +IdlInterface.prototype.constructors = function() +{ + var extendedAttributes = this.extAttrs + .filter(function(attr) { return attr.name == "Constructor"; }); + var operations = this.members + .filter(function(m) { return m.type == "constructor"; }); + return extendedAttributes.concat(operations); +} + IdlInterface.prototype.test_self = function() { subsetTestByKey(this.name, test, function() @@ -1594,7 +1605,7 @@ IdlInterface.prototype.test_self = function() "prototype of self's property " + format_value(this.name) + " is not Function.prototype"); } - if (!this.has_extended_attribute("Constructor")) { + if (!this.constructors().length) { // "The internal [[Call]] method of the interface object behaves as // follows . . . // @@ -1629,8 +1640,7 @@ IdlInterface.prototype.test_self = function() assert_false(desc.enumerable, this.name + ".length should not be enumerable"); assert_true(desc.configurable, this.name + ".length should be configurable"); - var constructors = this.extAttrs - .filter(function(attr) { return attr.name == "Constructor"; }); + var constructors = this.constructors(); var expected_length = minOverloadLength(constructors); assert_equals(this.get_interface_object().length, expected_length, "wrong value for " + this.name + ".length"); }.bind(this), this.name + " interface object length"); diff --git a/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/constructors.html b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/constructors.html new file mode 100644 index 000000000000..93cc7f42cc55 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/constructors.html @@ -0,0 +1,25 @@ + +IdlInterface.prototype.constructors() +
+ + + + + + diff --git a/testing/web-platform/tests/resources/webidl2/lib/webidl2.js b/testing/web-platform/tests/resources/webidl2/lib/webidl2.js index 900694b095df..98216ab750de 100644 --- a/testing/web-platform/tests/resources/webidl2/lib/webidl2.js +++ b/testing/web-platform/tests/resources/webidl2/lib/webidl2.js @@ -103,10 +103,10 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _lib_webidl2_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "parse", function() { return _lib_webidl2_js__WEBPACK_IMPORTED_MODULE_0__["parse"]; }); -/* harmony import */ var _lib_writer_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(29); +/* harmony import */ var _lib_writer_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(30); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "write", function() { return _lib_writer_js__WEBPACK_IMPORTED_MODULE_1__["write"]; }); -/* harmony import */ var _lib_validator_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(30); +/* harmony import */ var _lib_validator_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(31); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "validate", function() { return _lib_validator_js__WEBPACK_IMPORTED_MODULE_2__["validate"]; }); @@ -128,10 +128,10 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _productions_typedef_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(17); /* harmony import */ var _productions_callback_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(18); /* harmony import */ var _productions_interface_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(19); -/* harmony import */ var _productions_mixin_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(24); -/* harmony import */ var _productions_dictionary_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(25); -/* harmony import */ var _productions_namespace_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(27); -/* harmony import */ var _productions_callback_interface_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(28); +/* harmony import */ var _productions_mixin_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(25); +/* harmony import */ var _productions_dictionary_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(26); +/* harmony import */ var _productions_namespace_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(28); +/* harmony import */ var _productions_callback_interface_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(29); @@ -299,6 +299,7 @@ const nonRegexTerminals = [ "async", "boolean", "byte", + "constructor", "double", "false", "float", @@ -501,12 +502,13 @@ function lastLine(text) { /** * @typedef {object} WebIDL2ErrorOptions * @property {"error" | "warning"} level + * @property {Function} autofix * * @param {string} message error message * @param {"Syntax" | "Validation"} kind error type * @param {WebIDL2ErrorOptions} [options] */ -function error(source, position, current, message, kind, { level = "error" } = {}) { +function error(source, position, current, message, kind, { level = "error", autofix } = {}) { /** * @param {number} count */ @@ -556,6 +558,7 @@ function error(source, position, current, message, kind, { level = "error" } = { line, sourceName: source.name, level, + autofix, input: subsequentText, tokens: subsequentTokens }; @@ -664,12 +667,15 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "type_with_extended_attributes", function() { return type_with_extended_attributes; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "return_type", function() { return return_type; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "stringifier", function() { return stringifier; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "autofixAddExposedWindow", function() { return autofixAddExposedWindow; }); /* harmony import */ var _type_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6); /* harmony import */ var _argument_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9); /* harmony import */ var _token_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(13); /* harmony import */ var _extended_attributes_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(11); /* harmony import */ var _operation_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(14); /* harmony import */ var _attribute_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(15); +/* harmony import */ var _tokeniser_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(2); + @@ -838,6 +844,29 @@ function stringifier(tokeniser) { return member; } +/** + * @param {object} def + * @param {import("./extended-attributes.js").ExtendedAttributes} def.extAttrs + */ +function autofixAddExposedWindow(def) { + return () => { + if (def.extAttrs.length){ + const tokeniser = new _tokeniser_js__WEBPACK_IMPORTED_MODULE_6__["Tokeniser"]("Exposed=Window,"); + const exposed = _extended_attributes_js__WEBPACK_IMPORTED_MODULE_3__["SimpleExtendedAttribute"].parse(tokeniser); + exposed.tokens.separator = tokeniser.consume(","); + const existing = def.extAttrs[0]; + if (!/^\s/.test(existing.tokens.name.trivia)) { + existing.tokens.name.trivia = ` ${existing.tokens.name.trivia}`; + } + def.extAttrs.unshift(exposed); + } else { + def.extAttrs = _extended_attributes_js__WEBPACK_IMPORTED_MODULE_3__["ExtendedAttributes"].parse(new _tokeniser_js__WEBPACK_IMPORTED_MODULE_6__["Tokeniser"]("[Exposed=Window]")); + def.extAttrs.tokens.open.trivia = def.tokens.base.trivia; + def.tokens.base.trivia = " "; + } + }; +} + /***/ }), /* 6 */ @@ -1065,25 +1094,15 @@ class Base { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "dictionaryWithinUnion", function() { return dictionaryWithinUnion; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "idlTypeIncludesDictionary", function() { return idlTypeIncludesDictionary; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "referencesTypedef", function() { return referencesTypedef; }); -/** - * Yields direct references to dictionary within union. - */ -function* dictionaryWithinUnion(subtypes, defs) { - for (const subtype of subtypes) { - const def = defs.unique.get(subtype.idlType); - if (def && def.type === "dictionary") { - yield subtype; - } - } -} - /** + * @param {*} idlType + * @param {*[]} defs + * @param {object} [options] + * @param {boolean} [options.useNullableInner] use when the input idlType is nullable and you want to use its inner type * @return the type reference that ultimately includes dictionary. */ -function idlTypeIncludesDictionary(idlType, defs) { +function idlTypeIncludesDictionary(idlType, defs, { useNullableInner } = {}) { if (!idlType.union) { const def = defs.unique.get(idlType.idlType); if (!def) { @@ -1103,7 +1122,7 @@ function idlTypeIncludesDictionary(idlType, defs) { return idlType; } } - if (def.type === "dictionary") { + if (def.type === "dictionary" && (useNullableInner || !idlType.nullable)) { return idlType; } } @@ -1118,14 +1137,6 @@ function idlTypeIncludesDictionary(idlType, defs) { } } -/** - * @return true if the idlType directly references a typedef. - */ -function referencesTypedef(idlType, defs) { - const result = defs.unique.get(idlType.idlType); - return result && result.type === "typedef"; -} - /***/ }), /* 9 */ @@ -1189,19 +1200,29 @@ class Argument extends _base_js__WEBPACK_IMPORTED_MODULE_0__["Base"] { *validate(defs) { yield* this.idlType.validate(defs); - if (Object(_validators_helpers_js__WEBPACK_IMPORTED_MODULE_6__["idlTypeIncludesDictionary"])(this.idlType, defs)) { - if (this.optional && !this.default) { - const message = `Optional dictionary arguments must have a default value of \`{}\`.`; - yield Object(_error_js__WEBPACK_IMPORTED_MODULE_5__["validationError"])(this.source, this.tokens.name, this, message); - } + if (Object(_validators_helpers_js__WEBPACK_IMPORTED_MODULE_6__["idlTypeIncludesDictionary"])(this.idlType, defs, { useNullableInner: true })) { if (this.idlType.nullable) { const message = `Dictionary arguments cannot be nullable.`; yield Object(_error_js__WEBPACK_IMPORTED_MODULE_5__["validationError"])(this.source, this.tokens.name, this, message); + } else if (this.optional && !this.default) { + const message = `Optional dictionary arguments must have a default value of \`{}\`.`; + yield Object(_error_js__WEBPACK_IMPORTED_MODULE_5__["validationError"])(this.source, this.tokens.name, this, message, { + autofix: autofixOptionalDictionaryDefaultValue(this) + }); } } } } +/** + * @param {Argument} arg + */ +function autofixOptionalDictionaryDefaultValue(arg) { + return () => { + arg.default = _default_js__WEBPACK_IMPORTED_MODULE_1__["Default"].parse(new _tokeniser_js__WEBPACK_IMPORTED_MODULE_4__["Tokeniser"](" = {}")); + }; +} + /***/ }), /* 10 */ @@ -1259,6 +1280,7 @@ class Default extends _base_js__WEBPACK_IMPORTED_MODULE_0__["Base"] { "use strict"; __webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SimpleExtendedAttribute", function() { return SimpleExtendedAttribute; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ExtendedAttributes", function() { return ExtendedAttributes; }); /* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7); /* harmony import */ var _array_base_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); @@ -1715,6 +1737,8 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(5); /* harmony import */ var _error_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(3); /* harmony import */ var _validators_interface_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(23); +/* harmony import */ var _constructor_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(24); + @@ -1747,6 +1771,7 @@ class Interface extends _container_js__WEBPACK_IMPORTED_MODULE_0__["Container"] inheritable: !partial, allowedMembers: [ [_constant_js__WEBPACK_IMPORTED_MODULE_3__["Constant"].parse], + [_constructor_js__WEBPACK_IMPORTED_MODULE_8__["Constructor"].parse], [static_member], [_helpers_js__WEBPACK_IMPORTED_MODULE_5__["stringifier"]], [_iterable_js__WEBPACK_IMPORTED_MODULE_4__["IterableLike"].parse], @@ -1772,7 +1797,9 @@ To fix, add, for example, \`[Exposed=Window]\`. Please also consider carefully \ if your interface should also be exposed in a Worker scope. Refer to the \ [WebIDL spec section on Exposed](https://heycam.github.io/webidl/#Exposed) \ for more information.`; - yield Object(_error_js__WEBPACK_IMPORTED_MODULE_6__["validationError"])(this.source, this.tokens.name, this, message); + yield Object(_error_js__WEBPACK_IMPORTED_MODULE_6__["validationError"])(this.source, this.tokens.name, this, message, { + autofix: Object(_helpers_js__WEBPACK_IMPORTED_MODULE_5__["autofixAddExposedWindow"])(this) + }); } yield* super.validate(defs); @@ -2033,6 +2060,43 @@ function* checkInterfaceMemberDuplication(defs, i) { /* 24 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Constructor", function() { return Constructor; }); +/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7); +/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); + + + +class Constructor extends _base_js__WEBPACK_IMPORTED_MODULE_0__["Base"] { + /** + * @param {import("../tokeniser").Tokeniser} tokeniser + */ + static parse(tokeniser) { + const base = tokeniser.consume("constructor"); + if (!base) { + return; + } + const tokens = { base }; + tokens.open = tokeniser.consume("(") || tokeniser.error("No argument list in constructor"); + const args = Object(_helpers_js__WEBPACK_IMPORTED_MODULE_1__["argument_list"])(tokeniser); + tokens.close = tokeniser.consume(")") || tokeniser.error("Unterminated constructor"); + tokens.termination = tokeniser.consume(";") || tokeniser.error("No semicolon after constructor"); + const ret = new Constructor({ tokens }); + ret.arguments = args; + return ret; + } + + get type() { + return "constructor"; + } +} + + +/***/ }), +/* 25 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Mixin", function() { return Mixin; }); @@ -2075,14 +2139,14 @@ class Mixin extends _container_js__WEBPACK_IMPORTED_MODULE_0__["Container"] { /***/ }), -/* 25 */ +/* 26 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Dictionary", function() { return Dictionary; }); /* harmony import */ var _container_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(20); -/* harmony import */ var _field_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(26); +/* harmony import */ var _field_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(27); @@ -2112,7 +2176,7 @@ class Dictionary extends _container_js__WEBPACK_IMPORTED_MODULE_0__["Container"] /***/ }), -/* 26 */ +/* 27 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -2161,7 +2225,7 @@ class Field extends _base_js__WEBPACK_IMPORTED_MODULE_0__["Base"] { /***/ }), -/* 27 */ +/* 28 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -2171,6 +2235,8 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _attribute_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(15); /* harmony import */ var _operation_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(14); /* harmony import */ var _error_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(3); +/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(5); + @@ -2206,7 +2272,9 @@ To fix, add, for example, [Exposed=Window]. Please also consider carefully \ if your namespace should also be exposed in a Worker scope. Refer to the \ [WebIDL spec section on Exposed](https://heycam.github.io/webidl/#Exposed) \ for more information.`; - yield Object(_error_js__WEBPACK_IMPORTED_MODULE_3__["validationError"])(this.source, this.tokens.name, this, message); + yield Object(_error_js__WEBPACK_IMPORTED_MODULE_3__["validationError"])(this.source, this.tokens.name, this, message, { + autofix: Object(_helpers_js__WEBPACK_IMPORTED_MODULE_4__["autofixAddExposedWindow"])(this) + }); } yield* super.validate(defs); } @@ -2214,7 +2282,7 @@ for more information.`; /***/ }), -/* 28 */ +/* 29 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -2255,7 +2323,7 @@ class CallbackInterface extends _container_js__WEBPACK_IMPORTED_MODULE_0__["Cont /***/ }), -/* 29 */ +/* 30 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -2416,6 +2484,17 @@ function write(ast, { templates: ts = templates } = {}) { ]), { data: it, parent }); } + function constructor(it, parent) { + return ts.definition(ts.wrap([ + extended_attributes(it.extAttrs), + token(it.tokens.base), + token(it.tokens.open), + ts.wrap(it.arguments.map(argument)), + token(it.tokens.close), + token(it.tokens.termination) + ]), { data: it, parent }); + } + function inheritance(inh) { if (!inh.tokens.inheritance) { return ""; @@ -2538,6 +2617,7 @@ function write(ast, { templates: ts = templates } = {}) { namespace: container, operation, attribute, + constructor, dictionary: container, field, const: const_, @@ -2570,7 +2650,7 @@ function write(ast, { templates: ts = templates } = {}) { /***/ }), -/* 30 */ +/* 31 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict";