Skip to content
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

Create exceptions and promises in the correct realm #251

Merged
merged 5 commits into from
Oct 6, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ Returns a boolean indicating whether _value_ is an instance of the wrapper class

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

#### `convert(value, { context })`
#### `convert(globalObject, value, { context })`

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.

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

### For callback interfaces

#### `convert(value, { context })`
#### `convert(globalObject, value, { context })`

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.
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.

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.
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.

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

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

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

### For dictionaries

#### `convert(value, { context })`
#### `convert(globalObject, value, { context })`

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.
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.

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

### Other requirements

Expand Down
2 changes: 1 addition & 1 deletion lib/constructs/async-iterable.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class AsyncIterable {

this.interface.addMethod(this.interface.defaultWhence, key, [], `
if (!exports.is(this)) {
throw new TypeError("'${key}' called on an object that is not a valid instance of ${this.interface.name}.");
throw new globalObject.TypeError("'${key}' called on an object that is not a valid instance of ${this.interface.name}.");
}

${conv.body}
Expand Down
8 changes: 4 additions & 4 deletions lib/constructs/attribute.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ class Attribute {

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

let brandCheck = `
if (!exports.is(esValue)) {
throw new TypeError("'$KEYWORD$ ${this.idl.name}' called on an object that is not a valid instance of ${this.interface.name}.");
throw new globalObject.TypeError("'$KEYWORD$ ${this.idl.name}' called on an object that is not a valid instance of ${this.interface.name}.");
}
`;
let getterBody = `return utils.tryWrapperForImpl(esValue[implSymbol]["${this.idl.name}"]);`;
Expand Down Expand Up @@ -150,7 +150,7 @@ class Attribute {
setterBody = `
const Q = esValue["${this.idl.name}"];
if (!utils.isObject(Q)) {
throw new TypeError("Property '${this.idl.name}' is not an object");
throw new globalObject.TypeError("Property '${this.idl.name}' is not an object");
}
`;

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

${getterBody}
Expand Down
6 changes: 3 additions & 3 deletions lib/constructs/callback-function.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class CallbackFunction {
"" :
`
if (typeof value !== "function") {
throw new TypeError(context + " is not a function");
throw new globalObject.TypeError(context + " is not a function");
}
`;

Expand Down Expand Up @@ -109,7 +109,7 @@ class CallbackFunction {
}

this.str += `
exports.convert = (value, { context = "The provided value" } = {}) => {
exports.convert = (globalObject, value, { context = "The provided value" } = {}) => {
${assertCallable}
function invokeTheCallbackFunction(${inputArgs}) {
const thisArg = utils.tryWrapperForImpl(this);
Expand Down Expand Up @@ -144,7 +144,7 @@ class CallbackFunction {
if (isAsync) {
this.str += `
} catch (err) {
return Promise.reject(err);
return globalObject.Promise.reject(err);
}
`;
}
Expand Down
11 changes: 6 additions & 5 deletions lib/constructs/callback-interface.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ class CallbackInterface {
}

this.str += `
exports.convert = function convert(value, { context = "The provided value" } = {}) {
exports.convert = (globalObject, value, { context = "The provided value" } = {}) => {
if (!utils.isObject(value)) {
throw new TypeError(\`\${context} is not an object.\`);
throw new globalObject.TypeError(\`\${context} is not an object.\`);
}

function callTheUserObjectsOperation(${argNames.join(", ")}) {
Expand All @@ -115,7 +115,7 @@ class CallbackInterface {
if (typeof O !== "function") {
X = O[${utils.stringifyPropertyName(opName)}];
if (typeof X !== "function") {
throw new TypeError(\`\${context} does not correctly implement ${name}.\`)
throw new globalObject.TypeError(\`\${context} does not correctly implement ${name}.\`)
}
thisArg = O;
}
Expand Down Expand Up @@ -151,7 +151,7 @@ class CallbackInterface {
if (isAsync) {
this.str += `
} catch (err) {
return Promise.reject(err);
return globalObject.Promise.reject(err);
}
`;
}
Expand Down Expand Up @@ -215,8 +215,9 @@ class CallbackInterface {
return;
}

const ctorRegistry = utils.initCtorRegistry(globalObject);
const ${name} = () => {
throw new TypeError("Illegal invocation");
throw new globalObject.TypeError("Illegal invocation");
};
`;

Expand Down
12 changes: 6 additions & 6 deletions lib/constructs/dictionary.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class Dictionary {
if (field.required) {
str += `
else {
throw new TypeError("${field.name} is required in '${this.name}'");
throw new globalObject.TypeError("${field.name} is required in '${this.name}'");
}
`;
} else if (field.default) {
Expand All @@ -76,26 +76,26 @@ class Dictionary {

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

if (this.idl.inheritance) {
this.str += `
${this.idl.inheritance}._convertInherit(obj, ret, { context });
${this.idl.inheritance}._convertInherit(globalObject, obj, ret, { context });
`;
}

this.str += `
${this._generateConversions()}
};

exports.convert = function convert(obj, { context = "The provided value" } = {}) {
exports.convert = (globalObject, obj, { context = "The provided value" } = {}) => {
if (obj !== undefined && typeof obj !== "object" && typeof obj !== "function") {
throw new TypeError(\`\${context} is not an object.\`);
throw new globalObject.TypeError(\`\${context} is not an object.\`);
}

const ret = Object.create(null);
exports._convertInherit(obj, ret, { context });
exports._convertInherit(globalObject, obj, ret, { context });
return ret;
};
`;
Expand Down
4 changes: 2 additions & 2 deletions lib/constructs/enumeration.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ class Enumeration {
const enumerationValues = new Set(${JSON.stringify([...values])});
exports.enumerationValues = enumerationValues;

exports.convert = function convert(value, { context = "The provided value" } = {}) {
exports.convert = (globalObject, value, { context = "The provided value" } = {}) => {
const string = \`\${value}\`;
if (!enumerationValues.has(string)) {
throw new TypeError(\`\${context} '\${string}' is not a valid enumeration value for ${this.name}\`);
throw new globalObject.TypeError(\`\${context} '\${string}' is not a valid enumeration value for ${this.name}\`);
}
return string;
};
Expand Down
Loading