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

chore: improve internal webidl functions #9606

Merged
merged 2 commits into from
Mar 1, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion cli/tests/error_009_op_crates_error.js.out
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[WILDCARD]error: Uncaught TypeError: Failed to construct 'Event': 1 argument, but only 0 present.[WILDCARD]
[WILDCARD]error: Uncaught TypeError: Failed to construct 'Event': 1 argument required, but only 0 present.[WILDCARD]
at new Event (deno:op_crates/web/[WILDCARD])
at [WILDCARD]
98 changes: 87 additions & 11 deletions op_crates/web/00_webidl.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
return new ErrorType(
`${opts.prefix ? opts.prefix + ": " : ""}${
opts.context ? opts.context : "Value"
} ${message}.`,
} ${message}`,
);
}

Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -637,22 +637,26 @@
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) {
const defaultValue = member.defaultValue;
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 },
);
}
}
Expand All @@ -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}.`,
);
}

Expand All @@ -694,12 +698,84 @@
};
}

// 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,
requiredArguments,
createDictionaryConverter,
createEnumConverter,
createNullableConverter,
createSequenceConverter,
createInterfaceConverter,
brand,
createBranded,
assertBranded,
illegalConstructor,
};
})(this);
39 changes: 38 additions & 1 deletion op_crates/web/internal.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

/**
Expand All @@ -205,7 +207,7 @@ declare namespace globalThis {
declare interface DictionaryMember {
key: string;
converter: (v: any, opts: ValueConverterOpts) => any;
defaultValue?: boolean;
defaultValue?: any;
required?: boolean;
}

Expand All @@ -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: {
Expand Down