Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Use WeakMap for wrapper to impl conversion #155

Closed
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
6 changes: 3 additions & 3 deletions lib/constructs/attribute.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ class Attribute {
throw new TypeError("Illegal invocation");
}
`;
let getterBody = `return utils.tryWrapperForImpl(${objName}[impl]["${this.idl.name}"]);`;
let setterBody = `${objName}[impl]["${this.idl.name}"] = V;`;
let getterBody = `return utils.tryWrapperForImpl(implForWrapper(${objName})["${this.idl.name}"]);`;
let setterBody = `implForWrapper(${objName})["${this.idl.name}"] = V;`;
if (conversions[this.idl.idlType.idlType]) {
getterBody = `return ${objName}[impl]["${this.idl.name}"];`;
getterBody = `return implForWrapper(${objName})["${this.idl.name}"];`;
}

const addMethod = this.static ?
Expand Down
61 changes: 30 additions & 31 deletions lib/constructs/interface.js
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ class Interface {
value: function next() {
const internal = this[utils.iterInternalSymbol];
const { target, kind, index } = internal;
const values = Array.from(target[impl]);
const values = Array.from(implForWrapper(target));
const len = values.length;
if (index >= len) {
return { value: undefined, done: true };
Expand Down Expand Up @@ -498,7 +498,7 @@ class Interface {
}

generateRequires() {
this.requires.addRaw("impl", "utils.implSymbol");
this.requires.addRaw("implForWrapper", "utils.implForWrapper");
this.requires.addRaw("ctorRegistry", "utils.ctorRegistrySymbol");

if (this.idl.inheritance !== null) {
Expand Down Expand Up @@ -534,7 +534,8 @@ class Interface {
exports._mixedIntoPredicates = [];
exports.is = function is(obj) {
if (obj) {
if (utils.hasOwn(obj, impl) && obj[impl] instanceof Impl.implementation) {
const impl = implForWrapper(obj);
if (impl !== null && impl instanceof Impl.implementation) {
return true;
}
for (const isMixedInto of exports._mixedIntoPredicates) {
Expand Down Expand Up @@ -562,7 +563,7 @@ class Interface {
};
exports.convert = function convert(obj, { context = "The provided value" } = {}) {
if (exports.is(obj)) {
return utils.implForWrapper(obj);
return implForWrapper(obj);
}
throw new TypeError(\`\${context} is not of type '${this.name}'.\`);
};
Expand Down Expand Up @@ -595,10 +596,10 @@ class Interface {
}
if (unsupportedValue) {
const func = this.indexedGetter.name ? `.${this.indexedGetter.name}` : "[utils.indexedGet]";
const value = indexedValue || `${O}[impl]${func}(${index})`;
const value = indexedValue || `implForWrapper(${O})${func}(${index})`;
return `${value} !== ${unsupportedValue}`;
}
return `${O}[impl][utils.supportsPropertyIndex](${index})`;
return `implForWrapper(${O})[utils.supportsPropertyIndex](${index})`;
};

const supportsPropertyName = (O, P, namedValue) => {
Expand All @@ -608,10 +609,10 @@ class Interface {
}
if (unsupportedValue) {
const func = this.namedGetter.name ? `.${this.namedGetter.name}` : "[utils.namedGet]";
const value = namedValue || `${O}[impl]${func}(${P})`;
const value = namedValue || `implForWrapper(${O})${func}(${P})`;
return `${value} !== ${unsupportedValue}`;
}
return `${O}[impl][utils.supportsPropertyName](${P})`;
return `implForWrapper(${O})[utils.supportsPropertyName](${P})`;
};

// "named property visibility algorithm"
Expand Down Expand Up @@ -648,14 +649,14 @@ class Interface {
str += `
const creating = !(${supportsPropertyIndex(O, "index")});
if (creating) {
${O}[impl][utils.indexedSetNew](index, indexedValue);
implForWrapper(${O})[utils.indexedSetNew](index, indexedValue);
} else {
${O}[impl][utils.indexedSetExisting](index, indexedValue);
implForWrapper(${O})[utils.indexedSetExisting](index, indexedValue);
}
`;
} else {
str += `
${O}[impl].${this.indexedSetter.name}(index, indexedValue);
implForWrapper(${O}).${this.indexedSetter.name}(index, indexedValue);
`;
}

Expand All @@ -679,14 +680,14 @@ class Interface {
str += `
const creating = !(${supportsPropertyName(O, P)});
if (creating) {
${O}[impl][utils.namedSetNew](${P}, namedValue);
implForWrapper(${O})[utils.namedSetNew](${P}, namedValue);
} else {
${O}[impl][utils.namedSetExisting](${P}, namedValue);
implForWrapper(${O})[utils.namedSetExisting](${P}, namedValue);
}
`;
} else {
str += `
${O}[impl].${this.namedSetter.name}(${P}, namedValue);
implForWrapper(${O}).${this.namedSetter.name}(${P}, namedValue);
`;
}

Expand Down Expand Up @@ -749,14 +750,14 @@ class Interface {
`;
if (this.supportsIndexedProperties) {
this.str += `
for (const key of target[impl][utils.supportedPropertyIndices]) {
for (const key of implForWrapper(target)[utils.supportedPropertyIndices]) {
keys.add(\`\${key}\`);
}
`;
}
if (this.supportsNamedProperties) {
this.str += `
for (const key of target[impl][utils.supportedPropertyNames]) {
for (const key of implForWrapper(target)[utils.supportedPropertyNames]) {
if (${namedPropertyVisible("key", "target", true)}) {
keys.add(\`\${key}\`);
}
Expand Down Expand Up @@ -789,10 +790,10 @@ class Interface {
let preamble = "";
let condition;
if (utils.getExtAttr(this.indexedGetter.extAttrs, "WebIDL2JSValueAsUnsupported")) {
this.str += `const indexedValue = target[impl]${func}(index);`;
this.str += `const indexedValue = implForWrapper(target)${func}(index);`;
condition = supportsPropertyIndex("target", "index", "indexedValue");
} else {
preamble = `const indexedValue = target[impl]${func}(index);`;
preamble = `const indexedValue = implForWrapper(target)${func}(index);`;
condition = supportsPropertyIndex("target", "index");
}

Expand All @@ -817,13 +818,13 @@ class Interface {
const conditions = [];
if (utils.getExtAttr(this.namedGetter.extAttrs, "WebIDL2JSValueAsUnsupported")) {
this.str += `
const namedValue = target[impl]${func}(P);
const namedValue = implForWrapper(target)${func}(P);
`;
conditions.push(supportsPropertyName("target", "index", "namedValue"));
conditions.push(namedPropertyVisible("P", "target", true));
} else {
preamble = `
const namedValue = target[impl]${func}(P);
const namedValue = implForWrapper(target)${func}(P);
`;
conditions.push(namedPropertyVisible("P", "target", false));
}
Expand Down Expand Up @@ -899,10 +900,10 @@ class Interface {
let preamble = "";
let condition;
if (utils.getExtAttr(this.indexedGetter.extAttrs, "WebIDL2JSValueAsUnsupported")) {
this.str += `const indexedValue = target[impl]${func}(index);`;
this.str += `const indexedValue = implForWrapper(target)${func}(index);`;
condition = supportsPropertyIndex("target", "index", "indexedValue");
} else {
preamble = `const indexedValue = target[impl]${func}(index);`;
preamble = `const indexedValue = implForWrapper(target)${func}(index);`;
condition = supportsPropertyIndex("target", "index");
}

Expand Down Expand Up @@ -1068,11 +1069,11 @@ class Interface {
const func = this.namedDeleter.name ? `.${this.namedDeleter.name}` : "[utils.namedDelete]";
if (this.namedDeleter.idlType.idlType === "bool") {
this.str += `
return target[impl]${func}(P);
return implForWrapper(target)${func}(P);
`;
} else {
this.str += `
target[impl]${func}(P);
implForWrapper(target)${func}(P);
return true;
`;
}
Expand All @@ -1095,6 +1096,7 @@ class Interface {

this.str += `
});
utils.initWrapperImplMapping(obj, impl);
`;
}

Expand All @@ -1116,7 +1118,7 @@ class Interface {
};
exports.createImpl = function createImpl(globalObject, constructorArgs, privateData) {
const obj = exports.create(globalObject, constructorArgs, privateData);
return utils.implForWrapper(obj);
return implForWrapper(obj);
};
exports._internalSetup = function _internalSetup(obj) {
`;
Expand All @@ -1135,20 +1137,17 @@ class Interface {
privateData.wrapper = obj;

exports._internalSetup(obj);
Object.defineProperty(obj, impl, {
value: new Impl.implementation(globalObject, constructorArgs, privateData),
configurable: true
});
const impl = new Impl.implementation(globalObject, constructorArgs, privateData);
utils.initWrapperImplMapping(obj, impl);
`;

if (this.isLegacyPlatformObj) {
this.generateLegacyProxy();
}

this.str += `
obj[impl][utils.wrapperSymbol] = obj;
if (Impl.init) {
Impl.init(obj[impl], privateData);
Impl.init(impl, privateData);
}
return obj;
};
Expand Down
4 changes: 2 additions & 2 deletions lib/constructs/iterable.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ class Iterable {
"as parameter 1 is not a function.");
}
const thisArg = arguments[1];
let pairs = Array.from(this[impl]);
let pairs = Array.from(implForWrapper(this));
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]);
pairs = Array.from(implForWrapper(this));
i++;
}
`);
Expand Down
2 changes: 1 addition & 1 deletion lib/constructs/operation.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class Operation {
`;
}

