Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions lib/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const builtinTypes = webidl.parse(`
Uint8Array or Uint16Array or Uint32Array or Uint8ClampedArray or
Float32Array or Float64Array or DataView) ArrayBufferView;
typedef (ArrayBufferView or ArrayBuffer) BufferSource;
typedef (ArrayBuffer or SharedArrayBuffer or [AllowShared] ArrayBufferView) AllowSharedBufferSource;
typedef unsigned long long DOMTimeStamp;

callback Function = any (any... arguments);
Expand Down
16 changes: 14 additions & 2 deletions lib/output/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,22 @@ function isArrayIndexPropName(P) {
return true;
}

const byteLengthGetter =
const arrayBufferByteLengthGetter =
Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, "byteLength").get;
function isArrayBuffer(value) {
try {
byteLengthGetter.call(value);
arrayBufferByteLengthGetter.call(value);
return true;
} catch {
return false;
}
}

const sharedArrayBufferByteLengthGetter =
Object.getOwnPropertyDescriptor(SharedArrayBuffer.prototype, "byteLength").get;
function isSharedArrayBuffer(value) {
try {
sharedArrayBufferByteLengthGetter.call(value);
return true;
} catch {
return false;
Expand Down Expand Up @@ -219,6 +230,7 @@ module.exports = exports = {
tryImplForWrapper,
iterInternalSymbol,
isArrayBuffer,
isSharedArrayBuffer,
isArrayIndexPropName,
supportsPropertyIndex,
supportedPropertyIndices,
Expand Down
11 changes: 11 additions & 0 deletions lib/parameters.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,17 @@ module.exports.generateOverloadConversions = function (ctx, typeOfOp, name, pare
`);
}

const sharedArrayBuffers = S.filter(o => {
return isOrIncludes(ctx, o.typeList[d], t => t.idlType === "SharedArrayBuffer");
});
if (sharedArrayBuffers.length) {
possibilities.push(`
if (utils.isSharedArrayBuffer(curArg)) {
${continued(sharedArrayBuffers[0], i)}
}
`);
}

const arrayBufferViews = new Map();
for (const o of S) {
const type = Types.resolveType(ctx, o.typeList[d]);
Expand Down
158 changes: 100 additions & 58 deletions lib/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ function mergeExtAttrs(a = [], b = []) {
return [...a, ...b];
}

function mergeExtAttrsOfTypes(idlTypes) {
const extAttrs = [];
for (const idlType of idlTypes) {
if (idlType.extAttrs !== undefined) {
extAttrs.push(...idlType.extAttrs);
}
}
return extAttrs;
}

// Types of types that generate an output file.
const resolvedTypes = new Set(["callback", "callback interface", "dictionary", "enumeration", "interface"]);

Expand Down Expand Up @@ -142,7 +152,7 @@ function generateTypeConversion(
generateFrozenArray();
} else if (conversions[idlType.idlType]) {
// string or number type compatible with webidl-conversions
generateWebIDLConversions(`conversions["${idlType.idlType}"]`);
str += generateWebIDLConversions(`conversions["${idlType.idlType}"]`, extAttrs);
} else if (resolvedTypes.has(ctx.typeOf(idlType.idlType))) {
// callback functions, callback interfaces, dictionaries, enumerations, and interfaces
let fn;
Expand Down Expand Up @@ -192,9 +202,13 @@ function generateTypeConversion(
}

if (union.object) {
output.push(`if (utils.isObject(${name}) && ${name}[utils.implSymbol]) {
${name} = utils.implForWrapper(${name});
}`);
output.push(`
if (utils.isObject(${name})) {
if (${name}[utils.implSymbol]) {
${name} = utils.implForWrapper(${name});
}
}
`);
} else if (union.interfaces.size > 0) {
const exprs = [...union.interfaces].map(iface => {
let fn;
Expand All @@ -214,44 +228,53 @@ function generateTypeConversion(
`);
}

// Do not convert buffer source types as the impl code can either "get a reference" or "get a copy" to the bytes.
if (union.ArrayBuffer || union.object) {
output.push(`if (utils.isArrayBuffer(${name})) {}`);
if (union.ArrayBuffer) {
output.push(`if (utils.isArrayBuffer(${name})) {
${generateTypeConversion(ctx, name, union.ArrayBuffer, [], parentName, errPrefix).body}
}`);
}
if (union.SharedArrayBuffer) {
output.push(`if (utils.isSharedArrayBuffer(${name})) {
${generateTypeConversion(ctx, name, union.SharedArrayBuffer, [], parentName, errPrefix).body}
}`);
}
if (union.ArrayBufferViews.size > 0 || union.object) {
let condition = `ArrayBuffer.isView(${name})`;
if (union.ArrayBufferViews.size > 0) {
const viewIdlTypes = new Set([...union.ArrayBufferViews].map(item => item.idlType));
// Skip specific type check if all ArrayBufferView member types are allowed.
if (union.ArrayBufferViews.size !== arrayBufferViewTypes.size) {
const exprs = [...union.ArrayBufferViews].map(a => `${name}.constructor.name === "${a}"`);
condition += ` && (${exprs.join(" || ")})`;
if (viewIdlTypes.size === arrayBufferViewTypes.size) {
const viewExtAttrs = mergeExtAttrsOfTypes(union.ArrayBufferViews);
// We can't call generateTypeConversion since that will just expand the union again and recurse back to here,
// so instead we call generateWebIDLConversions directly.
output.push(`if (ArrayBuffer.isView(${name})) {
${generateWebIDLConversions(`conversions["ArrayBufferView"]`, viewExtAttrs)}
}`);
} else {
for (const viewType of union.ArrayBufferViews) {
output.push(`if (ArrayBuffer.isView(${name}) && ${name}.constructor.name === "${viewType.idlType}") {
${generateTypeConversion(ctx, name, viewType, [], parentName, errPrefix).body}
}`);
}
}
output.push(`if (${condition}) {}`);
}

if (union.callbackFunction || union.object) {
let code = `if (typeof ${name} === "function") {`;

if (union.callbackFunction) {
const conv = generateTypeConversion(
ctx,
name,
union.callbackFunction,
[],
parentName,
`${errPrefix} + " callback function"`
);
requires.merge(conv.requires);
code += conv.body;
} else if (union.object) {
// noop
}

code += "}";

output.push(code);
if (union.callbackFunction) {
const conv = generateTypeConversion(
ctx,
name,
union.callbackFunction,
[],
parentName,
`${errPrefix} + " callback function"`
);
requires.merge(conv.requires);
output.push(`
if (typeof ${name} === "function") {
${conv.body}
}
`);
}

if (union.sequenceLike || union.dictionary || union.record || union.object || union.callbackInterface) {
if (union.sequenceLike || union.dictionary || union.record || union.callbackInterface) {
let code = `if (utils.isObject(${name})) {`;

if (union.sequenceLike) {
Expand Down Expand Up @@ -295,8 +318,6 @@ function generateTypeConversion(
);
requires.merge(conv.requires);
code += conv.body;
} else if (union.object) {
// noop
}

if (union.sequenceLike) {
Expand Down Expand Up @@ -417,10 +438,12 @@ function generateTypeConversion(
str += `${name} = Object.freeze(${name});`;
}

function generateWebIDLConversions(conversionFn) {
const enforceRange = utils.getExtAttr(extAttrs, "EnforceRange");
const clamp = utils.getExtAttr(extAttrs, "Clamp");
const nullToEmptyString = utils.getExtAttr(extAttrs, "LegacyNullToEmptyString");
function generateWebIDLConversions(conversionFn, attrs) {
const enforceRange = utils.getExtAttr(attrs, "EnforceRange");
const clamp = utils.getExtAttr(attrs, "Clamp");
const nullToEmptyString = utils.getExtAttr(attrs, "LegacyNullToEmptyString");
const allowResizable = utils.getExtAttr(attrs, "AllowResizable");
const allowShared = utils.getExtAttr(attrs, "AllowShared");

let optString = `context: ${errPrefix}, globals: globalObject,`;
if (clamp) {
Expand All @@ -432,17 +455,22 @@ function generateTypeConversion(
if (nullToEmptyString) {
optString += "treatNullAsEmptyString: true,";
}
if (allowResizable) {
optString += "allowResizable: true,";
}
if (allowShared) {
optString += "allowShared: true,";
}
if (idlType.array) {
str += `
return `
for (let i = 0; i < ${name}.length; ++i) {
${name}[i] = ${conversionFn}(${name}[i], { ${optString} });
}
`;
} else {
str += `
${name} = ${conversionFn}(${name}, { ${optString} });
`;
}
return `
${name} = ${conversionFn}(${name}, { ${optString} });
`;
}

function generateWebIDL2JS(conversionFn) {
Expand Down Expand Up @@ -472,10 +500,11 @@ function extractUnionInfo(ctx, idlType, errPrefix) {
get dictionaryLike() {
return this.dictionary !== null || this.record !== null || this.callbackInterface !== null;
},
ArrayBuffer: false,
ArrayBuffer: null,
SharedArrayBuffer: null,
ArrayBufferViews: new Set(),
get BufferSource() {
return this.ArrayBuffer || this.ArrayBufferViews.size > 0;
return this.ArrayBuffer || this.SharedArrayBuffer || this.ArrayBufferViews.size > 0;
},
object: false,
string: null,
Expand Down Expand Up @@ -517,12 +546,17 @@ function extractUnionInfo(ctx, idlType, errPrefix) {
if (seen.object) {
error("ArrayBuffer is not distinguishable with object type");
}
seen.ArrayBuffer = true;
seen.ArrayBuffer = item;
} else if (item.idlType === "SharedArrayBuffer") {
if (seen.object) {
error("SharedArrayBuffer is not distinguishable with object type");
}
seen.SharedArrayBuffer = item;
} else if (arrayBufferViewTypes.has(item.idlType)) {
if (seen.object) {
error(`${item.idlType} is not distinguishable with object type`);
}
seen.ArrayBufferViews.add(item.idlType);
seen.ArrayBufferViews.add(item);
} else if (stringTypes.has(item.idlType) || ctx.enumerations.has(item.idlType)) {
if (seen.string) {
error("There can only be one string type in a union type");
Expand Down Expand Up @@ -556,7 +590,7 @@ function extractUnionInfo(ctx, idlType, errPrefix) {
if (seen.dictionaryLike) {
error("Callback functions are not distinguishable with dictionary-like types");
}
seen.callbackFunction = item.idlType;
seen.callbackFunction = item;
} else if (ctx.dictionaries.has(item.idlType)) {
if (seen.object) {
error("Dictionary-like types are not distinguishable with object type");
Expand All @@ -578,7 +612,7 @@ function extractUnionInfo(ctx, idlType, errPrefix) {
if (seen.dictionaryLike) {
error("There can only be one dictionary-like type in a union type");
}
seen.callbackInterface = item.idlType;
seen.callbackInterface = item;
} else if (ctx.interfaces.has(item.idlType)) {
if (seen.object) {
error("Interface types are not distinguishable with object type");
Expand Down Expand Up @@ -664,21 +698,29 @@ function sameType(ctx, type1, type2) {
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()) &&
sameType(ctx, extracted1.ArrayBuffer, extracted2.ArrayBuffer) &&
sameType(ctx, extracted1.SharedArrayBuffer, extracted2.SharedArrayBuffer) &&
sameTypeArray(ctx, [...extracted1.ArrayBufferViews].sort(), [...extracted2.ArrayBufferViews].sort()) &&
extracted1.object === extracted2.object &&
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.callbackFunction, extracted2.callbackFunction) &&
sameType(ctx, extracted1.dictionary, extracted2.dictionary) &&
JSON.stringify([...extracted1.interfaces].sort()) ===
JSON.stringify([...extracted2.interfaces].sort()) &&
extracted1.callbackInterface === extracted2.callbackInterface &&
sameArray([...extracted1.interfaces].sort(), [...extracted2.interfaces].sort()) &&
sameType(ctx, extracted1.callbackInterface, extracted2.callbackInterface) &&
extracted1.unknown === extracted2.unknown;
}

function sameTypeArray(ctx, types1, types2) {
return sameArray(types1, types2, (type1, type2) => sameType(ctx, type1, type2));
}

function sameArray(array1, array2, comparator = (x, y) => x === y) {
return array1.length === array2.length && array1.every((element1, index) => comparator(element1, array2[index]));
}

function areDistinguishable(ctx, type1, type2) {
const resolved1 = resolveType(ctx, type1);
const resolved2 = resolveType(ctx, type2);
Expand Down
Loading