Skip to content

Commit 90ba8b1

Browse files
authored
Create exceptions and promises in the correct realm
Closes #242 by superseding it. This approach moves the ctorRegistry back to being mostly for the registration of WebIDL2JS-generated constructors. We use the globalObject variable to pass to webidl-conversions and to find our Promise and TypeError constructors.
1 parent b8f389f commit 90ba8b1

File tree

14 files changed

+2454
-1719
lines changed

14 files changed

+2454
-1719
lines changed

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ Returns a boolean indicating whether _value_ is an instance of the wrapper class
258258

259259
This is useful in other parts of your program that are not implementation class files, but instead receive wrapper classes from client code.
260260

261-
#### `convert(value, { context })`
261+
#### `convert(globalObject, value, { context })`
262262

263263
Performs the Web IDL conversion algorithm for this interface, converting _value_ into the correct representation of the interface type suitable for consumption by implementation classes: the corresponding impl.
264264

@@ -296,13 +296,13 @@ jsdom does this for `Window`, which is written in custom, non-webidl2js-generate
296296

297297
### For callback interfaces
298298

299-
#### `convert(value, { context })`
299+
#### `convert(globalObject, value, { context })`
300300

301-
Performs the Web IDL conversion algorithm for this callback interface, converting _value_ into a function that performs [call a user object's operation](https://heycam.github.io/webidl/#call-a-user-objects-operation) when called, with _thisArg_ being the `this` value of the converted function.
301+
Performs the Web IDL conversion algorithm for this callback interface, converting `value` into a function that performs [call a user object's operation](https://heycam.github.io/webidl/#call-a-user-objects-operation) when called, with _thisArg_ being the `this` value of the converted function. `globalObject` is used to ensure error cases result in `Error` or `Promise` objects from the correct realm.
302302

303-
The resulting function has an _objectReference_ property, which is the same object as _value_ and can be used to perform identity checks, as `convert` returns a new function object every time.
303+
The resulting function has an `objectReference` property, which is the same object as `value` and can be used to perform identity checks, as `convert` returns a new function object every time.
304304

305-
If any part of the conversion fails, _context_ can be used to describe the provided value in any resulting error message.
305+
If any part of the conversion fails, `context` can be used to describe the provided value in any resulting error message.
306306

307307
#### `install(globalObject, globalNames)`
308308

@@ -312,11 +312,11 @@ The second argument `globalNames` is the same as for [the `install()` export for
312312

313313
### For dictionaries
314314

315-
#### `convert(value, { context })`
315+
#### `convert(globalObject, value, { context })`
316316

317-
Performs the Web IDL conversion algorithm for this dictionary, converting _value_ into the correct representation of the dictionary type suitable for consumption by implementation classes: a `null`-[[Prototype]] object with its properties properly converted.
317+
Performs the Web IDL conversion algorithm for this dictionary, converting `value` into the correct representation of the dictionary type suitable for consumption by implementation classes: a `null`-[[Prototype]] object with its properties properly converted. `globalObject` is used to ensure error cases result in `Error` or `Promise` objects from the correct realm.
318318

319-
If any part of the conversion fails, _context_ can be used to describe the provided value in any resulting error message.
319+
If any part of the conversion fails, `context` can be used to describe the provided value in any resulting error message.
320320

321321
### Other requirements
322322

lib/constructs/async-iterable.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class AsyncIterable {
3838

3939
this.interface.addMethod(this.interface.defaultWhence, key, [], `
4040
if (!exports.is(this)) {
41-
throw new TypeError("'${key}' called on an object that is not a valid instance of ${this.interface.name}.");
41+
throw new globalObject.TypeError("'${key}' called on an object that is not a valid instance of ${this.interface.name}.");
4242
}
4343
4444
${conv.body}

lib/constructs/attribute.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ class Attribute {
3535

3636
const async = this.idl.idlType.generic === "Promise";
3737
const promiseHandlingBefore = async ? `try {` : ``;
38-
const promiseHandlingAfter = async ? `} catch (e) { return Promise.reject(e); }` : ``;
38+
const promiseHandlingAfter = async ? `} catch (e) { return globalObject.Promise.reject(e); }` : ``;
3939

4040
let brandCheck = `
4141
if (!exports.is(esValue)) {
42-
throw new TypeError("'$KEYWORD$ ${this.idl.name}' called on an object that is not a valid instance of ${this.interface.name}.");
42+
throw new globalObject.TypeError("'$KEYWORD$ ${this.idl.name}' called on an object that is not a valid instance of ${this.interface.name}.");
4343
}
4444
`;
4545
let getterBody = `return utils.tryWrapperForImpl(esValue[implSymbol]["${this.idl.name}"]);`;
@@ -150,7 +150,7 @@ class Attribute {
150150
setterBody = `
151151
const Q = esValue["${this.idl.name}"];
152152
if (!utils.isObject(Q)) {
153-
throw new TypeError("Property '${this.idl.name}' is not an object");
153+
throw new globalObject.TypeError("Property '${this.idl.name}' is not an object");
154154
}
155155
`;
156156

@@ -180,7 +180,7 @@ class Attribute {
180180
addMethod("toString", [], `
181181
const esValue = this;
182182
if (!exports.is(esValue)) {
183-
throw new TypeError("'toString' called on an object that is not a valid instance of ${this.interface.name}.");
183+
throw new globalObject.TypeError("'toString' called on an object that is not a valid instance of ${this.interface.name}.");
184184
}
185185
186186
${getterBody}

lib/constructs/callback-function.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class CallbackFunction {
2525
"" :
2626
`
2727
if (typeof value !== "function") {
28-
throw new TypeError(context + " is not a function");
28+
throw new globalObject.TypeError(context + " is not a function");
2929
}
3030
`;
3131

@@ -109,7 +109,7 @@ class CallbackFunction {
109109
}
110110

111111
this.str += `
112-
exports.convert = (value, { context = "The provided value" } = {}) => {
112+
exports.convert = (globalObject, value, { context = "The provided value" } = {}) => {
113113
${assertCallable}
114114
function invokeTheCallbackFunction(${inputArgs}) {
115115
const thisArg = utils.tryWrapperForImpl(this);
@@ -144,7 +144,7 @@ class CallbackFunction {
144144
if (isAsync) {
145145
this.str += `
146146
} catch (err) {
147-
return Promise.reject(err);
147+
return globalObject.Promise.reject(err);
148148
}
149149
`;
150150
}

lib/constructs/callback-interface.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,9 @@ class CallbackInterface {
9494
}
9595

9696
this.str += `
97-
exports.convert = function convert(value, { context = "The provided value" } = {}) {
97+
exports.convert = (globalObject, value, { context = "The provided value" } = {}) => {
9898
if (!utils.isObject(value)) {
99-
throw new TypeError(\`\${context} is not an object.\`);
99+
throw new globalObject.TypeError(\`\${context} is not an object.\`);
100100
}
101101
102102
function callTheUserObjectsOperation(${argNames.join(", ")}) {
@@ -115,7 +115,7 @@ class CallbackInterface {
115115
if (typeof O !== "function") {
116116
X = O[${utils.stringifyPropertyName(opName)}];
117117
if (typeof X !== "function") {
118-
throw new TypeError(\`\${context} does not correctly implement ${name}.\`)
118+
throw new globalObject.TypeError(\`\${context} does not correctly implement ${name}.\`)
119119
}
120120
thisArg = O;
121121
}
@@ -151,7 +151,7 @@ class CallbackInterface {
151151
if (isAsync) {
152152
this.str += `
153153
} catch (err) {
154-
return Promise.reject(err);
154+
return globalObject.Promise.reject(err);
155155
}
156156
`;
157157
}
@@ -215,8 +215,9 @@ class CallbackInterface {
215215
return;
216216
}
217217
218+
const ctorRegistry = utils.initCtorRegistry(globalObject);
218219
const ${name} = () => {
219-
throw new TypeError("Illegal invocation");
220+
throw new globalObject.TypeError("Illegal invocation");
220221
};
221222
`;
222223

lib/constructs/dictionary.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class Dictionary {
5555
if (field.required) {
5656
str += `
5757
else {
58-
throw new TypeError("${field.name} is required in '${this.name}'");
58+
throw new globalObject.TypeError("${field.name} is required in '${this.name}'");
5959
}
6060
`;
6161
} else if (field.default) {
@@ -76,26 +76,26 @@ class Dictionary {
7676

7777
generate() {
7878
this.str += `
79-
exports._convertInherit = (obj, ret, { context = "The provided value" } = {}) => {
79+
exports._convertInherit = (globalObject, obj, ret, { context = "The provided value" } = {}) => {
8080
`;
8181

8282
if (this.idl.inheritance) {
8383
this.str += `
84-
${this.idl.inheritance}._convertInherit(obj, ret, { context });
84+
${this.idl.inheritance}._convertInherit(globalObject, obj, ret, { context });
8585
`;
8686
}
8787

8888
this.str += `
8989
${this._generateConversions()}
9090
};
9191
92-
exports.convert = function convert(obj, { context = "The provided value" } = {}) {
92+
exports.convert = (globalObject, obj, { context = "The provided value" } = {}) => {
9393
if (obj !== undefined && typeof obj !== "object" && typeof obj !== "function") {
94-
throw new TypeError(\`\${context} is not an object.\`);
94+
throw new globalObject.TypeError(\`\${context} is not an object.\`);
9595
}
9696
9797
const ret = Object.create(null);
98-
exports._convertInherit(obj, ret, { context });
98+
exports._convertInherit(globalObject, obj, ret, { context });
9999
return ret;
100100
};
101101
`;

lib/constructs/enumeration.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ class Enumeration {
1818
const enumerationValues = new Set(${JSON.stringify([...values])});
1919
exports.enumerationValues = enumerationValues;
2020
21-
exports.convert = function convert(value, { context = "The provided value" } = {}) {
21+
exports.convert = (globalObject, value, { context = "The provided value" } = {}) => {
2222
const string = \`\${value}\`;
2323
if (!enumerationValues.has(string)) {
24-
throw new TypeError(\`\${context} '\${string}' is not a valid enumeration value for ${this.name}\`);
24+
throw new globalObject.TypeError(\`\${context} '\${string}' is not a valid enumeration value for ${this.name}\`);
2525
}
2626
return string;
2727
};

0 commit comments

Comments
 (0)