summaryrefslogtreecommitdiff
path: root/op_crates/web
diff options
context:
space:
mode:
authorLuca Casonato <lucacasonato@yahoo.com>2021-03-01 01:25:31 +0100
committerGitHub <noreply@github.com>2021-03-01 01:25:31 +0100
commit72425ec4cebbabe5058bb79ce7eae8807c766cb4 (patch)
tree4d3aeba43edfa039b45aa22232bd44c5bd06077d /op_crates/web
parentded68aba7302488c1e41214766017f9f5a9a2d86 (diff)
chore: improve internal webidl functions (#9606)
Diffstat (limited to 'op_crates/web')
-rw-r--r--op_crates/web/00_webidl.js98
-rw-r--r--op_crates/web/internal.d.ts39
2 files changed, 125 insertions, 12 deletions
diff --git a/op_crates/web/00_webidl.js b/op_crates/web/00_webidl.js
index 8831cb71e..ab3047391 100644
--- a/op_crates/web/00_webidl.js
+++ b/op_crates/web/00_webidl.js
@@ -14,7 +14,7 @@
return new ErrorType(
`${opts.prefix ? opts.prefix + ": " : ""}${
opts.context ? opts.context : "Value"
- } ${message}.`,
+ } ${message}`,
);
}
@@ -602,7 +602,7 @@
opts.prefix ? opts.prefix + ": " : ""
}${required} argument${
required === 1 ? "" : "s"
- }, but only ${length} present.`;
+ } required, but only ${length} present.`;
throw new TypeError(errMsg);
}
}
@@ -637,13 +637,15 @@
esMemberValue = esDict[key];
}
+ const context = `'${key}' of '${name}'${
+ opts.context ? ` (${opts.context})` : ""
+ }`;
+
if (esMemberValue !== undefined) {
const converter = member.converter;
const idlMemberValue = converter(esMemberValue, {
...opts,
- context: `${key} of '${name}'${
- opts.context ? `(${opts.context})` : ""
- }`,
+ context,
});
idlDict[key] = idlMemberValue;
} else if ("defaultValue" in member) {
@@ -651,8 +653,10 @@
const idlMemberValue = defaultValue;
idlDict[key] = idlMemberValue;
} else if (member.required) {
- throw new TypeError(
- `can not be converted to '${name}' because ${key} is required in '${name}'.`,
+ throw makeException(
+ TypeError,
+ `can not be converted to '${name}' because '${key}' is required in '${name}'.`,
+ { ...opts },
);
}
}
@@ -670,10 +674,10 @@
const S = String(V);
if (!E.has(S)) {
- throw makeException(
- TypeError,
- `The provided value '${V}' is not a valid enum value of type ${name}.`,
- opts,
+ throw new TypeError(
+ `${
+ opts.prefix ? opts.prefix + ": " : ""
+ }The provided value '${S}' is not a valid enum value of type ${name}.`,
);
}
@@ -694,6 +698,72 @@
};
}
+ // https://heycam.github.io/webidl/#es-sequence
+ function createSequenceConverter(converter) {
+ return function (V, opts = {}) {
+ if (typeof V !== "object") {
+ throw makeException(
+ TypeError,
+ "can not be converted to sequence.",
+ opts,
+ );
+ }
+ const iter = V?.[Symbol.iterator]?.();
+ if (iter === undefined) {
+ throw makeException(
+ TypeError,
+ "can not be converted to sequence.",
+ opts,
+ );
+ }
+ const array = [];
+ while (true) {
+ const res = iter?.next?.();
+ if (res === undefined) {
+ throw makeException(
+ TypeError,
+ "can not be converted to sequence.",
+ opts,
+ );
+ }
+ if (res.done === true) break;
+ const val = converter(res.value, {
+ ...opts,
+ context: `${opts.context}, index ${array.length}`,
+ });
+ array.push(val);
+ }
+ return array;
+ };
+ }
+
+ const brand = Symbol("[[webidl.brand]]");
+
+ function createInterfaceConverter(name, prototype) {
+ return (V, opts) => {
+ if (!(V instanceof prototype) || V[brand] !== brand) {
+ throw makeException(TypeError, `is not of type ${name}.`, opts);
+ }
+ return V;
+ };
+ }
+
+ function createBranded(Type) {
+ const t = Object.create(Type.prototype);
+ t[brand] = brand;
+ return t;
+ }
+
+ function assertBranded(self, prototype) {
+ if (!(self instanceof prototype) || self[brand] !== brand) {
+ throw new TypeError("Illegal invocation");
+ }
+ }
+
+ function illegalConstructor() {
+ throw new TypeError("Illegal constructor");
+ }
+
window.__bootstrap ??= {};
window.__bootstrap.webidl = {
converters,
@@ -701,5 +771,11 @@
createDictionaryConverter,
createEnumConverter,
createNullableConverter,
+ createSequenceConverter,
+ createInterfaceConverter,
+ brand,
+ createBranded,
+ assertBranded,
+ illegalConstructor,
};
})(this);
diff --git a/op_crates/web/internal.d.ts b/op_crates/web/internal.d.ts
index 04fc061bb..d43332e4b 100644
--- a/op_crates/web/internal.d.ts
+++ b/op_crates/web/internal.d.ts
@@ -191,6 +191,8 @@ declare namespace globalThis {
* Convert a value into a `VoidFunction` (() => void).
*/
VoidFunction(v: any, opts?: ValueConverterOpts): () => void;
+
+ [type: string]: (v: any, opts: ValueConverterOpts) => any;
};
/**
@@ -205,7 +207,7 @@ declare namespace globalThis {
declare interface DictionaryMember {
key: string;
converter: (v: any, opts: ValueConverterOpts) => any;
- defaultValue?: boolean;
+ defaultValue?: any;
required?: boolean;
}
@@ -231,6 +233,41 @@ declare namespace globalThis {
declare function createNullableConverter<T>(
converter: (v: any, opts: ValueConverterOpts) => T,
): (v: any, opts: ValueConverterOpts) => T | null;
+
+ /**
+ * Create a converter that converts a sequence of the inner type.
+ */
+ declare function createSequenceConverter<T>(
+ converter: (v: any, opts: ValueConverterOpts) => T,
+ ): (v: any, opts: ValueConverterOpts) => T[];
+
+ /**
+ * Throw an illegal constructor error.
+ */
+ declare function illegalConstructor(): never;
+
+ /**
+ * The branding symbol.
+ */
+ declare const brand: unique symbol;
+
+ /**
+ * Create a branded instance of an interface.
+ */
+ declare function createBranded(self: any): any;
+
+ /**
+ * Assert that self is branded.
+ */
+ declare function assertBranded(self: any, type: any): void;
+
+ /**
+ * Create a converter for interfaces.
+ */
+ declare function createInterfaceConverter(
+ name: string,
+ prototype: any,
+ ): (v: any, opts: ValueConverterOpts) => any;
}
declare var url: {