diff --git a/lib/constructs/attribute.js b/lib/constructs/attribute.js index ea03c90c..fb062b66 100644 --- a/lib/constructs/attribute.js +++ b/lib/constructs/attribute.js @@ -73,7 +73,7 @@ class Attribute { ${brandCheck} ${getterBody} ${promiseHandlingAfter} - `, { type: "get" }, { configurable }); + `, "get", { configurable }); if (!this.idl.readonly) { if (async) { @@ -103,13 +103,13 @@ class Attribute { ${brandCheck} ${idlConversion} ${setterBody} - `, { type: "set" }, { configurable }); + `, "set", { configurable }); } else if (utils.getExtAttr(this.idl.extAttrs, "PutForwards")) { addMethod(this.idl.name, ["V"], ` const esValue = this !== null && this !== undefined ? this : globalObject; ${brandCheck} this.${this.idl.name}.${utils.getExtAttr(this.idl.extAttrs, "PutForwards").rhs.value} = V; - `, { type: "set" }, { configurable }); + `, "set", { configurable }); } else if (utils.getExtAttr(this.idl.extAttrs, "Replaceable")) { addMethod(this.idl.name, ["V"], ` const esValue = this !== null && this !== undefined ? this : globalObject; @@ -120,7 +120,7 @@ class Attribute { value: V, writable: true }); - `, { type: "set" }, { configurable }); + `, "set", { configurable }); } if (!this.static && this.idl.special === "stringifier") { @@ -131,7 +131,7 @@ class Attribute { } ${getterBody} - `, { type: "regular" }, { configurable, writable: configurable }); + `, "regular", { configurable, writable: configurable }); } return { requires }; diff --git a/lib/constructs/interface.js b/lib/constructs/interface.js index 9e72abd8..beeedc57 100644 --- a/lib/constructs/interface.js +++ b/lib/constructs/interface.js @@ -123,7 +123,7 @@ class Interface { // whence is either "instance" or "prototype" // type is either "regular", "get", or "set" - addMethod(whence, propName, args, body, { type = "regular", async = false } = {}, { + addMethod(whence, propName, args, body, type = "regular", { configurable = true, enumerable = typeof propName === "string", writable = type === "regular" ? true : undefined @@ -150,11 +150,11 @@ class Interface { } const descriptor = { configurable, enumerable, writable }; - this._outputMethods.set(propName, { whence, type, args, body, descriptor, async }); + this._outputMethods.set(propName, { whence, type, args, body, descriptor }); } // type is either "regular", "get", or "set" - addStaticMethod(propName, args, body, { type = "regular", async = false } = {}, { + addStaticMethod(propName, args, body, type = "regular", { configurable = true, enumerable = typeof propName === "string", writable = type === "regular" ? true : undefined @@ -178,7 +178,7 @@ class Interface { } const descriptor = { configurable, enumerable, writable }; - this._outputStaticMethods.set(propName, { type, args, body, descriptor, async }); + this._outputStaticMethods.set(propName, { type, args, body, descriptor }); } // whence is either "instance" or "prototype" @@ -1317,40 +1317,40 @@ class Interface { } generateOffInstanceMethods() { - const addOne = (name, args, body, { isAsync, isStatic }) => { + const addOne = (name, args, body) => { this.str += ` - ${isStatic ? "static " : ""}${isAsync ? "async " : ""}${name}(${formatArgs(args)}) {${body}} + ${name}(${formatArgs(args)}) {${body}} `; }; - for (const [name, { whence, type, args, body, async }] of this._outputMethods) { + for (const [name, { whence, type, args, body }] of this._outputMethods) { if (whence !== "prototype") { continue; } const propName = utils.stringifyPropertyKey(name); if (type === "regular") { - addOne(propName, args, body, { isAsync: async }); + addOne(propName, args, body); } else { if (body[0] !== undefined) { - addOne(`get ${propName}`, [], body[0], { isAsync: async }); + addOne(`get ${propName}`, [], body[0]); } if (body[1] !== undefined) { - addOne(`set ${propName}`, args, body[1], { isAsync: async }); + addOne(`set ${propName}`, args, body[1]); } } } - for (const [name, { type, args, body, async }] of this._outputStaticMethods) { + for (const [name, { type, args, body }] of this._outputStaticMethods) { const propName = utils.stringifyPropertyKey(name); if (type === "regular") { - addOne(`${propName}`, args, body, { isAsync: async, isStatic: true }); + addOne(`static ${propName}`, args, body); } else { if (body[0] !== undefined) { - addOne(`get ${propName}`, [], body[0], { isAsync: async, isStatic: true }); + addOne(`static get ${propName}`, [], body[0]); } if (body[1] !== undefined) { - addOne(`set ${propName}`, args, body[0], { isAsync: async, isStatic: true }); + addOne(`static set ${propName}`, args, body[0]); } } } diff --git a/lib/constructs/operation.js b/lib/constructs/operation.js index fd551956..8c3cc669 100644 --- a/lib/constructs/operation.js +++ b/lib/constructs/operation.js @@ -26,12 +26,14 @@ class Operation { } isAsync() { + // As of the time of this writing, the current spec does not disallow such overloads, but the intention is to do so: + // https://github.com/heycam/webidl/pull/776 const firstAsync = this.idls[0].idlType.generic === "Promise"; for (const overload of this.idls.slice(1)) { const isAsync = overload.idlType.generic === "Promise"; if (isAsync !== firstAsync) { throw new Error( - `Overloading between Promise and non-Promise return types is not yet implemented: operation ` + + `Overloading between Promise and non-Promise return types is not allowed: operation ` + `"${this.name}" on ${this.interface.name}` ); } @@ -63,6 +65,8 @@ class Operation { const onInstance = this.isOnInstance(); const async = this.isAsync(); + const promiseHandlingBefore = async ? `try {` : ``; + const promiseHandlingAfter = async ? `} catch (e) { return Promise.reject(e); }` : ``; const type = this.static ? "static operation" : "regular operation"; const overloads = Overloads.getEffectiveOverloads(type, this.name, 0, this.interface); @@ -113,8 +117,10 @@ class Operation { } str += invocation; + str = promiseHandlingBefore + str + promiseHandlingAfter; + if (this.static) { - this.interface.addStaticMethod(this.name, argNames, str, { async }); + this.interface.addStaticMethod(this.name, argNames, str); } else { const forgeable = !utils.getExtAttr(this.idls[0].extAttrs, "Unforgeable"); this.interface.addMethod( @@ -122,7 +128,7 @@ class Operation { this.name, argNames, str, - { type: "regular", async }, + "regular", { configurable: forgeable, writable: forgeable } ); } diff --git a/test/__snapshots__/test.js.snap b/test/__snapshots__/test.js.snap index b900b854..98321a34 100644 --- a/test/__snapshots__/test.js.snap +++ b/test/__snapshots__/test.js.snap @@ -446,17 +446,21 @@ exports.install = (globalObject, globalNames) => { } } - async promiseOperation() { - const esValue = this !== null && this !== undefined ? this : globalObject; - if (!exports.is(esValue)) { - throw new TypeError(\\"Illegal invocation\\"); - } - - CEReactions.preSteps(globalObject); + promiseOperation() { try { - return utils.tryWrapperForImpl(esValue[implSymbol].promiseOperation()); - } finally { - CEReactions.postSteps(globalObject); + const esValue = this !== null && this !== undefined ? this : globalObject; + if (!exports.is(esValue)) { + throw new TypeError(\\"Illegal invocation\\"); + } + + CEReactions.preSteps(globalObject); + try { + return utils.tryWrapperForImpl(esValue[implSymbol].promiseOperation()); + } finally { + CEReactions.postSteps(globalObject); + } + } catch (e) { + return Promise.reject(e); } } @@ -2542,6 +2546,22 @@ exports.install = (globalObject, globalNames) => { args.push(curArg); } break; + case 2: + { + let curArg = arguments[0]; + curArg = conversions[\\"DOMString\\"](curArg, { + context: \\"Failed to execute 'compatible' on 'Overloads': parameter 1\\" + }); + args.push(curArg); + } + { + let curArg = arguments[1]; + curArg = conversions[\\"DOMString\\"](curArg, { + context: \\"Failed to execute 'compatible' on 'Overloads': parameter 2\\" + }); + args.push(curArg); + } + break; default: { let curArg = arguments[0]; @@ -2557,8 +2577,19 @@ exports.install = (globalObject, globalNames) => { }); args.push(curArg); } + { + let curArg = arguments[2]; + if (curArg !== undefined) { + curArg = conversions[\\"long\\"](curArg, { + context: \\"Failed to execute 'compatible' on 'Overloads': parameter 3\\" + }); + } else { + curArg = 0; + } + args.push(curArg); + } } - return esValue[implSymbol].compatible(...args); + return utils.tryWrapperForImpl(esValue[implSymbol].compatible(...args)); } incompatible1(arg1) { @@ -2851,12 +2882,16 @@ exports._internalSetup = (wrapper, globalObject) => { wrapper, Object.getOwnPropertyDescriptors({ unforgeablePromiseOperation() { - const esValue = this !== null && this !== undefined ? this : globalObject; - if (!exports.is(esValue)) { - throw new TypeError(\\"Illegal invocation\\"); - } + try { + const esValue = this !== null && this !== undefined ? this : globalObject; + if (!exports.is(esValue)) { + throw new TypeError(\\"Illegal invocation\\"); + } - return utils.tryWrapperForImpl(esValue[implSymbol].unforgeablePromiseOperation()); + return utils.tryWrapperForImpl(esValue[implSymbol].unforgeablePromiseOperation()); + } catch (e) { + return Promise.reject(e); + } }, get unforgeablePromiseAttribute() { try { @@ -2979,13 +3014,17 @@ exports.install = (globalObject, globalNames) => { return esValue[implSymbol].promiseConsumer(...args); } - async promiseOperation() { - const esValue = this !== null && this !== undefined ? this : globalObject; - if (!exports.is(esValue)) { - throw new TypeError(\\"Illegal invocation\\"); - } + promiseOperation() { + try { + const esValue = this !== null && this !== undefined ? this : globalObject; + if (!exports.is(esValue)) { + throw new TypeError(\\"Illegal invocation\\"); + } - return utils.tryWrapperForImpl(esValue[implSymbol].promiseOperation()); + return utils.tryWrapperForImpl(esValue[implSymbol].promiseOperation()); + } catch (e) { + return Promise.reject(e); + } } get promiseAttribute() { @@ -3002,8 +3041,12 @@ exports.install = (globalObject, globalNames) => { } } - static async staticPromiseOperation() { - return utils.tryWrapperForImpl(Impl.implementation.staticPromiseOperation()); + static staticPromiseOperation() { + try { + return utils.tryWrapperForImpl(Impl.implementation.staticPromiseOperation()); + } catch (e) { + return Promise.reject(e); + } } static get staticPromiseAttribute() { @@ -8911,13 +8954,17 @@ exports.install = (globalObject, globalNames) => { return esValue[implSymbol].method(); } - async promiseOperation() { - const esValue = this !== null && this !== undefined ? this : globalObject; - if (!exports.is(esValue)) { - throw new TypeError(\\"Illegal invocation\\"); - } + promiseOperation() { + try { + const esValue = this !== null && this !== undefined ? this : globalObject; + if (!exports.is(esValue)) { + throw new TypeError(\\"Illegal invocation\\"); + } - return utils.tryWrapperForImpl(esValue[implSymbol].promiseOperation()); + return utils.tryWrapperForImpl(esValue[implSymbol].promiseOperation()); + } catch (e) { + return Promise.reject(e); + } } get attr() { @@ -10971,6 +11018,22 @@ exports.install = (globalObject, globalNames) => { args.push(curArg); } break; + case 2: + { + let curArg = arguments[0]; + curArg = conversions[\\"DOMString\\"](curArg, { + context: \\"Failed to execute 'compatible' on 'Overloads': parameter 1\\" + }); + args.push(curArg); + } + { + let curArg = arguments[1]; + curArg = conversions[\\"DOMString\\"](curArg, { + context: \\"Failed to execute 'compatible' on 'Overloads': parameter 2\\" + }); + args.push(curArg); + } + break; default: { let curArg = arguments[0]; @@ -10986,8 +11049,19 @@ exports.install = (globalObject, globalNames) => { }); args.push(curArg); } + { + let curArg = arguments[2]; + if (curArg !== undefined) { + curArg = conversions[\\"long\\"](curArg, { + context: \\"Failed to execute 'compatible' on 'Overloads': parameter 3\\" + }); + } else { + curArg = 0; + } + args.push(curArg); + } } - return esValue[implSymbol].compatible(...args); + return utils.tryWrapperForImpl(esValue[implSymbol].compatible(...args)); } incompatible1(arg1) { @@ -11280,12 +11354,16 @@ exports._internalSetup = (wrapper, globalObject) => { wrapper, Object.getOwnPropertyDescriptors({ unforgeablePromiseOperation() { - const esValue = this !== null && this !== undefined ? this : globalObject; - if (!exports.is(esValue)) { - throw new TypeError(\\"Illegal invocation\\"); - } + try { + const esValue = this !== null && this !== undefined ? this : globalObject; + if (!exports.is(esValue)) { + throw new TypeError(\\"Illegal invocation\\"); + } - return utils.tryWrapperForImpl(esValue[implSymbol].unforgeablePromiseOperation()); + return utils.tryWrapperForImpl(esValue[implSymbol].unforgeablePromiseOperation()); + } catch (e) { + return Promise.reject(e); + } }, get unforgeablePromiseAttribute() { try { @@ -11408,13 +11486,17 @@ exports.install = (globalObject, globalNames) => { return esValue[implSymbol].promiseConsumer(...args); } - async promiseOperation() { - const esValue = this !== null && this !== undefined ? this : globalObject; - if (!exports.is(esValue)) { - throw new TypeError(\\"Illegal invocation\\"); - } + promiseOperation() { + try { + const esValue = this !== null && this !== undefined ? this : globalObject; + if (!exports.is(esValue)) { + throw new TypeError(\\"Illegal invocation\\"); + } - return utils.tryWrapperForImpl(esValue[implSymbol].promiseOperation()); + return utils.tryWrapperForImpl(esValue[implSymbol].promiseOperation()); + } catch (e) { + return Promise.reject(e); + } } get promiseAttribute() { @@ -11431,8 +11513,12 @@ exports.install = (globalObject, globalNames) => { } } - static async staticPromiseOperation() { - return utils.tryWrapperForImpl(Impl.implementation.staticPromiseOperation()); + static staticPromiseOperation() { + try { + return utils.tryWrapperForImpl(Impl.implementation.staticPromiseOperation()); + } catch (e) { + return Promise.reject(e); + } } static get staticPromiseAttribute() { diff --git a/test/cases/Overloads.webidl b/test/cases/Overloads.webidl index 00191002..175789ee 100644 --- a/test/cases/Overloads.webidl +++ b/test/cases/Overloads.webidl @@ -6,9 +6,7 @@ interface Overloads { DOMString compatible(DOMString arg1); byte compatible(DOMString arg1, DOMString arg2); - - // Not yet implemented: - // Promise compatible(DOMString arg1, DOMString arg2, optional long arg3 = 0); + URL compatible(DOMString arg1, DOMString arg2, optional long arg3 = 0); DOMString incompatible1(DOMString arg1); byte incompatible1(long arg1);