Socket
Socket
Sign inDemoInstall

webidl2js

Package Overview
Dependencies
Maintainers
2
Versions
57
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

webidl2js - npm Package Compare versions

Comparing version 8.0.0 to 9.0.0

.npmignore

4

lib/constructs/constant.js

@@ -17,7 +17,7 @@ "use strict";

Object.defineProperty(${this.interface.name}, "${this.idl.name}", {
value: ${JSON.stringify(this.idl.value.value)},
value: ${utils.getDefault(this.idl.value)},
enumerable: true
});
Object.defineProperty(${this.interface.name}.prototype, "${this.idl.name}", {
value: ${JSON.stringify(this.idl.value.value)},
value: ${utils.getDefault(this.idl.value)},
enumerable: true

@@ -24,0 +24,0 @@ });

@@ -12,3 +12,3 @@ "use strict";

generate() {
const values = new Set(this.idl.values);
const values = new Set(this.idl.values.map(val => val.value));
if (values.size !== this.idl.values.length) {

@@ -15,0 +15,0 @@ throw new Error(`Duplicates found in ${this.name}'s enumeration values`);

@@ -317,3 +317,2 @@ "use strict";

let minConstructor = overloads[0];
for (let i = 1; i < overloads.length; ++i) {

@@ -325,23 +324,14 @@ if (overloads[i].nameList.length < minConstructor.nameList.length) {

const conversions =
Parameters.generateOverloadConversions(this.ctx, overloads, this.name, `Failed to construct '${this.name}': `);
const conversions = Parameters.generateOverloadConversions(
this.ctx, "constructor", this.name, this, `Failed to construct '${this.name}': `);
this.requires.merge(conversions.requires);
minConstructor.nameList = minConstructor.nameList.map(name => (keywords.has(name) ? "_" : "") + name);
const argNames = minConstructor.nameList.map(name => (keywords.has(name) ? "_" : "") + name);
this.str += `
function ${this.name}(${minConstructor.nameList.join(", ")}) {
`;
if (minConstructor.nameList.length !== 0) {
const plural = minConstructor.nameList.length > 1 ? "s" : "";
this.str += `
if (!new.target) {
function ${this.name}(${argNames.join(", ")}) {
if (new.target === undefined) {
throw new TypeError("Failed to construct '${this.name}'. Please use the 'new' operator; this constructor " +
"cannot be called as a function.");
}
if (arguments.length < ${minConstructor.nameList.length}) {
throw new TypeError("Failed to construct '${this.name}': ${minConstructor.nameList.length} " +
"argument${plural} required, but only " + arguments.length + " present.");
}
`;
}
`;
this.str += conversions.body + "\n";

@@ -1236,3 +1226,3 @@

for (const member of this.idl.members) {
if (utils.getExtAttr(member.extAttrs, "Unscopeable")) {
if (utils.getExtAttr(member.extAttrs, "Unscopable")) {
unscopables[member.name] = true;

@@ -1239,0 +1229,0 @@ }

@@ -29,4 +29,18 @@ "use strict";

fixUpArgsExtAttrs() {
for (const idl of this.idls) {
for (const arg of idl.arguments) {
if (arg.extAttrs) {
if (!arg.idlType.extAttrs) {
arg.idlType.extAttrs = [];
}
arg.idlType.extAttrs.push(...arg.extAttrs);
}
}
}
}
generate() {
const requires = new utils.RequiresMap(this.ctx);
this.fixUpArgsExtAttrs();
let str = "";

@@ -45,15 +59,14 @@

const overloads = Overloads.getEffectiveOverloads(type, this.name, 0, this.interface);
let minConstructor = overloads[0];
let minOp = overloads[0];
for (let i = 1; i < overloads.length; ++i) {
if (overloads[i].nameList.length < minConstructor.nameList.length) {
minConstructor = overloads[i];
if (overloads[i].nameList.length < minOp.nameList.length) {
minOp = overloads[i];
}
}
const fnName = keywords.has(this.name) ? "_" : this.name;
minConstructor.nameList = minConstructor.nameList.map(n => (keywords.has(n) ? "_" : "") + n);
const fnName = (keywords.has(this.name) ? "_" : "") + this.name;
const argNames = minOp.nameList.map(name => (keywords.has(name) ? "_" : "") + name);
str += `
${targetObj}.${this.name} = function ${fnName}(${minConstructor.nameList.join(", ")}) {
${targetObj}.${this.name} = function ${fnName}(${argNames.join(", ")}) {
`;

@@ -68,13 +81,2 @@ if (!this.static) {

if (minConstructor.nameList.length !== 0) {
const plural = minConstructor.nameList.length > 1 ? "s" : "";
str += `
if (arguments.length < ${minConstructor.nameList.length}) {
throw new TypeError("Failed to execute '${this.name}' on '${this.interface.name}': " +
"${minConstructor.nameList.length} argument${plural} required, but only " +
arguments.length + " present.");
}
`;
}
const callOn = this.static ? "Impl.implementation" : "this[impl]";

@@ -86,3 +88,3 @@ // In case of stringifiers, use the named implementation function rather than hardcoded "toString".

const parameterConversions = Parameters.generateOverloadConversions(
this.ctx, overloads, this.interface.name, `Failed to execute '${this.name}' on '${this.interface.name}': `);
this.ctx, type, this.name, this.interface, `Failed to execute '${this.name}' on '${this.interface.name}': `);
const argsSpread = parameterConversions.hasArgs ? "...args" : "";

@@ -89,0 +91,0 @@ requires.merge(parameterConversions.requires);

@@ -21,3 +21,2 @@ "use strict";

initialize() {
this.customTypes = new Map();
this.typedefs = new Map();

@@ -32,4 +31,20 @@ this.interfaces = new Map();

}
typeOf(name) {
if (this.typedefs.has(name)) {
return "typedef";
}
if (this.interfaces.has(name)) {
return "interface";
}
if (this.dictionaries.has(name)) {
return "dictionary";
}
if (this.enumerations.has(name)) {
return "enumeration";
}
return undefined;
}
}
module.exports = Context;

@@ -12,17 +12,2 @@ "use strict";

function getReferenceToBytes(bufferSource) {
// Node.js' Buffer does not allow subclassing for now, so we can get away with a prototype object check for perf.
if (Object.getPrototypeOf(bufferSource) === Buffer.prototype) {
return bufferSource;
}
if (bufferSource instanceof ArrayBuffer) {
return Buffer.from(bufferSource);
}
return Buffer.from(bufferSource.buffer, bufferSource.byteOffset, bufferSource.byteLength);
}
function getCopyToBytes(bufferSource) {
return Buffer.from(getReferenceToBytes(bufferSource));
}
const wrapperSymbol = Symbol("wrapper");

@@ -96,4 +81,2 @@ const implSymbol = Symbol("impl");

hasOwn,
getReferenceToBytes,
getCopyToBytes,
wrapperSymbol,

@@ -100,0 +83,0 @@ implSymbol,

"use strict";
module.exports.getEffectiveOverloads = function (type, A, N, I) {
const S = [];
let F = null;
const { areDistinguishable, sameType } = require("./types");
function getOperations(type, A, I) {
switch (type) {
case "regular operation":
F = I.operations.get(A).idls;
break;
return I.operations.get(A).idls;
case "static operation":
F = I.staticOperations.get(A).idls;
break;
case "constructor":
F = I.idl.extAttrs.filter(a => a.name === "Constructor");
for (const c of F) {
return I.staticOperations.get(A).idls;
case "constructor": {
const ctor = I.idl.extAttrs.filter(a => a.name === "Constructor");
for (const c of ctor) {
if (!c.arguments) {

@@ -21,7 +18,12 @@ c.arguments = [];

}
break;
default:
throw new RangeError(`${type}s are not yet supported`);
return ctor;
}
}
throw new RangeError(`${type}s are not yet supported`);
}
module.exports.getOperations = getOperations;
module.exports.getEffectiveOverloads = function (type, A, N, I) {
const S = [];
const F = getOperations(type, A, I);
let maxArgs = 0;

@@ -34,7 +36,7 @@ for (const X of F) {

const m = Math.max(maxArgs, N);
const max = Math.max(maxArgs, N);
for (const X of F) {
const n = X.arguments.length;
const nameList = X.arguments.map(arg => arg.name);
const typeList = X.arguments.map(arg => arg.idlType.idlType);
const typeList = X.arguments.map(arg => arg.idlType);
const optionalityList = X.arguments.map(arg => {

@@ -58,8 +60,16 @@ if (arg.optional) {

if (optionalityList[optionalityList.length - 1] === "variadic") {
for (let i = n; i <= m - 1; i++) {
for (let i = n; i <= max - 1; i++) {
const variadicNames = nameList.slice(0, n);
const variadicTypes = typeList.slice(0, n);
const variadicOptionalityValues = optionalityList.slice(0, n);
for (let j = n; j <= i; j++) {
variadicNames.push(nameList[n - 1]);
variadicTypes.push(typeList[n - 1]);
variadicOptionalityValues.push("variadic");
}
S.push({
operation: X,
nameList: nameList.slice(0, i + 1),
typeList: typeList.slice(0, i + 1),
optionalityList: optionalityList.slice(0, i + 1)
nameList: variadicNames,
typeList: variadicTypes,
optionalityList: variadicOptionalityValues
});

@@ -85,38 +95,26 @@ }

module.exports.proveSimiliarity = function (ctx, overloads) {
let maxArguments = overloads[0].nameList.length;
for (let i = 1; i < overloads.length; ++i) {
if (overloads[i].nameList.length > maxArguments) {
maxArguments = overloads[i].nameList.length;
module.exports.distinguishingArgumentIndex = function (ctx, S) {
for (let i = 0; i < S[0].typeList.length; i++) {
let distinguishable = true;
for (let j = 0; j < S.length - 1; j++) {
for (let k = j + 1; k < S.length; k++) {
if (!areDistinguishable(ctx, S[j].typeList[i], S[k].typeList[i])) {
distinguishable = false;
}
}
}
}
const typeConversions = [];
for (let i = 0; i < maxArguments; ++i) {
if (overloads[0].operation.arguments.length <= i) {
break;
if (distinguishable) {
return i;
}
let maybeType = {
type: overloads[0].operation.arguments[i].idlType,
optional: overloads[0].optionalityList[i] !== "required",
default: overloads[0].operation.arguments[i].default
};
for (let j = 1; j < overloads.length; ++j) {
if (overloads[j].optionalityList[i] !== "required") {
maybeType.optional = true;
for (let j = 0; j < S.length - 1; j++) {
for (let k = j + 1; k < S.length; k++) {
if (!sameType(ctx, S[j].typeList[i], S[k].typeList[i])) {
throw new Error(`Different but indistinguishable types at index ${i}`);
}
}
const thisType = overloads[j].operation.arguments[i].idlType;
if (maybeType.type.idlType !== thisType.idlType || maybeType.type.array !== thisType.array ||
maybeType.default !== overloads[j].operation.arguments[i].default) {
maybeType = null;
break;
}
}
typeConversions.push(maybeType);
}
return typeConversions;
return -1;
};

@@ -8,26 +8,31 @@ "use strict";

function generateVarConversion(ctx, name, conversion, argAttrs, ...typeArgs) {
const { customTypes } = ctx;
function isOrIncludes(ctx, parent, predicate) {
parent = Types.resolveType(ctx, parent);
return predicate(parent) || parent.union && parent.idlType.some(predicate);
}
function generateVarConversion(ctx, overload, i, parent, errPrefix, targetIdx = i) {
const requires = new utils.RequiresMap(ctx);
let str = "";
const idlType = conversion.type;
const idlType = overload.typeList[i];
// Always (try to) force-convert dictionaries
const optional = conversion.optional && customTypes.get(idlType.idlType) !== "dictionary";
const optional = overload.optionalityList[i] === "optional" && !ctx.dictionaries.has(idlType.idlType);
let str = `{ let curArg = arguments[${targetIdx}];`;
if (optional) {
str += `
if (${name} !== undefined) {
if (curArg !== undefined) {
`;
}
const conv = Types.generateTypeConversion(ctx, name, idlType, argAttrs, ...typeArgs);
const msg = typeof targetIdx === "string" ?
`"${errPrefix}parameter " + (${targetIdx} + 1)` : `"${errPrefix}parameter ${i + 1}"`;
const conv = Types.generateTypeConversion(
ctx, "curArg", idlType, [], parent.name, msg);
requires.merge(conv.requires);
str += conv.body;
if (optional) {
str += "}";
if (conversion.default) {
const defaultValue = overload.operation.arguments[i].default;
if (defaultValue) {
str += `
else {
${name} = ${utils.getDefault(conversion.default)};
curArg = ${utils.getDefault(defaultValue)};
}

@@ -37,3 +42,4 @@ `;

}
str += "args.push(curArg);";
str += "}";
return {

@@ -45,80 +51,270 @@ requires,

module.exports.generateOverloadConversions = function (ctx, overloads, parentName, errPrefix) {
module.exports.generateOverloadConversions = function (ctx, typeOfOp, name, parent, errPrefix) {
const requires = new utils.RequiresMap(ctx);
let str = ``;
let maxConstructor = overloads[0];
let maxArguments = overloads[0].nameList.length;
let isVariadic = overloads[0].optionalityList.indexOf("variadic") !== -1;
for (let i = 1; i < overloads.length; ++i) {
if (overloads[i].nameList.length > maxConstructor.nameList.length) {
maxConstructor = overloads[i];
const ops = Overloads.getOperations(typeOfOp, name, parent);
const argLengths = Overloads.getEffectiveOverloads(typeOfOp, name, 0, parent).map(o => o.typeList.length);
const maxArgs = Math.max(...argLengths);
let str = "";
if (maxArgs > 0) {
const minArgs = Math.min(...argLengths);
if (minArgs > 0) {
const plural = minArgs > 1 ? "s" : "";
str += `
if (arguments.length < ${minArgs}) {
throw new TypeError("${errPrefix}${minArgs} argument${plural} required, but only " + arguments.length +
" present.");
}
`;
}
if (overloads[i].nameList.length > maxArguments) {
maxArguments = overloads[i].nameList.length;
}
if (overloads[i].optionalityList.indexOf("variadic") !== -1) {
isVariadic = true;
}
}
const typeConversions = Overloads.proveSimiliarity(ctx, overloads);
const isAlwaysZeroArgs = !isVariadic && maxArguments === 0;
if (!isAlwaysZeroArgs) {
const extraClause = !isVariadic ? ` && i < ${maxArguments}` : ``;
str += `
const args = [];
for (let i = 0; i < arguments.length${extraClause}; ++i) {
args[i] = arguments[i];
str += "const args = [];";
const switchCases = [];
// Special case: when the operation isn't overloaded, always try to convert to the maximum number of args.
for (let numArgs = ops.length === 1 ? maxArgs : minArgs; numArgs <= maxArgs; numArgs++) {
// for (let numArgs = minArgs; numArgs <= maxArgs; numArgs++) {
const S = Overloads.getEffectiveOverloads(typeOfOp, name, numArgs, parent)
.filter(o => o.typeList.length === numArgs);
if (S.length === 0) {
switchCases.push(`
throw new TypeError("${errPrefix}only " + arguments.length + " arguments present.");
`);
continue;
}
`;
for (let i = 0; i < typeConversions.length; ++i) {
if (typeConversions[i] === null) {
continue;
let d = -1;
if (S.length > 1) {
d = Overloads.distinguishingArgumentIndex(ctx, S);
}
let caseSrc = "";
let i = 0;
for (; i < d; i++) {
const conv = generateVarConversion(ctx, S[0], i, parent, errPrefix);
requires.merge(conv.requires);
caseSrc += conv.body;
}
if (i === d) {
caseSrc += "{";
caseSrc += `let curArg = arguments[${d}];`;
const possibilities = [];
// For variadic operations, we only try to fully support non-overloaded variadic operations right now. It is a
// TODO to support overloaded variadic functions. The decision to only support the simplest cases of variadic
// operations is made, because overload resolution for overloaded variadic operations is non-trivial but hardly
// used in the Web platform (or at least the jsdom-supported subset of it), and because the entire overload
// resolution apparatus will require a rewrite for https://github.com/jsdom/webidl2js/issues/29.
//
// To determine that the current argument we are processing is the variadic one, the following three-fold
// algorithm is used:
//
// 1. The current argument must the last one.
// 2. The effective overload set must have a length of two (one with the final variadic argument, one without).
// 3. The current argument must have an optionality value of "variadic" in either one of the overloads.
const curIsVariadic = i + 1 === maxArguments && overloads.length === 2 &&
(overloads[0].optionalityList[i] === "variadic" ||
overloads[1].optionalityList[i] === "variadic");
let msg = `"${errPrefix}parameter ${i + 1}"`;
let lvalue = `args[${i}]`;
const optionals = S.filter(o => o.optionalityList[d] === "optional");
if (optionals.length) {
possibilities.push(`
if (curArg === undefined) {
${continued(optionals[0], i)}
}
`);
}
if (curIsVariadic) {
msg = `"${errPrefix}parameter " + (i + 1)`;
lvalue = `args[i]`;
str += `for (let i = ${i}; i < arguments.length; ++i) {`;
const nullables = S.filter(o => {
return isOrIncludes(ctx, o.typeList[d], t => t.nullable || ctx.dictionaries.has(t.idlType));
});
if (nullables.length) {
possibilities.push(`
if (curArg === null || curArg === undefined) {
${continued(nullables[0], i)}
}
`);
}
// The object will not be re-used outside of this function, so mutating it is fine here.
typeConversions[i].optional = false;
}
const interfaceTypes = new Map();
for (const o of S) {
const type = Types.resolveType(ctx, o.typeList[d]);
if (ctx.interfaces.has(type.idlType)) {
interfaceTypes.set(type.idlType, o);
} else if (type.union) {
for (const child of type.idlType) {
if (ctx.interfaces.has(child.idlType)) {
interfaceTypes.set(child.idlType, o);
}
}
}
}
for (const [iface, overload] of interfaceTypes) {
let fn;
// Avoid requiring the interface itself
if (iface !== parent.name) {
fn = `is${iface}`;
requires.add(iface, "is");
} else {
fn = "module.exports.is";
}
possibilities.push(`
if (${fn}(curArg)) {
${continued(overload, i)}
}
`);
}
const conv = generateVarConversion(
ctx, lvalue, typeConversions[i], maxConstructor.operation.arguments[i].extAttrs, parentName, msg);
requires.merge(conv.requires);
str += conv.body;
// Handle Error and DOMException the same way
const exceptions = S.filter(o => {
return isOrIncludes(ctx, o.typeList[d], t => Types.exceptionTypes.has(t.idlType));
});
if (exceptions.length) {
possibilities.push(`
if (curArg instanceof Error) {
${continued(exceptions[0], i)}
}
`);
}
if (curIsVariadic) {
str += "}";
const arrayBuffers = S.filter(o => isOrIncludes(ctx, o.typeList[d], t => t.idlType === "ArrayBuffer"));
if (arrayBuffers.length) {
possibilities.push(`
if (curArg instanceof ArrayBuffer ||
typeof SharedArrayBuffer !== "undefined" && curArg instanceof SharedArrayBuffer) {
${continued(arrayBuffers[0], i)}
}
`);
}
const arrayBufferViews = new Map();
for (const o of S) {
const type = Types.resolveType(ctx, o.typeList[d]);
if (Types.arrayBufferViewTypes.has(type.idlType)) {
arrayBufferViews.set(type.idlType, o);
} else if (type.union) {
for (const child of type.idlType) {
if (Types.arrayBufferViewTypes.has(child.idlType)) {
arrayBufferViews.set(child.idlType, o);
}
}
}
}
if (arrayBufferViews.size) {
// Special case for all ArrayBufferView types.
if (arrayBufferViews.size === Types.arrayBufferViewTypes.size &&
new Set(arrayBufferViews.values()).size === 1) {
possibilities.push(`
if (ArrayBuffer.isView(curArg)) {
${continued(arrayBufferViews.get("Uint8Array"), i)}
}
`);
} else {
for (const [type, overload] of arrayBufferViews) {
possibilities.push(`
if (ArrayBuffer.isView(curArg) && curArg instanceof ${type}) {
${continued(overload, i)}
}
`);
}
}
}
const callables = S.filter(o => {
return isOrIncludes(ctx, o.typeList[d], t => ["Function", "VoidFunction"].includes(t.idlType));
});
if (callables.length) {
possibilities.push(`
if (typeof curArg === "function") {
${continued(callables[0], i)}
}
`);
}
const iterables = S.filter(o => {
return isOrIncludes(ctx, o.typeList[d], t => ["sequence", "FrozenArray"].includes(t.generic));
});
if (iterables.length) {
possibilities.push(`
if (utils.isObject(curArg) && typeof curArg[Symbol.iterator] === "function") {
${continued(iterables[0], i)}
}
`);
}
const objects = S.filter(o => isOrIncludes(ctx, o.typeList[d], t => t.idlType === "object"));
if (objects.length) {
possibilities.push(`
if (utils.isObject(curArg)) {
${continued(objects[0], i)}
}
`);
}
const booleans = S.filter(o => isOrIncludes(ctx, o.typeList[d], t => t.idlType === "boolean"));
if (booleans.length) {
possibilities.push(`
if (typeof curArg === "boolean") {
${continued(booleans[0], i)}
}
`);
}
const numerics = S.filter(o => isOrIncludes(ctx, o.typeList[d], t => Types.numericTypes.has(t.idlType)));
if (numerics.length) {
possibilities.push(`
if (typeof curArg === "number") {
${continued(numerics[0], i)}
}
`);
}
const strings = S.filter(o => {
return isOrIncludes(ctx, o.typeList[d], t => {
return Types.stringTypes.has(t.idlType) || ctx.enumerations.has(t.idlType);
});
});
const any = S.filter(o => isOrIncludes(ctx, o.typeList[d], t => t.idlType === "any"));
if (strings.length) {
possibilities.push(`{ ${continued(strings[0], i)} }`);
} else if (numerics.length) {
possibilities.push(`{ ${continued(numerics[0], i)} }`);
} else if (booleans.length) {
possibilities.push(`{ ${continued(booleans[0], i)} }`);
} else if (any.length) {
possibilities.push(`{ ${continued(any[0], i)} }`);
} else {
possibilities.push(`throw new TypeError("${errPrefix}No such overload");`);
}
caseSrc += possibilities.join(" else ");
caseSrc += "}";
} else {
// Branch taken when S.length === 1.
caseSrc += continued(S[0], i);
}
switchCases.push(caseSrc);
function continued(overload, idx) {
let continuedStr = "";
for (; idx < numArgs; idx++) {
let targetIdx = idx;
if (overload.optionalityList[idx] === "variadic" && numArgs === maxArgs && idx === numArgs - 1) {
continuedStr += `for (let i = ${idx}; i < arguments.length; i++)`;
targetIdx = "i";
}
const conv = generateVarConversion(ctx, overload, idx, parent, errPrefix, targetIdx);
requires.merge(conv.requires);
continuedStr += conv.body;
}
return continuedStr;
}
}
if (switchCases.length === 1) {
str += switchCases[0];
} else {
str += "switch (arguments.length) {";
let lastBody;
for (let i = 0; i < switchCases.length - 1; i++) {
if (lastBody !== undefined && switchCases[i] !== lastBody) {
str += lastBody + "break;";
}
str += `case ${minArgs + i}:`;
lastBody = switchCases[i];
}
if (lastBody !== undefined && switchCases[switchCases.length - 1] !== lastBody) {
str += lastBody + "break;";
}
str += "default:";
str += switchCases[switchCases.length - 1];
str += "}";
}
}
return {
requires,
body: str,
hasArgs: !isAlwaysZeroArgs
hasArgs: maxArgs > 0
};
};

@@ -25,3 +25,3 @@ "use strict";

this.sources = [];
this.sources = []; // Absolute paths to the IDL and Impl directories.
this.utilPath = null;

@@ -37,3 +37,3 @@ }

}
this.sources.push({ idlPath: idl, impl });
this.sources.push({ idlPath: path.resolve(idl), impl: path.resolve(impl) });
return this;

@@ -85,3 +85,3 @@ }

this.ctx.initialize();
const { interfaces, dictionaries, enumerations, typedefs, customTypes } = this.ctx;
const { interfaces, dictionaries, enumerations, typedefs } = this.ctx;

@@ -99,6 +99,5 @@ // first we're gathering all full interfaces and ignore partial ones

obj = new Interface(this.ctx, instruction, {
implDir: path.resolve(outputDir, file.impl)
implDir: file.impl
});
interfaces.set(obj.name, obj);
customTypes.set(obj.name, "interface");
break;

@@ -114,3 +113,2 @@ case "implements":

dictionaries.set(obj.name, obj);
customTypes.set(obj.name, "dictionary");
break;

@@ -120,3 +118,2 @@ case "enum":

enumerations.set(obj.name, obj);
customTypes.set(obj.name, "enumeration");
break;

@@ -186,4 +183,4 @@ case "typedef":

const implDir = path.relative(outputDir, obj.opts.implDir).replace(/\\/g, "/"); // fix windows file paths
let implFile = implDir + "/" + obj.name + this.ctx.implSuffix;
let implFile = path.relative(outputDir, path.resolve(obj.opts.implDir, obj.name + this.ctx.implSuffix));
implFile = implFile.replace(/\\/g, "/"); // fix windows file paths
if (implFile[0] !== ".") {

@@ -190,0 +187,0 @@ implFile = "./" + implFile;

@@ -7,6 +7,8 @@ "use strict";

const arrayBufferViewTypes = new Set([
const typedArrayTypes = new Set([
"Int8Array", "Int16Array", "Int32Array", "Uint8Array", "Uint16Array", "Uint32Array",
"Uint8ClampedArray", "Float32Array", "Float64Array", "DataView"
"Uint8ClampedArray", "Float32Array", "Float64Array"
]);
const arrayBufferViewTypes = new Set([...typedArrayTypes, "DataView"]);
const bufferSourceTypes = new Set([...arrayBufferViewTypes, "ArrayBuffer"]);
const stringTypes = new Set(["DOMString", "ByteString", "USVString"]);

@@ -20,3 +22,6 @@ const integerTypes = new Set([

]);
const exceptionTypes = new Set(["Error", "DOMException"]);
const resolvedMap = new WeakMap();
function mergeExtAttrs(a = [], b = []) {

@@ -26,5 +31,13 @@ return [...a, ...b];

// Types of types that generate an output file.
const resolvedTypes = new Set(["dictionary", "enumeration", "interface"]);
function resolveType(ctx, idlType, stack = []) {
if (resolvedMap.has(idlType)) {
return resolvedMap.get(idlType);
}
const original = idlType;
idlType = deepClone(idlType);
const { customTypes, typedefs } = ctx;
resolvedMap.set(original, idlType);
if (idlType.union) {

@@ -54,7 +67,8 @@ const types = [];

return idlType;
} else if (customTypes.has(idlType.idlType)) {
} else if (resolvedTypes.has(ctx.typeOf(idlType.idlType))) {
// already resolved
return idlType;
} else if (typedefs.has(idlType.idlType)) {
const out = deepClone(typedefs.get(idlType.idlType).resolve(stack));
} else if (ctx.typedefs.has(idlType.idlType)) {
const out = deepClone(ctx.typedefs.get(idlType.idlType).resolve(stack));
resolvedMap.set(original, out);
out.nullable = out.nullable || idlType.nullable;

@@ -81,3 +95,2 @@ out.extAttrs = deepClone(mergeExtAttrs(out.extAttrs, idlType.extAttrs));

function generateTypeConversion(ctx, name, idlType, argAttrs = [], parentName, errPrefix = '"The provided value"') {
const { customTypes } = ctx;
const requires = new utils.RequiresMap(ctx);

@@ -115,4 +128,4 @@ let str = "";

generateGeneric(`conversions["${idlType.idlType}"]`);
} else if (customTypes.has(idlType.idlType)) {
// dictionaries or interfaces
} else if (resolvedTypes.has(ctx.typeOf(idlType.idlType))) {
// dictionaries, enumerations, and interfaces
let fn;

@@ -381,3 +394,2 @@ // Avoid requiring the interface itself

function extractUnionInfo(ctx, idlType, errPrefix) {
const { customTypes, enumerations } = ctx;
const seen = {

@@ -435,3 +447,3 @@ sequenceLike: null,

seen.ArrayBufferViews.add(item.idlType);
} else if (stringTypes.has(item.idlType) || enumerations.has(item.idlType)) {
} else if (stringTypes.has(item.idlType) || ctx.enumerations.has(item.idlType)) {
if (seen.string) {

@@ -460,3 +472,3 @@ error("There can only be one string type in a union type");

seen.object = true;
} else if (item.idlType === "DOMException" || item.idlType === "Error") {
} else if (exceptionTypes.has(item.idlType)) {
if (seen.object) {

@@ -480,24 +492,18 @@ error("Exception types are not distinguishable with object type");

seen.callback = true;
} else if (customTypes.has(item.idlType)) {
// Enumerations were handled with the string types.
const type = customTypes.get(item.idlType);
if (type === "dictionary") {
if (seen.object) {
error("Dictionary-like types are not distinguishable with object type");
}
if (seen.callback) {
error("Dictionary-like types are not distinguishable with callback functions");
}
if (seen.dictionaryLike) {
error("There can only be one dictionary-like type in a union type");
}
seen.dictionary = item;
} else if (type === "interface") {
if (seen.object) {
error("Interface types are not distinguishable with object type");
}
seen.interfaces.add(item.idlType);
} else {
error(`Unknown custom type ${type}`);
} else if (ctx.dictionaries.has(item.idlType)) {
if (seen.object) {
error("Dictionary-like types are not distinguishable with object type");
}
if (seen.callback) {
error("Dictionary-like types are not distinguishable with callback functions");
}
if (seen.dictionaryLike) {
error("There can only be one dictionary-like type in a union type");
}
seen.dictionary = item;
} else if (ctx.interfaces.has(item.idlType)) {
if (seen.object) {
error("Interface types are not distinguishable with object type");
}
seen.interfaces.add(item.idlType);
} else {

@@ -514,5 +520,184 @@ seen.unknown = true;

// https://heycam.github.io/webidl/#dfn-includes-a-nullable-type
function includesNullableType(ctx, idlType) {
idlType = resolveType(ctx, idlType);
if (idlType.nullable) {
return true;
}
if (!idlType.union) {
return false;
}
for (const type of idlType.idlType) {
if (type.nullable) {
return true;
}
}
return false;
}
function includesDictionaryType(ctx, idlType) {
idlType = resolveType(ctx, idlType);
if (typeof idlType.idlType === "string" && ctx.dictionaries.has(idlType.idlType)) {
return true;
}
if (!idlType.union) {
return false;
}
for (const type of idlType.idlType) {
if (includesDictionaryType(ctx, type)) {
return true;
}
}
return false;
}
function sameType(ctx, type1, type2) {
if (type1 === type2) {
return true;
}
type1 = resolveType(ctx, type1);
type2 = resolveType(ctx, type2);
if (type1.generic !== type2.generic) {
return false;
}
if (type1.union !== type2.union) {
return false;
}
if (includesNullableType(ctx, type1) !== includesNullableType(ctx, type2)) {
return false;
}
// TODO: check extended attributes
if (typeof type1.idlType === "string" || typeof type2.idlType === "string") {
return type1.idlType === type2.idlType;
}
if (type1.generic === "sequence" || type1.generic === "FrozenArray") {
return sameType(ctx, type1.idlType, type2.idlType);
}
if (type1.generic === "record") {
return sameType(ctx, type1.idlType[0], type2.idlType[0]) &&
sameType(ctx, type2.idlType[1], type2.idlType[1]);
}
if (!type1.union) {
// This branch should never be taken.
return false;
}
const extracted1 = extractUnionInfo(ctx, type1, `""`);
const extracted2 = extractUnionInfo(ctx, type2, `""`);
return sameType(ctx, extracted1.sequenceLike, extracted2.sequenceLike) &&
sameType(ctx, extracted1.record, extracted2.record) &&
extracted1.ArrayBuffer !== extracted2.ArrayBuffer &&
JSON.stringify([...extracted1.ArrayBufferViews].sort()) ===
JSON.stringify([...extracted2.ArrayBufferViews].sort()) &&
extracted1.object === extracted2.object &&
sameType(ctx, extracted1.exception, extracted2.exception) &&
sameType(ctx, extracted1.string, extracted2.string) &&
sameType(ctx, extracted1.numeric, extracted2.numeric) &&
sameType(ctx, extracted1.boolean, extracted2.boolean) &&
extracted1.callback === extracted2.callback &&
sameType(ctx, extracted1.dictionary, extracted2.dictionary) &&
JSON.stringify([...extracted1.interfaces].sort()) ===
JSON.stringify([...extracted2.interfaces].sort()) &&
extracted1.unknown === extracted2.unknown;
}
function areDistinguishable(ctx, type1, type2) {
const resolved1 = resolveType(ctx, type1);
const resolved2 = resolveType(ctx, type2);
const effectivelyNullable1 = includesNullableType(ctx, resolved1) || includesDictionaryType(ctx, resolved1);
const effectivelyNullable2 = includesNullableType(ctx, resolved2) || includesDictionaryType(ctx, resolved2);
if (includesNullableType(ctx, resolved1) && effectivelyNullable2 ||
effectivelyNullable1 && includesNullableType(ctx, resolved2)) {
return false;
}
if (resolved1.union && resolved2.union) {
for (const i of resolved1.idlType) {
for (const j of resolved2.idlType) {
if (!areDistinguishable(ctx, i, j)) {
return false;
}
}
}
return true;
}
function inner(inner1, inner2) {
if (inner1.union) {
for (const i of inner1.idlType) {
if (!areDistinguishable(ctx, i, inner2)) {
return false;
}
}
return true;
}
if (inner1.idlType === "boolean") {
return inner2.idlType !== "boolean";
}
if (numericTypes.has(inner1.idlType)) {
return !numericTypes.has(inner2.idlType);
}
if (stringTypes.has(inner1.idlType) || ctx.enumerations.has(inner1.idlType)) {
return !stringTypes.has(inner2.idlType) && !ctx.enumerations.has(inner2.idlType);
}
const isInterfaceLike1 = ctx.interfaces.has(inner1.idlType) || exceptionTypes.has(inner1.idlType) ||
bufferSourceTypes.has(inner1.idlType);
const isInterfaceLike2 = ctx.interfaces.has(inner2.idlType) || exceptionTypes.has(inner2.idlType) ||
bufferSourceTypes.has(inner2.idlType);
const isDictionaryLike1 = ctx.dictionaries.has(inner1.idlType) || inner1.generic === "record";
const isDictionaryLike2 = ctx.dictionaries.has(inner2.idlType) || inner2.generic === "record";
const isSequenceLike1 = inner1.generic === "sequence" || inner1.generic === "FrozenArray";
const isSequenceLike2 = inner2.generic === "sequence" || inner2.generic === "FrozenArray";
if (inner1.idlType === "object") {
return inner2.idlType !== "object" &&
!isInterfaceLike2 &&
!isDictionaryLike2 &&
!isSequenceLike2;
}
if (inner1.idlType === "symbol") {
return inner2.idlType !== "symbol";
}
if (isInterfaceLike1) {
return inner2.idlType !== "object" &&
(!isInterfaceLike2 ||
!(inner2.idlType === "Error" && inner1.idlType === "DOMException") &&
(!ctx.interfaces.has(inner2.idlType) ||
!new Set(ctx.interfaces.get(inner2.idlType).allInterfaces()).has(inner1.idlType)));
}
if (isDictionaryLike1) {
return inner2.idlType !== "object" && !isDictionaryLike2;
}
if (isSequenceLike1) {
return inner2.idlType !== "object" && !isSequenceLike2;
}
return true;
}
return inner(resolved1, resolved2) && inner(resolved2, resolved1);
}
module.exports = {
arrayBufferViewTypes,
stringTypes,
numericTypes,
exceptionTypes,
generateTypeConversion,
resolveType
resolveType,
includesNullableType,
includesDictionaryType,
areDistinguishable,
sameType
};

@@ -6,5 +6,6 @@ "use strict";

case "boolean":
case "number":
case "string":
return JSON.stringify(dflt.value);
case "number":
return dflt.value;
case "null":

@@ -11,0 +12,0 @@ case "NaN":

{
"name": "webidl2js",
"version": "8.0.0",
"version": "9.0.0",
"description": "Auto-generates class structures for WebIDL specifications",

@@ -11,7 +11,7 @@ "main": "lib/transformer.js",

"webidl-conversions": "^4.0.0",
"webidl2": "^4.1.0"
"webidl2": "^8.1.0"
},
"devDependencies": {
"eslint": "^4.3.0",
"jest": "^20.0.4"
"jest": "^21.2.1"
},

@@ -18,0 +18,0 @@ "scripts": {

@@ -231,46 +231,8 @@ # JavaScript bindings generator for Web IDL

Variadic operations are fully supported by webidl2js.
#### Overloaded operations
One other subtlety here is overloads: if the IDL file defines overloads for a given operation, webidl2js is not always as helpful as it could be.
In the case of overloaded operations, webidl2js is not always as helpful as it could be. Due to the fact that JavaScript does not have a C++-like method overloading system, all overloads of a method are dispatched to the same implementation method, which means that the implementation class would need to do some secondary overload resolution to determine which overload is actually called.
1. webidl2js does not yet implement the [overload resolution algorithm](https://heycam.github.io/webidl/#dfn-overload-resolution-algorithm) fully. Take the `overload1()` operations below:
```webidl
void overload1(DOMString first, long second);
void overload1(DOMString first, long second, DOMString third);
```
webidl2js fully handles type conversion for both `first` and `second` arguments, but does not try to convert the third argument even if it is provided.
Similarly, consider the following `overload2()` operations:
```webidl
void overload2(Blob first, long second);
void overload2(long first, long second);
```
webidl2js only converts `second` argument because its type is unambiguous, and calls the implementation method with `first` unconverted. In particular, this means that webidl2js will not try to unwrap the `first` argument into a `Blob` implementation class, even if it is a `Blob`.
2. webidl2js does not dispatch overloaded operations to separate implementation class methods, but instead performs type conversions if possible and then sends the result to the same backing method.
We're hoping to fix both of these problems in [#29](https://github.com/jsdom/webidl2js/issues/29). But in the meantime, properly implementing overloads requires doing some extra type-checking (often using appropriate [`is()`](#isvalue) functions) to determine which case of the overload you ended up in, and manual conversion with [webidl-conversions][] and/or unwrapping.
#### Variadic operations
Variadic operations are fully supported by webidl2js, but only if the particular operation is not overloaded. So while type conversions for the `simple1` and `simple2` operations below are fully implemented, webidl2js will not provide any variadic semantics for `overloaded1()` or `overloaded2()`.
```webidl
void simple1(DOMString... strings);
void simple2(DOMString first, URL... urls);
void overloaded1(DOMString... strings);
void overloaded1(unsigned long... numbers);
void overloaded2(DOMString first, DOMString... strings);
void overloaded2(unsigned long first, DOMString... strings);
```
We hope to fix this problem in the overload resolution overhaul ([#29](https://github.com/jsdom/webidl2js/issues/29)). Right now, however, manual conversions will be needed for overloaded variadic operations.
#### Static operations

@@ -379,3 +341,3 @@

- Overload resolution (although [tricky cases are not easy on the implementation class](#overloaded-operations))
- Variadic arguments ([only non-overloaded operations](#variadic-operations) are supported though)
- Variadic arguments
- `[Clamp]`

@@ -393,3 +355,3 @@ - `[Constructor]`

- `[Unforgeable]`
- `[Unscopeable]`
- `[Unscopable]`

@@ -396,0 +358,0 @@ Notable missing features include:

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc