Comparing version 7.0.1 to 7.1.0
"use strict"; | ||
const webidl = require("webidl2"); | ||
const Typedef = require("./constructs/typedef"); | ||
const builtinTypedefs = webidl.parse(` | ||
typedef (Int8Array or Int16Array or Int32Array or | ||
Uint8Array or Uint16Array or Uint32Array or Uint8ClampedArray or | ||
Float32Array or Float64Array or DataView) ArrayBufferView; | ||
typedef (ArrayBufferView or ArrayBuffer) BufferSource; | ||
typedef unsigned long long DOMTimeStamp; | ||
`); | ||
class Context { | ||
@@ -11,4 +21,9 @@ constructor({ implSuffix = "" } = {}) { | ||
this.customTypes = new Map(); | ||
this.typedefs = new Map(); | ||
this.interfaces = Object.create(null); | ||
this.dictionaries = Object.create(null); | ||
for (const typedef of builtinTypedefs) { | ||
this.typedefs.set(typedef.name, new Typedef(this, typedef)); | ||
} | ||
} | ||
@@ -15,0 +30,0 @@ } |
"use strict"; | ||
module.exports.mixin = function mixin(target, source) { | ||
// Returns "Type(value) is Object" in ES terminology. | ||
function isObject(value) { | ||
return typeof value === "object" && value !== null || typeof value === "function"; | ||
} | ||
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)); | ||
} | ||
function mixin(target, source) { | ||
const keys = Object.getOwnPropertyNames(source); | ||
@@ -12,3 +32,3 @@ for (let i = 0; i < keys.length; ++i) { | ||
} | ||
}; | ||
} | ||
@@ -52,10 +72,16 @@ const wrapperSymbol = Symbol("wrapper"); | ||
module.exports.wrapperSymbol = wrapperSymbol; | ||
module.exports.implSymbol = implSymbol; | ||
module.exports.getSameObject = getSameObject; | ||
module.exports.wrapperForImpl = wrapperForImpl; | ||
module.exports.implForWrapper = implForWrapper; | ||
module.exports.tryWrapperForImpl = tryWrapperForImpl; | ||
module.exports.tryImplForWrapper = tryImplForWrapper; | ||
module.exports.iterInternalSymbol = iterInternalSymbol; | ||
module.exports.IteratorPrototype = IteratorPrototype; | ||
module.exports = exports = { | ||
isObject, | ||
getReferenceToBytes, | ||
getCopyToBytes, | ||
mixin, | ||
wrapperSymbol, | ||
implSymbol, | ||
getSameObject, | ||
wrapperForImpl, | ||
implForWrapper, | ||
tryWrapperForImpl, | ||
tryImplForWrapper, | ||
iterInternalSymbol, | ||
IteratorPrototype | ||
}; |
@@ -11,2 +11,3 @@ "use strict"; | ||
const Context = require("./context"); | ||
const Typedef = require("./constructs/typedef"); | ||
const Interface = require("./constructs/interface"); | ||
@@ -81,3 +82,3 @@ const Dictionary = require("./constructs/dictionary"); | ||
this.ctx.initialize(); | ||
const { interfaces, dictionaries, customTypes } = this.ctx; | ||
const { interfaces, dictionaries, typedefs, customTypes } = this.ctx; | ||
@@ -111,2 +112,6 @@ // first we're gathering all full interfaces and ignore partial ones | ||
break; | ||
case "typedef": | ||
obj = new Typedef(this.ctx, instruction); | ||
typedefs.set(obj.name, obj); | ||
break; | ||
default: | ||
@@ -113,0 +118,0 @@ if (!this.options.suppressErrors) { |
350
lib/types.js
@@ -7,3 +7,60 @@ "use strict"; | ||
function generateTypeConversion(ctx, name, idlType, argAttrs, parentName, errPrefix = '"The provided value"') { | ||
function mergeExtAttrs(a = [], b = []) { | ||
return [...a, ...b]; | ||
} | ||
function resolveType(ctx, idlType, stack = []) { | ||
idlType = deepClone(idlType); | ||
const { customTypes, typedefs } = ctx; | ||
if (idlType.union) { | ||
const types = []; | ||
for (let type of idlType.idlType) { | ||
type = resolveType(ctx, type, stack); | ||
idlType.nullable = idlType.nullable || type.nullable; | ||
// Only the outermost union is nullable | ||
type.nullable = false; | ||
if (type.union) { | ||
types.push(...type.idlType); | ||
} else { | ||
types.push(type); | ||
} | ||
} | ||
for (const type of types) { | ||
type.extAttrs = deepClone(mergeExtAttrs(type.extAttrs, idlType.extAttrs)); | ||
} | ||
idlType.idlType = types; | ||
return idlType; | ||
} else if (idlType.generic === "sequence" || idlType.generic === "FrozenArray" || idlType.generic === "Promise") { | ||
idlType.idlType = resolveType(ctx, idlType.idlType, stack); | ||
return idlType; | ||
} else if (idlType.generic === "record") { | ||
idlType.idlType = idlType.idlType.map(t => resolveType(ctx, t, stack)); | ||
return idlType; | ||
} else if (customTypes.has(idlType.idlType)) { | ||
// already resolved | ||
return idlType; | ||
} else if (typedefs.has(idlType.idlType)) { | ||
const out = deepClone(typedefs.get(idlType.idlType).resolve(stack)); | ||
out.nullable = out.nullable || idlType.nullable; | ||
out.extAttrs = deepClone(mergeExtAttrs(out.extAttrs, idlType.extAttrs)); | ||
if (out.union) { | ||
for (const type of out.idlType) { | ||
type.extAttrs = deepClone(mergeExtAttrs(type.extAttrs, idlType.extAttrs)); | ||
} | ||
} | ||
return out; | ||
} else if (conversions[idlType.idlType]) { | ||
// already resolved | ||
return idlType; | ||
} else { | ||
// unknown | ||
return idlType; | ||
} | ||
} | ||
function deepClone(obj) { | ||
return JSON.parse(JSON.stringify(obj)); | ||
} | ||
function generateTypeConversion(ctx, name, idlType, argAttrs = [], parentName, errPrefix = '"The provided value"') { | ||
const { customTypes } = ctx; | ||
@@ -13,2 +70,5 @@ const requires = {}; | ||
idlType = resolveType(ctx, idlType); | ||
const extAttrs = idlType.extAttrs !== undefined ? [...idlType.extAttrs, ...argAttrs] : argAttrs; | ||
if (idlType.nullable) { | ||
@@ -21,3 +81,6 @@ str += ` | ||
if (idlType.generic === "sequence") { | ||
if (idlType.union) { | ||
// union type | ||
generateUnion(); | ||
} else if (idlType.generic === "sequence") { | ||
// sequence type | ||
@@ -65,5 +128,124 @@ generateSequence(); | ||
function generateUnion() { | ||
const union = extractUnionInfo(ctx, idlType, errPrefix); | ||
const output = []; | ||
if (union.unknown) { | ||
// Oh well, what do we know... | ||
str += `${name} = utils.tryImplForWrapper(${name});`; | ||
return; | ||
} | ||
if (!idlType.nullable && union.dictionary) { | ||
let str = `if (${name} === null || ${name} === undefined) {` | ||
const conv = generateTypeConversion(ctx, name, union.dictionary, [], parentName, errPrefix); | ||
Object.assign(requires, conv.requires); | ||
str += conv.body; | ||
str += "}"; | ||
output.push(str); | ||
} | ||
if (union.object) { | ||
output.push(`if (utils.isObject(${name}) && ${name}[utils.implSymbol]) { | ||
${name} = utils.implForWrapper(${name}); | ||
}`); | ||
} else if (union.interfaces.size > 0) { | ||
const exprs = [...union.interfaces].map(iface => { | ||
let fn; | ||
// Avoid requiring the interface itself | ||
if (iface !== parentName) { | ||
fn = `is${iface}`; | ||
requires[fn] = `require("./${iface}").is`; | ||
} else { | ||
fn = "module.exports.is"; | ||
} | ||
return `${fn}(${name})`; | ||
}); | ||
output.push(`if (${exprs.join(" || ")}) { | ||
${name} = utils.implForWrapper(${name}); | ||
}`); | ||
} | ||
// Handle Error and DOMException the same way | ||
if (union.exception || union.object) { | ||
output.push(`if (${name} instanceof Error) {}`); | ||
} | ||
// 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 (${name} instanceof ArrayBuffer) {}`); | ||
} | ||
if (union.ArrayBufferViews.size > 0 || union.object) { | ||
let condition = `ArrayBuffer.isView(${name})`; | ||
// Skip specific type check if all ArrayBufferView member types are allowed. | ||
if (union.ArrayBufferViews.size !== arrayBufferViewTypes.size) { | ||
const exprs = [...union.ArrayBufferViews].map(a => `${name} instanceof ${a}`); | ||
condition += ` && (${exprs.join(" || ")})`; | ||
} | ||
output.push(`if (${condition}) {}`); | ||
} | ||
if (union.callback || union.object) { | ||
output.push(`if (typeof ${name} === "function") {}`); | ||
} | ||
if (union.sequenceLike || union.dictionary || union.record || union.object) { | ||
let str = `if (utils.isObject(${name})) {`; | ||
if (union.sequenceLike) { | ||
str += `if (${name}[Symbol.iterator] !== undefined) {`; | ||
const conv = generateTypeConversion(ctx, name, union.sequenceLike, [], parentName, `${errPrefix} + " sequence"`); | ||
Object.assign(requires, conv.requires); | ||
str += conv.body; | ||
str += `} else {`; | ||
} | ||
if (union.dictionary || union.record) { | ||
const prop = union.dictionary ? "dictionary" : "record"; | ||
const conv = generateTypeConversion(ctx, name, union[prop], [], parentName, `${errPrefix} + " ${prop}"`); | ||
Object.assign(requires, conv.requires); | ||
str += conv.body; | ||
} else if (union.object) { | ||
// noop | ||
} | ||
if (union.sequenceLike) { | ||
str += "}"; | ||
} | ||
str += "}"; | ||
output.push(str); | ||
} | ||
if (union.boolean) { | ||
output.push(`if (typeof ${name} === "boolean") { | ||
${generateTypeConversion(ctx, name, union.boolean, [], parentName, errPrefix).body} | ||
}`); | ||
} | ||
if (union.numeric) { | ||
output.push(`if (typeof ${name} === "number") { | ||
${generateTypeConversion(ctx, name, union.numeric, [], parentName, errPrefix).body} | ||
}`); | ||
} | ||
{ | ||
let str = "{"; | ||
const type = union.string || union.numeric || union.boolean; | ||
if (type) { | ||
str += generateTypeConversion(ctx, name, type, [], parentName, errPrefix).body; | ||
} else { | ||
str += `throw new TypeError(${errPrefix} + " is not of any supported type.")`; | ||
} | ||
str += "}"; | ||
output.push(str); | ||
} | ||
str += output.join(" else "); | ||
} | ||
function generateSequence() { | ||
str += ` | ||
if (${name} === null || typeof ${name} !== "object" && typeof ${name} !== "function") { | ||
if (!utils.isObject(${name})) { | ||
throw new TypeError(${errPrefix} + " is not an iterable object."); | ||
@@ -88,3 +270,3 @@ } else { | ||
str += ` | ||
if (${name} === null || typeof ${name} !== "object" && typeof ${name} !== "function") { | ||
if (!utils.isObject(${name})) { | ||
throw new TypeError(${errPrefix} + " is not an object."); | ||
@@ -136,5 +318,5 @@ } else { | ||
function generateGeneric(conversionFn) { | ||
const enforceRange = utils.getExtAttr(argAttrs, "EnforceRange"); | ||
const clamp = utils.getExtAttr(argAttrs, "Clamp"); | ||
const treatNullAs = utils.getExtAttr(argAttrs, "TreatNullAs"); | ||
const enforceRange = utils.getExtAttr(extAttrs, "EnforceRange"); | ||
const clamp = utils.getExtAttr(extAttrs, "Clamp"); | ||
const treatNullAs = utils.getExtAttr(extAttrs, "TreatNullAs"); | ||
@@ -144,5 +326,7 @@ let optString = `, { context: ${errPrefix}`; | ||
optString += ", clamp: true"; | ||
} else if (enforceRange) { | ||
} | ||
if (enforceRange) { | ||
optString += ", enforceRange: true"; | ||
} else if (treatNullAs && treatNullAs.rhs.value === "EmptyString") { | ||
} | ||
if (treatNullAs && treatNullAs.rhs.value === "EmptyString") { | ||
optString += ", treatNullAsEmptyString: true"; | ||
@@ -163,4 +347,150 @@ } | ||
const arrayBufferViewTypes = new Set([ | ||
"Int8Array", "Int16Array", "Int32Array", "Uint8Array", "Uint16Array", "Uint32Array", | ||
"Uint8ClampedArray", "Float32Array", "Float64Array", "DataView" | ||
]); | ||
const stringTypes = new Set(["DOMString", "ByteString", "USVString"]); | ||
const integerTypes = new Set([ | ||
"byte", "octet", "short", "unsigned short", "long", "unsigned long", | ||
"long long", "unsigned long long" | ||
]); | ||
const numericTypes = new Set([ | ||
...integerTypes, "float", "unrestricted float", "double", "unrestricted double" | ||
]); | ||
// Condense the member types of a union to a more consumable structured object. At the same time, check for the validity | ||
// of the union type (no forbidden types, no indistinguishable member types). Duplicated types are allowed for now | ||
// though. | ||
function extractUnionInfo(ctx, idlType, errPrefix) { | ||
const { customTypes } = ctx; | ||
const seen = { | ||
sequenceLike: null, | ||
record: null, | ||
get dictionaryLike() { | ||
return this.dictionary !== null || this.record !== null; | ||
}, | ||
ArrayBuffer: false, | ||
ArrayBufferViews: new Set(), | ||
get BufferSource() { | ||
return this.ArrayBuffer || this.ArrayBufferViews.size > 0; | ||
}, | ||
object: false, | ||
exception: null, | ||
string: null, | ||
numeric: null, | ||
boolean: null, | ||
// Callback function, not interface | ||
callback: false, | ||
dictionary: null, | ||
interfaces: new Set(), | ||
get interfaceLike() { | ||
return this.interfaces.size > 0 || this.exception !== null || this.BufferSource; | ||
}, | ||
unknown: false | ||
}; | ||
for (const item of idlType.idlType) { | ||
if (item.generic === "sequence" || item.generic === "FrozenArray") { | ||
if (seen.sequenceLike) { | ||
error("There can only be one sequence-like type in a union type"); | ||
} | ||
seen.sequenceLike = item; | ||
} else if (item.generic === "record") { | ||
if (seen.record || seen.dictionary) { | ||
error("There can only be one dictionary-like type in a union type"); | ||
} | ||
seen.record = item; | ||
} else if (item.generic === "Promise") { | ||
error("Promise types are not supported in union types"); | ||
} else if (item.generic) { | ||
error(`Unknown generic type ${item.generic}`); | ||
} else if (item.idlType === "any") { | ||
error("any type is not allowed in a union type"); | ||
} else if (item.idlType === "ArrayBuffer") { | ||
if (seen.object) { | ||
error("ArrayBuffer is not distinguishable with object type"); | ||
} | ||
seen.ArrayBuffer = true; | ||
} else if (arrayBufferViewTypes.has(item.idlType)) { | ||
if (seen.object) { | ||
error(`${item.idlType} is not distinguishable with object type`); | ||
} | ||
seen.ArrayBufferViews.add(item.idlType); | ||
} else if (stringTypes.has(item.idlType)) { | ||
if (seen.string) { | ||
error("There can only be one string type in a union type"); | ||
} | ||
seen.string = item; | ||
} else if (numericTypes.has(item.idlType)) { | ||
if (seen.numeric) { | ||
error("There can only be one numeric type in a union type"); | ||
} | ||
seen.numeric = item; | ||
} else if (item.idlType === "object") { | ||
if (seen.interfaceLike) { | ||
error("Object type is not distinguishable with interface-like types"); | ||
} | ||
if (seen.callback) { | ||
error("Object type is not distinguishable with callback functions"); | ||
} | ||
if (seen.dictionaryLike) { | ||
error("Object type is not distinguishable with dictionary-like types"); | ||
} | ||
if (seen.sequenceLike) { | ||
error("Object type is not distinguishable with sequence-like types"); | ||
} | ||
seen.object = true; | ||
} else if (item.idlType === "DOMException" || item.idlType === "Error") { | ||
if (seen.object) { | ||
error("Exception types are not distinguishable with object type"); | ||
} | ||
if (seen.exception && seen.exception.idlType !== item.idlType) { | ||
error("DOMException is not distinguishable with Error type"); | ||
} | ||
seen.exception = item; | ||
} else if (item.idlType === "boolean") { | ||
seen.boolean = item; | ||
} else if (item.idlType === "Function") { | ||
// TODO: add full support for callback functions | ||
if (seen.object) { | ||
error("Callback functions are not distinguishable with object type"); | ||
} | ||
if (seen.dictionaryLike) { | ||
error("Callback functions are not distinguishable with dictionary-like types"); | ||
} | ||
seen.callback = true; | ||
} else if (customTypes.has(item.idlType)) { | ||
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 { | ||
seen.unknown = true; | ||
} | ||
} | ||
return seen; | ||
function error(msg) { | ||
throw new Error(`${msg}\n When compiling "${eval(errPrefix)}"`); | ||
} | ||
} | ||
module.exports = { | ||
generateTypeConversion | ||
generateTypeConversion, | ||
resolveType | ||
}; |
{ | ||
"name": "webidl2js", | ||
"version": "7.0.1", | ||
"version": "7.1.0", | ||
"description": "Auto-generates class structures for WebIDL specifications", | ||
@@ -5,0 +5,0 @@ "main": "lib/transformer.js", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
60069
20
1769
1