const callOn = this.static ? "Impl.implementation" : "this[impl]";
const callOn = this.static ? "Impl.implementation" : "implForWrapper(this)";
// In case of stringifiers, use the named implementation function rather than hardcoded "toString".
// All overloads will have the same name, so pick the first one.
const implFunc = this.idls[0].name || this.name;
Expand Down
15 changes: 12 additions & 3 deletions lib/output/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ function hasOwn(obj, prop) {
}

const wrapperSymbol = Symbol("wrapper");
const implSymbol = Symbol("impl");
const implForWrapperMap = new WeakMap();
const sameObjectCaches = Symbol("SameObject caches");
const ctorRegistrySymbol = Symbol.for("[webidl2js] constructor registry");

Expand All @@ -27,12 +27,21 @@ function getSameObject(wrapper, prop, creator) {
return wrapper[sameObjectCaches][prop];
}

function initWrapperImplMapping(wrapper, impl) {
if (implForWrapperMap.has(wrapper)) {
throw new Error("Internal error: wrapper->impl is already registered");
}
implForWrapperMap.set(wrapper, impl);
Object.defineProperty(impl, wrapperSymbol, { value: wrapper, configurable: true });
}

function wrapperForImpl(impl) {
return impl ? impl[wrapperSymbol] : null;
}

