Comparing version 7.2.0 to 7.3.0
@@ -19,3 +19,3 @@ "use strict"; | ||
let str = ""; | ||
const requires = {}; | ||
const requires = new utils.RequiresMap(this.ctx); | ||
@@ -32,2 +32,7 @@ const configurable = !utils.getExtAttr(this.idl.extAttrs, "Unforgeable"); | ||
} | ||
let brandCheck = ` | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
`; | ||
let getterBody = `return utils.tryWrapperForImpl(${objName}[impl]["${this.idl.name}"]);`; | ||
@@ -40,2 +45,3 @@ let setterBody = `${objName}[impl]["${this.idl.name}"] = V;`; | ||
if (this.idl.static) { | ||
brandCheck = ""; | ||
getterBody = `return Impl["${this.idl.name}"];`; | ||
@@ -52,2 +58,6 @@ setterBody = `Impl["${this.idl.name}"] = V;`; | ||
if (utils.getExtAttr(this.idl.extAttrs, "LenientThis")) { | ||
brandCheck = ""; | ||
} | ||
if (sameObject) { | ||
@@ -60,2 +70,3 @@ getterBody = `return utils.getSameObject(this, "${this.idl.name}", () => { ${getterBody} });`; | ||
get() { | ||
${brandCheck} | ||
${getterBody} | ||
@@ -68,5 +79,6 @@ }, | ||
`"Failed to set the '${this.idl.name}' property on '${this.interface.name}': The provided value"`); | ||
Object.assign(requires, conv.requires); | ||
requires.merge(conv.requires); | ||
str += ` | ||
set(V) { | ||
${brandCheck} | ||
${conv.body} | ||
@@ -79,2 +91,3 @@ ${setterBody} | ||
set(V) { | ||
${brandCheck} | ||
this.${this.idl.name}.${utils.getExtAttr(this.idl.extAttrs, "PutForwards").rhs.value} = V; | ||
@@ -86,2 +99,3 @@ }, | ||
set(V) { | ||
${brandCheck} | ||
Object.defineProperty(this, "${this.idl.name}", { | ||
@@ -88,0 +102,0 @@ configurable: true, |
"use strict"; | ||
const utils = require("../utils"); | ||
class Constant { | ||
@@ -25,3 +27,3 @@ constructor(ctx, obj, I, idl) { | ||
return { | ||
requires: {}, | ||
requires: new utils.RequiresMap(this.ctx), | ||
body | ||
@@ -28,0 +30,0 @@ }; |
@@ -12,3 +12,3 @@ "use strict"; | ||
this.requires = {}; | ||
this.requires = new utils.RequiresMap(ctx); | ||
} | ||
@@ -38,3 +38,3 @@ | ||
this.ctx, "value", typeConversion, argAttrs, this.name, `context + " has member ${field.name} that"`); | ||
Object.assign(this.requires, conv.requires); | ||
this.requires.merge(conv.requires); | ||
@@ -102,6 +102,6 @@ str += ` | ||
if (this.idl.inheritance) { | ||
this.requires[this.idl.inheritance] = `require("./${this.idl.inheritance}")`; | ||
this.requires.add(this.idl.inheritance); | ||
} | ||
this.str = ` | ||
${Object.keys(this.requires).map(key => `const ${key} = ${this.requires[key]};`).join("\n")} | ||
${this.requires.generate()} | ||
@@ -108,0 +108,0 @@ ${this.str} |
@@ -8,2 +8,3 @@ "use strict"; | ||
const Operation = require("./operation"); | ||
const Types = require("../types"); | ||
const Overloads = require("../overloads"); | ||
@@ -13,2 +14,12 @@ const Parameters = require("../parameters"); | ||
// Used as a sentinel in inheritedMembers to signify that the following members are inherited. | ||
const inherited = Symbol("inherited"); | ||
function isNamed(idl) { | ||
return idl.arguments[0].idlType.idlType === "DOMString"; | ||
} | ||
function isIndexed(idl) { | ||
return idl.arguments[0].idlType.idlType === "unsigned long"; | ||
} | ||
class Interface { | ||
@@ -21,12 +32,182 @@ constructor(ctx, idl, opts) { | ||
this.mixins = []; | ||
this.requires = {}; | ||
this.str = null; | ||
this.opts = opts; | ||
this.requires = new utils.RequiresMap(ctx); | ||
this.mixins = []; | ||
this.operations = new Map(); | ||
this.attributes = new Map(); | ||
this.constants = new Map(); | ||
this.indexedGetter = null; | ||
this.indexedSetter = null; | ||
this.namedGetter = null; | ||
this.namedSetter = null; | ||
this.namedDeleter = null; | ||
this.iterable = null; | ||
this._analyzed = false; | ||
} | ||
get hasPairIterator() { | ||
return this.idl.members.some(utils.isPairIterable); | ||
_analyzeMembers() { | ||
let definingInterface = null; | ||
for (const member of this.inheritedMembers()) { | ||
if (member[0] === inherited) { | ||
definingInterface = member[1]; | ||
continue; | ||
} | ||
if (!definingInterface) { | ||
switch (member.type) { | ||
case "operation": { | ||
let name = member.name; | ||
if (name === null && member.stringifier) { | ||
name = "toString"; | ||
} | ||
if (name !== null && !this.operations.has(name)) { | ||
this.operations.set(name, new Operation(this.ctx, this, this.idl, member)); | ||
} | ||
break; | ||
} | ||
case "attribute": | ||
this.attributes.set(member.name, new Attribute(this.ctx, this, this.idl, member)); | ||
break; | ||
case "const": | ||
this.constants.set(member.name, new Constant(this.ctx, this, this.idl, member)); | ||
break; | ||
case "iterable": | ||
if (this.iterable) { | ||
throw new Error(`Interface ${this.name} has more than one iterable declaration`); | ||
} | ||
this.iterable = new Iterable(this.ctx, this, this.idl, member); | ||
break; | ||
default: | ||
if (!this.ctx.options.suppressErrors) { | ||
throw new Error(`Unknown IDL member type "${member.type}" in interface ${this.name}`); | ||
} | ||
} | ||
} else { | ||
switch (member.type) { | ||
case "iterable": | ||
if (this.iterable) { | ||
throw new Error(`Iterable interface ${this.name} inherits from another iterable interface ` + | ||
`${definingInterface}`); | ||
} | ||
break; | ||
} | ||
} | ||
if (member.type === "operation") { | ||
if (member.getter) { | ||
let msg = `Invalid getter ${member.name ? `"${member.name}" ` : ""}on interface ${this.name}`; | ||
if (definingInterface) { | ||
msg += ` (defined in ${definingInterface})`; | ||
} | ||
msg += ": "; | ||
if (member.arguments.length < 1 || | ||
(!this.ctx.options.suppressErrors && member.arguments.length !== 1)) { | ||
throw new Error(msg + `1 argument should be present, found ${member.arguments.length}`); | ||
} | ||
if (isIndexed(member)) { | ||
if (!this.ctx.options.suppressErrors && this.indexedGetter) { | ||
throw new Error(msg + "duplicated indexed getter"); | ||
} | ||
this.indexedGetter = member; | ||
} else if (isNamed(member)) { | ||
if (!this.ctx.options.suppressErrors && this.namedGetter) { | ||
throw new Error(msg + "duplicated named getter"); | ||
} | ||
this.namedGetter = member; | ||
} else { | ||
throw new Error(msg + "getter is neither indexed nor named"); | ||
} | ||
} | ||
if (member.setter) { | ||
let msg = `Invalid setter ${member.name ? `"${member.name}" ` : ""}on interface ${this.name}`; | ||
if (definingInterface) { | ||
msg += ` (defined in ${definingInterface})`; | ||
} | ||
msg += ": "; | ||
if (member.arguments.length < 2 || | ||
(!this.ctx.options.suppressErrors && member.arguments.length !== 2)) { | ||
throw new Error(msg + `2 arguments should be present, found ${member.arguments.length}`); | ||
} | ||
if (isIndexed(member)) { | ||
if (!this.ctx.options.suppressErrors && this.indexedSetter) { | ||
throw new Error(msg + "duplicated indexed setter"); | ||
} | ||
this.indexedSetter = member; | ||
} else if (isNamed(member)) { | ||
if (!this.ctx.options.suppressErrors && this.namedSetter) { | ||
throw new Error(msg + "duplicated named setter"); | ||
} | ||
this.namedSetter = member; | ||
} else { | ||
throw new Error(msg + "setter is neither indexed nor named"); | ||
} | ||
} | ||
if (member.deleter) { | ||
let msg = `Invalid deleter ${member.name ? `"${member.name}" ` : ""}on interface ${this.name}`; | ||
if (definingInterface) { | ||
msg += ` (defined in ${definingInterface})`; | ||
} | ||
msg += ": "; | ||
if (member.arguments.length < 1 || | ||
(!this.ctx.options.suppressErrors && member.arguments.length !== 1)) { | ||
throw new Error(msg + `1 arguments should be present, found ${member.arguments.length}`); | ||
} | ||
if (isNamed(member)) { | ||
if (!this.ctx.options.suppressErrors && this.namedDeleter) { | ||
throw new Error(msg + "duplicated named deleter"); | ||
} | ||
this.namedDeleter = member; | ||
} else { | ||
throw new Error(msg + "deleter is not named"); | ||
} | ||
} | ||
} | ||
} | ||
const forbiddenMembers = new Set(); | ||
if (this.iterable) { | ||
if (this.iterable.isValue) { | ||
if (!this.supportsIndexedProperties) { | ||
throw new Error(`A value iterator cannot be declared on ${this.name} which does not support indexed ` + | ||
"properties"); | ||
} | ||
} else if (this.iterable.isPair && this.supportsIndexedProperties) { | ||
throw new Error(`A pair iterator cannot be declared on ${this.name} which supports indexed properties`); | ||
} | ||
for (const n of ["entries", "forEach", "keys", "values"]) { | ||
forbiddenMembers.add(n); | ||
} | ||
} | ||
definingInterface = null; | ||
for (const member of this.inheritedMembers()) { | ||
if (member[0] === inherited) { | ||
definingInterface = member[1]; | ||
continue; | ||
} | ||
if (forbiddenMembers.has(member.name)) { | ||
let msg = `${member.name} is forbidden in interface ${this.name}`; | ||
if (definingInterface) { | ||
msg += ` (defined in ${definingInterface})`; | ||
} | ||
throw new Error(msg); | ||
} | ||
} | ||
} | ||
get supportsIndexedProperties() { | ||
return this.indexedGetter !== null; | ||
} | ||
get supportsNamedProperties() { | ||
return this.namedGetter !== null; | ||
} | ||
get isLegacyPlatformObj() { | ||
return !utils.isGlobal(this.idl) && (this.supportsIndexedProperties || this.supportsNamedProperties); | ||
} | ||
implements(source) { | ||
@@ -37,3 +218,3 @@ this.mixins.push(source); | ||
generateIterator() { | ||
if (this.hasPairIterator) { | ||
if (this.iterable && this.iterable.isPair) { | ||
this.str += ` | ||
@@ -98,3 +279,3 @@ const IteratorPrototype = Object.create(utils.IteratorPrototype, { | ||
Parameters.generateOverloadConversions(this.ctx, overloads, this.name, `Failed to construct '${this.name}': `); | ||
Object.assign(this.requires, conversions.requires); | ||
this.requires.merge(conversions.requires); | ||
@@ -138,2 +319,6 @@ minConstructor.nameList = minConstructor.nameList.map(name => (keywords.has(name) ? "_" : "") + name); | ||
`; | ||
} else if (utils.getExtAttr(this.idl.extAttrs, "LegacyArrayClass")) { | ||
this.str += ` | ||
Object.setPrototypeOf(${this.name}.prototype, Array.prototype); | ||
`; | ||
} | ||
@@ -151,13 +336,23 @@ | ||
* inheritedMembers() { | ||
yield* this.idl.members; | ||
for (const iface of [...this.mixins, this.idl.inheritance]) { | ||
if (this.ctx.interfaces.has(iface)) { | ||
yield [inherited, iface]; | ||
yield* this.ctx.interfaces.get(iface).inheritedMembers(); | ||
} | ||
} | ||
} | ||
generateRequires() { | ||
this.requires.impl = "utils.implSymbol"; | ||
this.requires.addRaw("impl", "utils.implSymbol"); | ||
if (this.idl.inheritance !== null) { | ||
this.requires[this.idl.inheritance] = `require("./${this.idl.inheritance}.js")`; | ||
this.requires.add(this.idl.inheritance); | ||
} | ||
if (this.mixins.length !== 0) { | ||
this.requires.mixin = "utils.mixin"; | ||
this.requires.addRaw("mixin", "utils.mixin"); | ||
for (const mixin of this.mixins) { | ||
this.requires[mixin] = `require("./${mixin}.js")`; | ||
this.requires.add(mixin); | ||
} | ||
@@ -167,3 +362,3 @@ } | ||
this.str = ` | ||
${Object.keys(this.requires).map(key => `const ${key} = ${this.requires[key]};`).join("\n")} | ||
${this.requires.generate()} | ||
@@ -222,3 +417,3 @@ ${this.str} | ||
if (this.hasPairIterator) { | ||
if (this.iterable && this.iterable.isPair) { | ||
this.str += ` | ||
@@ -239,2 +434,523 @@ createDefaultIterator(target, kind) { | ||
generateLegacyProxy() { | ||
const hasIndexedSetter = this.indexedSetter !== null; | ||
const hasNamedSetter = this.namedSetter !== null; | ||
const hasNamedDeleter = this.namedDeleter !== null; | ||
const overrideBuiltins = Boolean(utils.getExtAttr(this.idl.extAttrs, "OverrideBuiltins")); | ||
const supportsPropertyIndex = (O, index, indexedValue) => { | ||
let unsupportedValue = utils.getExtAttr(this.indexedGetter.extAttrs, "WebIDL2JSValueAsUnsupported"); | ||
if (unsupportedValue) { | ||
unsupportedValue = unsupportedValue.rhs.value; | ||
} | ||
if (unsupportedValue) { | ||
const func = this.indexedGetter.name !== null ? `.${this.indexedGetter.name}` : "[utils.indexedGet]"; | ||
const value = indexedValue || `${O}[impl]${func}(${index})`; | ||
return `${value} !== ${unsupportedValue}`; | ||
} | ||
return `${O}[impl][utils.supportsPropertyIndex](${index})`; | ||
}; | ||
const supportsPropertyName = (O, P, namedValue) => { | ||
let unsupportedValue = utils.getExtAttr(this.namedGetter.extAttrs, "WebIDL2JSValueAsUnsupported"); | ||
if (unsupportedValue) { | ||
unsupportedValue = unsupportedValue.rhs.value; | ||
} | ||
if (unsupportedValue) { | ||
const func = this.namedGetter.name !== null ? `.${this.namedGetter.name}` : "[utils.namedGet]"; | ||
const value = namedValue || `${O}[impl]${func}(${P})`; | ||
return `${value} !== ${unsupportedValue}`; | ||
} | ||
return `${O}[impl][utils.supportsPropertyName](${P})`; | ||
}; | ||
// "named property visibility algorithm" | ||
// If `supports` is true then skip the supportsPropertyName check. | ||
function namedPropertyVisible(P, O, supports = false) { | ||
const conditions = []; | ||
if (!supports) { | ||
conditions.push(supportsPropertyName(O, P)); | ||
} | ||
if (overrideBuiltins) { | ||
conditions.push(`!Object.prototype.hasOwnProperty.call(${O}, ${P})`); | ||
} else { | ||
// TODO: create a named properties object. | ||
conditions.push(`!(${P} in ${O})`); | ||
} | ||
return conditions.join(" && "); | ||
} | ||
// "invoke an indexed property setter" | ||
const invokeIndexedSetter = (O, P, V) => { | ||
const arg = this.indexedSetter.arguments[1]; | ||
const conv = Types.generateTypeConversion( | ||
this.ctx, "indexedValue", arg.idlType, arg.extAttrs, this.name, | ||
`"Failed to set the " + index + " property on '${this.name}': The provided value"`); | ||
this.requires.merge(conv.requires); | ||
let str = ` | ||
const index = ${P} >>> 0; | ||
let indexedValue = ${V}; | ||
${conv.body} | ||
`; | ||
if (this.indexedSetter.name === null) { | ||
str += ` | ||
const creating = !(${supportsPropertyIndex(O, "index")}); | ||
if (creating) { | ||
${O}[impl][utils.indexedSetNew](index, indexedValue); | ||
} else { | ||
${O}[impl][utils.indexedSetExisting](index, indexedValue); | ||
} | ||
`; | ||
} else { | ||
str += ` | ||
${O}[impl].${this.indexedSetter.name}(index, indexedValue); | ||
`; | ||
} | ||
return str; | ||
}; | ||
// "invoke a named property setter" | ||
const invokeNamedSetter = (O, P, V) => { | ||
const arg = this.namedSetter.arguments[1]; | ||
const conv = Types.generateTypeConversion( | ||
this.ctx, "namedValue", arg.idlType, arg.extAttrs, this.name, | ||
`"Failed to set the '" + ${P} + "' property on '${this.name}': The provided value"`); | ||
this.requires.merge(conv.requires); | ||
let str = ` | ||
let namedValue = ${V}; | ||
${conv.body} | ||
`; | ||
if (this.namedSetter.name === null) { | ||
str += ` | ||
const creating = !(${supportsPropertyName(O, P)}); | ||
if (creating) { | ||
${O}[impl][utils.namedSetNew](${P}, namedValue); | ||
} else { | ||
${O}[impl][utils.namedSetExisting](${P}, namedValue); | ||
} | ||
`; | ||
} else { | ||
str += ` | ||
${O}[impl].${this.namedSetter.name}(${P}, namedValue); | ||
`; | ||
} | ||
return str; | ||
}; | ||
this.str += ` | ||
obj = new Proxy(obj, { | ||
`; | ||
// [[Get]] (necessary because of proxy semantics) | ||
this.str += ` | ||
get(target, P, receiver) { | ||
if (typeof P === "symbol") { | ||
return Reflect.get(target, P, receiver); | ||
} | ||
const desc = this.getOwnPropertyDescriptor(target, P); | ||
if (desc === undefined) { | ||
const parent = Object.getPrototypeOf(target); | ||
if (parent === null) { | ||
return undefined; | ||
} | ||
return Reflect.get(target, P, receiver); | ||
} | ||
if (!desc.get && !desc.set) { | ||
return desc.value; | ||
} | ||
const getter = desc.get; | ||
if (getter === undefined) { | ||
return undefined; | ||
} | ||
return Reflect.apply(getter, receiver, []); | ||
}, | ||
`; | ||
// [[HasProperty]] (necessary because of proxy semantics) | ||
this.str += ` | ||
has(target, P) { | ||
if (typeof P === "symbol") { | ||
return Reflect.has(target, P); | ||
} | ||
const desc = this.getOwnPropertyDescriptor(target, P); | ||
if (desc !== undefined) { | ||
return true; | ||
} | ||
const parent = Object.getPrototypeOf(target); | ||
if (parent !== null) { | ||
return Reflect.has(parent, P); | ||
} | ||
return false; | ||
}, | ||
`; | ||
// [[OwnPropertyKeys]] | ||
// Loosely defined by https://heycam.github.io/webidl/#legacy-platform-object-property-enumeration, but with finer | ||
// points tuned according to Firefox until https://github.com/heycam/webidl/issues/400 is resolved. | ||
this.str += ` | ||
ownKeys(target) { | ||
const keys = new Set(); | ||
`; | ||
if (this.supportsIndexedProperties) { | ||
this.str += ` | ||
for (const key of target[impl][utils.supportedPropertyIndices]) { | ||
keys.add(\`\${key}\`); | ||
} | ||
`; | ||
} | ||
if (this.supportsNamedProperties) { | ||
this.str += ` | ||
for (const key of target[impl][utils.supportedPropertyNames]) { | ||
if (${namedPropertyVisible("key", "target", true)}) { | ||
keys.add(\`\${key}\`); | ||
} | ||
} | ||
`; | ||
} | ||
this.str += ` | ||
for (const key of Reflect.ownKeys(target)) { | ||
keys.add(key); | ||
} | ||
return [...keys]; | ||
}, | ||
`; | ||
// [[GetOwnProperty]] | ||
this.str += ` | ||
getOwnPropertyDescriptor(target, P) { | ||
if (typeof P === "symbol") { | ||
return Reflect.getOwnPropertyDescriptor(target, P); | ||
} | ||
let ignoreNamedProps = false; | ||
`; | ||
if (this.supportsIndexedProperties) { | ||
this.str += ` | ||
if (utils.isArrayIndexPropName(P)) { | ||
const index = P >>> 0; | ||
`; | ||
const func = this.indexedGetter.name !== null ? `.${this.indexedGetter.name}` : "[utils.indexedGet]"; | ||
let preamble = ""; | ||
let condition; | ||
if (utils.getExtAttr(this.indexedGetter.extAttrs, "WebIDL2JSValueAsUnsupported")) { | ||
this.str += `const indexedValue = target[impl]${func}(index);`; | ||
condition = supportsPropertyIndex("target", "index", "indexedValue"); | ||
} else { | ||
preamble = `const indexedValue = target[impl]${func}(index);`; | ||
condition = supportsPropertyIndex("target", "index"); | ||
} | ||
this.str += ` | ||
if (${condition}) { | ||
${preamble} | ||
return { | ||
writable: ${hasIndexedSetter}, | ||
enumerable: true, | ||
configurable: true, | ||
value: utils.tryWrapperForImpl(indexedValue) | ||
}; | ||
} | ||
ignoreNamedProps = true; | ||
} | ||
`; | ||
} | ||
if (this.supportsNamedProperties) { | ||
const func = this.namedGetter.name !== null ? `.${this.namedGetter.name}` : "[utils.namedGet]"; | ||
const enumerable = !utils.getExtAttr(this.idl.extAttrs, "LegacyUnenumerableNamedProperties"); | ||
let preamble = ""; | ||
const conditions = []; | ||
if (utils.getExtAttr(this.namedGetter.extAttrs, "WebIDL2JSValueAsUnsupported")) { | ||
this.str += ` | ||
const namedValue = target[impl]${func}(P); | ||
`; | ||
conditions.push(supportsPropertyName("target", "index", "namedValue")); | ||
conditions.push(namedPropertyVisible("P", "target", true)); | ||
} else { | ||
preamble = ` | ||
const namedValue = target[impl]${func}(P); | ||
`; | ||
conditions.push(namedPropertyVisible("P", "target", false)); | ||
} | ||
conditions.push("!ignoreNamedProps"); | ||
this.str += ` | ||
if (${conditions.join(" && ")}) { | ||
${preamble} | ||
return { | ||
writable: ${hasNamedSetter}, | ||
enumerable: ${enumerable}, | ||
configurable: true, | ||
value: utils.tryWrapperForImpl(namedValue) | ||
}; | ||
} | ||
`; | ||
} | ||
this.str += ` | ||
return Reflect.getOwnPropertyDescriptor(target, P); | ||
}, | ||
`; | ||
// [[Set]] | ||
this.str += ` | ||
set(target, P, V, receiver) { | ||
if (typeof P === "symbol") { | ||
return Reflect.set(target, P, V, receiver); | ||
} | ||
if (target === receiver) { | ||
`; | ||
if (this.supportsIndexedProperties) { | ||
if (hasIndexedSetter) { | ||
this.str += ` | ||
if (utils.isArrayIndexPropName(P)) { | ||
${invokeIndexedSetter("target", "P", "V")} | ||
return true; | ||
} | ||
`; | ||
} else { | ||
// Side-effects | ||
this.str += ` | ||
utils.isArrayIndexPropName(P); | ||
`; | ||
} | ||
} | ||
if (this.supportsNamedProperties) { | ||
if (hasNamedSetter) { | ||
this.str += ` | ||
if (typeof P === "string" && !utils.isArrayIndexPropName(P)) { | ||
${invokeNamedSetter("target", "P", "V")} | ||
return true; | ||
} | ||
`; | ||
} else { | ||
// Side-effects | ||
this.str += ` | ||
typeof P === "string" && !utils.isArrayIndexPropName(P); | ||
`; | ||
} | ||
} | ||
this.str += ` | ||
} | ||
let ownDesc; | ||
`; | ||
if (this.supportsIndexedProperties) { | ||
this.str += ` | ||
if (utils.isArrayIndexPropName(P)) { | ||
const index = P >>> 0; | ||
`; | ||
const func = this.indexedGetter.name !== null ? `.${this.indexedGetter.name}` : "[utils.indexedGet]"; | ||
let preamble = ""; | ||
let condition; | ||
if (utils.getExtAttr(this.indexedGetter.extAttrs, "WebIDL2JSValueAsUnsupported")) { | ||
this.str += `const indexedValue = target[impl]${func}(index);`; | ||
condition = supportsPropertyIndex("target", "index", "indexedValue"); | ||
} else { | ||
preamble = `const indexedValue = target[impl]${func}(index);`; | ||
condition = supportsPropertyIndex("target", "index"); | ||
} | ||
this.str += ` | ||
if (${condition}) { | ||
${preamble} | ||
ownDesc = { | ||
writable: ${hasIndexedSetter}, | ||
enumerable: true, | ||
configurable: true, | ||
value: utils.tryWrapperForImpl(indexedValue) | ||
}; | ||
} | ||
} | ||
`; | ||
} | ||
this.str += ` | ||
if (ownDesc === undefined) { | ||
ownDesc = Reflect.getOwnPropertyDescriptor(target, P); | ||
} | ||
if (ownDesc === undefined) { | ||
const parent = Reflect.getPrototypeOf(target); | ||
if (parent !== null) { | ||
return Reflect.set(parent, P, V, receiver); | ||
} | ||
ownDesc = { writable: true, enumerable: true, configurable: true, value: undefined }; | ||
} | ||
if (!ownDesc.writable) { | ||
return false; | ||
} | ||
if (!utils.isObject(receiver)) { | ||
return false; | ||
} | ||
const existingDesc = Reflect.getOwnPropertyDescriptor(receiver, P); | ||
let valueDesc; | ||
if (existingDesc !== undefined) { | ||
if (existingDesc.get || existingDesc.set) { | ||
return false; | ||
} | ||
if (!existingDesc.writable) { | ||
return false; | ||
} | ||
valueDesc = { value: V }; | ||
} else { | ||
valueDesc = { writable: true, enumerable: true, configurable: true, value: V }; | ||
} | ||
return Reflect.defineProperty(receiver, P, valueDesc); | ||
}, | ||
`; | ||
// [[DefineOwnProperty]] | ||
this.str += ` | ||
defineProperty(target, P, desc) { | ||
if (typeof P === "symbol") { | ||
return Reflect.defineProperty(target, P, desc); | ||
} | ||
`; | ||
if (this.supportsIndexedProperties) { | ||
this.str += ` | ||
if (utils.isArrayIndexPropName(P)) { | ||
`; | ||
if (hasIndexedSetter) { | ||
this.str += ` | ||
if (desc.get || desc.set) { | ||
return false; | ||
} | ||
${invokeIndexedSetter("target", "P", "desc.value")} | ||
return true; | ||
`; | ||
} else { | ||
this.str += ` | ||
return false; | ||
`; | ||
} | ||
this.str += ` | ||
} | ||
`; | ||
} | ||
let needFallback = false; | ||
if (this.supportsNamedProperties && !utils.isGlobal(this.idl)) { | ||
const unforgeable = new Set(); | ||
for (const m of this.inheritedMembers()) { | ||
if (m[0] === inherited) { | ||
continue; | ||
} | ||
if ((m.type === "attribute" || m.type === "operation") && !m.static && | ||
utils.getExtAttr(m.extAttrs, "Unforgeable")) { | ||
unforgeable.add(m.name); | ||
} | ||
} | ||
if (unforgeable.size > 0) { | ||
needFallback = true; | ||
this.str += `if (!${JSON.stringify([...unforgeable])}.includes(P)) {`; | ||
} | ||
if (!overrideBuiltins) { | ||
needFallback = true; | ||
this.str += "if (!Object.prototype.hasOwnProperty.call(target, P)) {"; | ||
} | ||
if (!hasNamedSetter) { | ||
needFallback = true; | ||
this.str += ` | ||
const creating = !(${supportsPropertyName("target", "P")}); | ||
if (!creating) { | ||
return false; | ||
} | ||
`; | ||
} else { | ||
this.str += ` | ||
if (desc.get || desc.set) { | ||
return false; | ||
} | ||
${invokeNamedSetter("target", "P", "desc.value")} | ||
return true; | ||
`; | ||
} | ||
if (!overrideBuiltins) { | ||
this.str += "}"; | ||
} | ||
if (unforgeable.size > 0) { | ||
this.str += "}"; | ||
} | ||
} else { | ||
needFallback = true; | ||
} | ||
if (needFallback) { | ||
// Spec says to set configurable to true, but doing so will make Proxy's trap throw and also fail WPT. | ||
// if (!utils.isGlobal(this.idl)) { | ||
// this.str += ` | ||
// desc.configurable = true; | ||
// `; | ||
// } | ||
this.str += ` | ||
return Reflect.defineProperty(target, P, desc); | ||
`; | ||
} | ||
this.str += ` | ||
}, | ||
`; | ||
// [[Delete]] | ||
this.str += ` | ||
deleteProperty(target, P) { | ||
if (typeof P === "symbol") { | ||
return Reflect.deleteProperty(target, P); | ||
} | ||
`; | ||
if (this.supportsIndexedProperties) { | ||
this.str += ` | ||
if (utils.isArrayIndexPropName(P)) { | ||
const index = P >>> 0; | ||
return !(${supportsPropertyIndex("target", "index")}); | ||
} | ||
`; | ||
} | ||
if (this.supportsNamedProperties && !utils.isGlobal(this.idl)) { | ||
this.str += ` | ||
if (${namedPropertyVisible("P", "target")}) { | ||
`; | ||
if (!hasNamedDeleter) { | ||
this.str += ` | ||
return false; | ||
`; | ||
} else { | ||
const func = this.namedDeleter.name !== null ? `.${this.namedDeleter.name}` : "[utils.namedDelete]"; | ||
if (this.namedDeleter.idlType.idlType === "bool") { | ||
this.str += ` | ||
return target[impl]${func}(P); | ||
`; | ||
} else { | ||
this.str += ` | ||
target[impl]${func}(P); | ||
return true; | ||
`; | ||
} | ||
} | ||
this.str += ` | ||
} | ||
`; | ||
} | ||
this.str += ` | ||
return Reflect.deleteProperty(target, P); | ||
}, | ||
`; | ||
// TODO: Implement [[Call]] / legacycallers. | ||
// [[PreventExtensions]] | ||
this.str += ` | ||
preventExtensions() { | ||
return false; | ||
} | ||
`; | ||
this.str += ` | ||
}); | ||
`; | ||
} | ||
generateIface() { | ||
@@ -267,8 +983,6 @@ const shouldExposeRoot = !utils.getExtAttr(this.idl.extAttrs, "NoInterfaceObject"); | ||
// since we don't have spread arg calls, we can't do new Interface(...arguments) yet | ||
// add initialized symbol as to not destroy the object shape and cause deopts | ||
this.str += ` | ||
create(constructorArgs, privateData) { | ||
let obj = Object.create(${this.name}.prototype); | ||
this.setup(obj, constructorArgs, privateData); | ||
obj = this.setup(obj, constructorArgs, privateData); | ||
return obj; | ||
@@ -278,3 +992,3 @@ }, | ||
let obj = Object.create(${this.name}.prototype); | ||
this.setup(obj, constructorArgs, privateData); | ||
obj = this.setup(obj, constructorArgs, privateData); | ||
return utils.implForWrapper(obj); | ||
@@ -291,21 +1005,7 @@ }, | ||
for (let i = 0; i < this.idl.members.length; ++i) { | ||
const memberIdl = this.idl.members[i]; | ||
if (utils.isOnInstance(memberIdl, this.idl)) { | ||
let member; | ||
switch (memberIdl.type) { | ||
case "operation": { | ||
member = new Operation(this.ctx, this, this.idl, memberIdl); | ||
break; | ||
} | ||
case "attribute": { | ||
member = new Attribute(this.ctx, this, this.idl, memberIdl); | ||
break; | ||
} | ||
default: { | ||
throw new Error("Cannot handle on-instance members that are not operations or attributes"); | ||
} | ||
} | ||
this.str += member.generate().body; | ||
for (const member of [...this.operations.values(), ...this.attributes.values()]) { | ||
if (utils.isOnInstance(member.idl, this.idl)) { | ||
const data = member.generate(); | ||
this.requires.merge(data.requires); | ||
this.str += data.body; | ||
} | ||
@@ -340,2 +1040,9 @@ } | ||
}); | ||
`; | ||
if (this.isLegacyPlatformObj) { | ||
this.generateLegacyProxy(); | ||
} | ||
this.str += ` | ||
obj[impl][utils.wrapperSymbol] = obj; | ||
@@ -345,2 +1052,3 @@ if (Impl.init) { | ||
} | ||
return obj; | ||
}, | ||
@@ -355,62 +1063,91 @@ interface: ${this.name}, | ||
generateOperations() { | ||
const done = {}; | ||
for (let i = 0; i < this.idl.members.length; ++i) { | ||
const memberIdl = this.idl.members[i]; | ||
let member = null; | ||
// TODO maplike setlike | ||
// Don't bother checking "length" attribute as interfaces that support indexed properties must implement one. | ||
// "Has value iterator" implies "supports indexed properties". | ||
if (this.supportsIndexedProperties || this.iterable && this.iterable.isPair) { | ||
let expr; | ||
switch (memberIdl.type) { | ||
case "operation": | ||
if (utils.isOnInstance(memberIdl, this.idl)) { | ||
break; | ||
if (this.supportsIndexedProperties) { | ||
expr = "Array.prototype[Symbol.iterator]"; | ||
} else { | ||
expr = ` | ||
function entries() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return module.exports.createDefaultIterator(this, "key+value"); | ||
} | ||
member = new Operation(this.ctx, this, this.idl, memberIdl); | ||
if (done[member.name]) { | ||
continue; | ||
} | ||
done[member.name] = true; | ||
break; | ||
case "iterable": | ||
member = new Iterable(this.ctx, this, this.idl, memberIdl); | ||
break; | ||
default: | ||
// throw new Error("Can't handle member of type '" + memberIdl.type + "'"); | ||
break; | ||
`; | ||
} | ||
if (member !== null) { | ||
const data = member.generate(); | ||
Object.assign(this.requires, data.requires); | ||
this.str += data.body; | ||
} | ||
this.str += ` | ||
Object.defineProperty(${this.name}.prototype, Symbol.iterator, { | ||
writable: true, | ||
enumerable: false, | ||
configurable: true, | ||
value: ${expr} | ||
}); | ||
`; | ||
} | ||
} | ||
generateAttributes() { | ||
for (let i = 0; i < this.idl.members.length; ++i) { | ||
const memberIdl = this.idl.members[i]; | ||
let member = null; | ||
if (this.iterable) { | ||
let expr; | ||
switch (memberIdl.type) { | ||
case "attribute": | ||
if (utils.isOnInstance(memberIdl, this.idl)) { | ||
break; | ||
if (this.iterable.isValue) { | ||
expr = "Array.prototype.forEach"; | ||
} else { | ||
expr = ` | ||
function forEach(callback) { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
if (arguments.length < 1) { | ||
throw new TypeError("Failed to execute 'forEach' on '${this.name}': 1 argument required, " + | ||
"but only 0 present."); | ||
} | ||
if (typeof callback !== "function") { | ||
throw new TypeError("Failed to execute 'forEach' on '${this.name}': The callback provided " + | ||
"as parameter 1 is not a function."); | ||
} | ||
const thisArg = arguments[1]; | ||
let pairs = Array.from(this[impl]); | ||
let i = 0; | ||
while (i < pairs.length) { | ||
const [key, value] = pairs[i].map(utils.tryWrapperForImpl); | ||
callback.call(thisArg, value, key, this); | ||
pairs = Array.from(this[impl]); | ||
i++; | ||
} | ||
} | ||
member = new Attribute(this.ctx, this, this.idl, memberIdl); | ||
break; | ||
case "const": | ||
member = new Constant(this.ctx, this, this.idl, memberIdl); | ||
break; | ||
default: | ||
// throw new Error("Can't handle member of type '" + memberIdl.type + "'"); | ||
break; | ||
`; | ||
} | ||
if (member !== null) { | ||
this.str += `${this.name}.prototype.forEach = ${expr};`; | ||
} | ||
for (const member of this.operations.values()) { | ||
if (!utils.isOnInstance(member.idl, this.idl)) { | ||
const data = member.generate(); | ||
Object.assign(this.requires, data.requires); | ||
this.requires.merge(data.requires); | ||
this.str += data.body; | ||
} | ||
} | ||
if (this.iterable) { | ||
const data = this.iterable.generate(); | ||
this.requires.merge(data.requires); | ||
this.str += data.body; | ||
} | ||
} | ||
generateAttributes() { | ||
for (const member of [...this.attributes.values(), ...this.constants.values()]) { | ||
if (member instanceof Attribute && utils.isOnInstance(member.idl, this.idl)) { | ||
continue; | ||
} | ||
const data = member.generate(); | ||
this.requires.merge(data.requires); | ||
this.str += data.body; | ||
} | ||
} | ||
generateSymbols() { | ||
@@ -460,4 +1197,2 @@ const unscopables = Object.create(null); | ||
this.generateRequires(); | ||
this.generateSymbols(); | ||
@@ -488,2 +1223,4 @@ | ||
} | ||
this.generateRequires(); | ||
} | ||
@@ -493,2 +1230,6 @@ | ||
this.str = ""; | ||
if (!this._analyzed) { | ||
this._analyzed = true; | ||
this._analyzeMembers(); | ||
} | ||
this.generate(); | ||
@@ -495,0 +1236,0 @@ return this.str; |
@@ -15,2 +15,10 @@ "use strict"; | ||
get isValue() { | ||
return !Array.isArray(this.idl.idlType); | ||
} | ||
get isPair() { | ||
return Array.isArray(this.idl.idlType) && this.idl.idlType.length === 2; | ||
} | ||
generateFunction(key, kind, keyExpr, fnName) { | ||
@@ -38,40 +46,20 @@ if (fnName === undefined) { | ||
generate() { | ||
const isPairIterator = utils.isPairIterable(this.idl); | ||
let str = ""; | ||
if (isPairIterator) { | ||
if (this.isPair) { | ||
str += ` | ||
${this.generateFunction(Symbol.iterator, "key+value", "Symbol.iterator", "entries")} | ||
${this.obj.name}.prototype.entries = ${this.obj.name}.prototype[Symbol.iterator]; | ||
${this.generateFunction("keys", "key")} | ||
${this.generateFunction("values", "value")} | ||
${this.obj.name}.prototype.forEach = function forEach(callback) { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
if (arguments.length < 1) { | ||
throw new TypeError("Failed to execute 'forEach' on '${this.obj.name}': 1 argument required, " + | ||
"but only 0 present."); | ||
} | ||
if (typeof callback !== "function") { | ||
throw new TypeError("Failed to execute 'forEach' on '${this.obj.name}': The callback provided " + | ||
"as parameter 1 is not a function."); | ||
} | ||
const thisArg = arguments[1]; | ||
let pairs = Array.from(this[impl]); | ||
let i = 0; | ||
while (i < pairs.length) { | ||
const [key, value] = pairs[i].map(utils.tryWrapperForImpl); | ||
callback.call(thisArg, value, key, this); | ||
pairs = Array.from(this[impl]); | ||
i++; | ||
} | ||
}; | ||
`; | ||
} else { | ||
// value iterator; WIP | ||
str += ` | ||
${this.obj.name}.prototype.entries = Array.prototype.entries; | ||
${this.obj.name}.prototype.keys = Array.prototype.keys; | ||
${this.obj.name}.prototype.values = Array.prototype.values; | ||
`; | ||
} | ||
return { | ||
requires: {}, | ||
requires: new utils.RequiresMap(this.ctx), | ||
body: str | ||
@@ -78,0 +66,0 @@ }; |
@@ -20,3 +20,3 @@ "use strict"; | ||
generate() { | ||
const requires = {}; | ||
const requires = new utils.RequiresMap(this.ctx); | ||
let str = ""; | ||
@@ -29,3 +29,3 @@ | ||
} else { | ||
return { requires: {}, body: "" }; | ||
return { requires, body: "" }; | ||
} | ||
@@ -77,3 +77,3 @@ } | ||
const argsSpread = parameterConversions.hasArgs ? "...args" : ""; | ||
Object.assign(requires, parameterConversions.requires); | ||
requires.merge(parameterConversions.requires); | ||
str += parameterConversions.body; | ||
@@ -80,0 +80,0 @@ |
@@ -14,4 +14,5 @@ "use strict"; | ||
class Context { | ||
constructor({ implSuffix = "" } = {}) { | ||
constructor({ implSuffix = "", options } = {}) { | ||
this.implSuffix = implSuffix; | ||
this.options = options; | ||
this.initialize(); | ||
@@ -18,0 +19,0 @@ } |
@@ -72,2 +72,29 @@ "use strict"; | ||
function isArrayIndexPropName(P) { | ||
if (typeof P !== "string") { | ||
return false; | ||
} | ||
const i = P >>> 0; | ||
if (i === Math.pow(2, 32) - 1) { | ||
return false; | ||
} | ||
const s = `${i}`; | ||
if (P !== s) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
const supportsPropertyIndex = Symbol("supports property index"); | ||
const supportedPropertyIndices = Symbol("supported property indices"); | ||
const supportsPropertyName = Symbol("supports property name"); | ||
const supportedPropertyNames = Symbol("supported property names"); | ||
const indexedGet = Symbol("indexed property get"); | ||
const indexedSetNew = Symbol("indexed property set new"); | ||
const indexedSetExisting = Symbol("indexed property set existing"); | ||
const namedGet = Symbol("named property get"); | ||
const namedSetNew = Symbol("named property set new"); | ||
const namedSetExisting = Symbol("named property set existing"); | ||
const namedDelete = Symbol("named property delete"); | ||
module.exports = exports = { | ||
@@ -86,3 +113,15 @@ isObject, | ||
iterInternalSymbol, | ||
IteratorPrototype | ||
IteratorPrototype, | ||
isArrayIndexPropName, | ||
supportsPropertyIndex, | ||
supportedPropertyIndices, | ||
supportsPropertyName, | ||
supportedPropertyNames, | ||
indexedGet, | ||
indexedSetNew, | ||
indexedSetExisting, | ||
namedGet, | ||
namedSetNew, | ||
namedSetExisting, | ||
namedDelete | ||
}; |
@@ -10,3 +10,3 @@ "use strict"; | ||
const { customTypes } = ctx; | ||
const requires = {}; | ||
const requires = new utils.RequiresMap(ctx); | ||
let str = ""; | ||
@@ -24,3 +24,3 @@ const idlType = conversion.type; | ||
const conv = Types.generateTypeConversion(ctx, name, idlType, argAttrs, ...typeArgs); | ||
Object.assign(requires, conv.requires); | ||
requires.merge(conv.requires); | ||
str += conv.body; | ||
@@ -46,3 +46,3 @@ | ||
module.exports.generateOverloadConversions = function (ctx, overloads, parentName, errPrefix) { | ||
const requires = {}; | ||
const requires = new utils.RequiresMap(ctx); | ||
let str = ``; | ||
@@ -85,3 +85,3 @@ let maxConstructor = overloads[0]; | ||
`"${errPrefix}parameter ${i + 1}"`); | ||
Object.assign(requires, conv.requires); | ||
requires.merge(conv.requires); | ||
str += conv.body; | ||
@@ -88,0 +88,0 @@ } |
@@ -18,9 +18,10 @@ "use strict"; | ||
this.ctx = new Context({ | ||
implSuffix: opts.implSuffix | ||
implSuffix: opts.implSuffix, | ||
options: { | ||
suppressErrors: Boolean(opts.suppressErrors) | ||
} | ||
}); | ||
this.options = Object.assign({ | ||
suppressErrors: false | ||
}, opts); | ||
this.sources = []; | ||
this.utilPath = null; | ||
} | ||
@@ -116,3 +117,3 @@ | ||
default: | ||
if (!this.options.suppressErrors) { | ||
if (!this.ctx.options.suppressErrors) { | ||
throw new Error("Can't convert type '" + instruction.type + "'"); | ||
@@ -135,3 +136,3 @@ } | ||
if (this.options.suppressErrors && !interfaces.has(instruction.name)) { | ||
if (this.ctx.options.suppressErrors && !interfaces.has(instruction.name)) { | ||
break; | ||
@@ -148,3 +149,3 @@ } | ||
} | ||
if (this.options.suppressErrors && !dictionaries.has(instruction.name)) { | ||
if (this.ctx.options.suppressErrors && !dictionaries.has(instruction.name)) { | ||
break; | ||
@@ -158,3 +159,3 @@ } | ||
case "implements": | ||
if (this.options.suppressErrors && !interfaces.has(instruction.target)) { | ||
if (this.ctx.options.suppressErrors && !interfaces.has(instruction.target)) { | ||
break; | ||
@@ -171,3 +172,3 @@ } | ||
const utilsText = yield fs.readFile(path.resolve(__dirname, "output/utils.js")); | ||
yield fs.writeFile(this.options.utilPath, utilsText); | ||
yield fs.writeFile(this.utilPath, utilsText); | ||
@@ -185,3 +186,3 @@ const { interfaces, dictionaries } = this.ctx; | ||
let relativeUtils = path.relative(outputDir, this.options.utilPath).replace(/\\/g, "/"); | ||
let relativeUtils = path.relative(outputDir, this.utilPath).replace(/\\/g, "/"); | ||
if (relativeUtils[0] !== ".") { | ||
@@ -208,3 +209,3 @@ relativeUtils = "./" + relativeUtils; | ||
let relativeUtils = path.relative(outputDir, this.options.utilPath).replace(/\\/g, "/"); | ||
let relativeUtils = path.relative(outputDir, this.utilPath).replace(/\\/g, "/"); | ||
if (relativeUtils[0] !== ".") { | ||
@@ -235,4 +236,4 @@ relativeUtils = "./" + relativeUtils; | ||
generate(outputDir) { | ||
if (!this.options.utilPath) { | ||
this.options.utilPath = path.join(outputDir, "utils.js"); | ||
if (!this.utilPath) { | ||
this.utilPath = path.join(outputDir, "utils.js"); | ||
} | ||
@@ -239,0 +240,0 @@ |
@@ -78,3 +78,3 @@ "use strict"; | ||
const { customTypes } = ctx; | ||
const requires = {}; | ||
const requires = new utils.RequiresMap(ctx); | ||
let str = ""; | ||
@@ -117,3 +117,3 @@ | ||
fn = `convert${idlType.idlType}`; | ||
requires[fn] = `require("./${idlType.idlType}").convert`; | ||
requires.add(idlType.idlType, "convert"); | ||
} else { | ||
@@ -152,3 +152,3 @@ fn = `module.exports.convert`; | ||
const conv = generateTypeConversion(ctx, name, union.dictionary, [], parentName, errPrefix); | ||
Object.assign(requires, conv.requires); | ||
requires.merge(conv.requires); | ||
output.push(` | ||
@@ -171,3 +171,3 @@ if (${name} === null || ${name} === undefined) { | ||
fn = `is${iface}`; | ||
requires[fn] = `require("./${iface}").is`; | ||
requires.add(iface, "is"); | ||
} else { | ||
@@ -215,3 +215,3 @@ fn = "module.exports.is"; | ||
`${errPrefix} + " sequence"`); | ||
Object.assign(requires, conv.requires); | ||
requires.merge(conv.requires); | ||
code += conv.body; | ||
@@ -225,3 +225,3 @@ code += `} else {`; | ||
`${errPrefix} + " ${prop}"`); | ||
Object.assign(requires, conv.requires); | ||
requires.merge(conv.requires); | ||
code += conv.body; | ||
@@ -275,3 +275,3 @@ } else if (union.object) { | ||
`${errPrefix} + "'s element"`); | ||
Object.assign(requires, conv.requires); | ||
requires.merge(conv.requires); | ||
@@ -296,6 +296,6 @@ str += ` | ||
`${errPrefix} + "'s key"`); | ||
Object.assign(requires, keyConv.requires); | ||
requires.merge(keyConv.requires); | ||
const valConv = generateTypeConversion(ctx, "typedValue", idlType.idlType[1], [], parentName, | ||
`${errPrefix} + "'s value"`); | ||
Object.assign(requires, valConv.requires); | ||
requires.merge(valConv.requires); | ||
@@ -330,3 +330,3 @@ str += ` | ||
`${errPrefix} + " promise value"`); | ||
Object.assign(requires, conv.requires); | ||
requires.merge(conv.requires); | ||
handler = ` | ||
@@ -333,0 +333,0 @@ ${conv.body} |
@@ -35,6 +35,2 @@ "use strict"; | ||
function isPairIterable(idl) { | ||
return idl.type === "iterable" && Array.isArray(idl.idlType) && idl.idlType.length === 2; | ||
} | ||
function isOnInstance(memberIDL, interfaceIDL) { | ||
@@ -44,2 +40,38 @@ return getExtAttr(memberIDL.extAttrs, "Unforgeable") || isGlobal(interfaceIDL); | ||
class RequiresMap extends Map { | ||
constructor(ctx) { | ||
super(); | ||
this.ctx = ctx; | ||
} | ||
add(type, func = "") { | ||
const key = func + type; | ||
let req = `require("./${type}.js")`; | ||
if (func) { | ||
req += `.${func}`; | ||
} | ||
this.addRaw(key, req); | ||
} | ||
addRaw(key, expr) { | ||
if (this.has(key) && this.get(key) !== expr) { | ||
throw new Error(`Internal error: Variable name clash: ${key}; was ${this.get(key)}, adding: ${expr}`); | ||
} | ||
super.set(key, expr); | ||
} | ||
merge(src) { | ||
if (!src || !(src instanceof RequiresMap)) { | ||
return; | ||
} | ||
for (const [key, val] of src) { | ||
this.addRaw(key, val); | ||
} | ||
} | ||
generate() { | ||
return [...this.keys()].map(key => `const ${key} = ${this.get(key)};`).join("\n"); | ||
} | ||
} | ||
module.exports = { | ||
@@ -49,4 +81,4 @@ getDefault, | ||
isGlobal, | ||
isPairIterable, | ||
isOnInstance | ||
isOnInstance, | ||
RequiresMap | ||
}; |
{ | ||
"name": "webidl2js", | ||
"version": "7.2.0", | ||
"version": "7.3.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
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
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
Found 1 instance in 1 package
116044
22
2881
0
354