function implForWrapper(wrapper) {
return wrapper ? wrapper[implSymbol] : null;
const impl = implForWrapperMap.get(wrapper);
return impl !== undefined ? impl : null;
}

function tryWrapperForImpl(impl) {
Expand Down Expand Up @@ -90,9 +99,9 @@ module.exports = exports = {
isObject,
hasOwn,
wrapperSymbol,
implSymbol,
getSameObject,
ctorRegistrySymbol,
initWrapperImplMapping,
wrapperForImpl,
implForWrapper,
tryWrapperForImpl,
Expand Down
18 changes: 9 additions & 9 deletions lib/reflector.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

module.exports.boolean = {
get(objName, attrName) {
return `return this[impl].hasAttributeNS(null, "${attrName}");`;
return `return implForWrapper(this).hasAttributeNS(null, "${attrName}");`;
},
set(objName, attrName) {
return `
if (V) {
this[impl].setAttributeNS(null, "${attrName}", "");
implForWrapper(this).setAttributeNS(null, "${attrName}", "");
} else {
this[impl].removeAttributeNS(null, "${attrName}");
implForWrapper(this).removeAttributeNS(null, "${attrName}");
}
`;
}
Expand All @@ -18,35 +18,35 @@ module.exports.boolean = {
module.exports.DOMString = {
get(objName, attrName) {
return `
const value = this[impl].getAttributeNS(null, "${attrName}");
const value = implForWrapper(this).getAttributeNS(null, "${attrName}");
return value === null ? "" : value;
`;
},
set(objName, attrName) {
return `this[impl].setAttributeNS(null, "${attrName}", V);`;
return `implForWrapper(this).setAttributeNS(null, "${attrName}", V);`;
}
};

module.exports.long = {
get(objName, attrName) {
return `
const value = parseInt(this[impl].getAttributeNS(null, "${attrName}"));
const value = parseInt(implForWrapper(this).getAttributeNS(null, "${attrName}"));
return isNaN(value) || value < -2147483648 || value > 2147483647 ? 0 : value
`;
},
set(objName, attrName) {
return `this[impl].setAttributeNS(null, "${attrName}", String(V));`;
return `implForWrapper(this).setAttributeNS(null, "${attrName}", String(V));`;
}
};

module.exports["unsigned long"] = {
get(objName, attrName) {
return `
const value = parseInt(this[impl].getAttributeNS(null, "${attrName}"));
const value = parseInt(implForWrapper(this).getAttributeNS(null, "${attrName}"));
return isNaN(value) || value < 0 || value > 2147483647 ? 0 : value
`;
},
set(objName, attrName) {
return `this[impl].setAttributeNS(null, "${attrName}", String(V > 2147483647 ? 0 : V));`;
return `implForWrapper(this).setAttributeNS(null, "${attrName}", String(V > 2147483647 ? 0 : V));`;
}
};
2 changes: 1 addition & 1 deletion lib/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ function generateTypeConversion(ctx, name, idlType, argAttrs = [], parentName, e
}

if (union.object) {
output.push(`if (utils.isObject(${name}) && ${name}[utils.implSymbol]) {
output.push(`if (utils.isObject(${name}) && utils.implForWrapper(${name})) {
${name} = utils.implForWrapper(${name});
}`);
} else if (union.interfaces.size > 0) {
Expand Down
Loading