Skip to content

Commit bb4a763

Browse files
committed
Make wrapper instanciation lazy
1 parent 7c8037f commit bb4a763

File tree

6 files changed

+1360
-1108
lines changed

6 files changed

+1360
-1108
lines changed

lib/constructs/attribute.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ class Attribute {
3232
throw new TypeError("Illegal invocation");
3333
}
3434
`;
35-
let getterBody = `return utils.tryWrapperForImpl(${objName}[impl]["${this.idl.name}"]);`;
36-
let setterBody = `${objName}[impl]["${this.idl.name}"] = V;`;
35+
let getterBody = `return utils.tryWrapperForImpl(${objName}[implSymbol]["${this.idl.name}"]);`;
36+
let setterBody = `${objName}[implSymbol]["${this.idl.name}"] = V;`;
3737
if (conversions[this.idl.idlType.idlType]) {
38-
getterBody = `return ${objName}[impl]["${this.idl.name}"];`;
38+
getterBody = `return ${objName}[implSymbol]["${this.idl.name}"];`;
3939
}
4040

4141
const addMethod = this.static ?

lib/constructs/interface.js

Lines changed: 64 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ class Interface {
404404
value: function next() {
405405
const internal = this[utils.iterInternalSymbol];
406406
const { target, kind, index } = internal;
407-
const values = Array.from(target[impl]);
407+
const values = Array.from(target[implSymbol]);
408408
const len = values.length;
409409
if (index >= len) {
410410
return { value: undefined, done: true };
@@ -498,8 +498,11 @@ class Interface {
498498
}
499499

500500
generateRequires() {
501-
this.requires.addRaw("impl", "utils.implSymbol");
502-
this.requires.addRaw("ctorRegistry", "utils.ctorRegistrySymbol");
501+
this.requires.addRaw("implSymbol", "utils.implSymbol");
502+
this.requires.addRaw("wrapperSymbol", "utils.wrapperSymbol");
503+
this.requires.addRaw("ctorRegistrySymbol", "utils.ctorRegistrySymbol");
504+
this.requires.addRaw("globalObjectSymbol", "utils.globalObjectSymbol");
505+
this.requires.addRaw("createWrapperSymbol", "utils.createWrapperSymbol");
503506

504507
if (this.idl.inheritance !== null) {
505508
this.requires.add(this.idl.inheritance);
@@ -534,7 +537,7 @@ class Interface {
534537
exports._mixedIntoPredicates = [];
535538
exports.is = function is(obj) {
536539
if (obj) {
537-
if (utils.hasOwn(obj, impl) && obj[impl] instanceof Impl.implementation) {
540+
if (utils.hasOwn(obj, implSymbol) && obj[implSymbol] instanceof Impl.implementation) {
538541
return true;
539542
}
540543
for (const isMixedInto of exports._mixedIntoPredicates) {
@@ -595,10 +598,10 @@ class Interface {
595598
}
596599
if (unsupportedValue) {
597600
const func = this.indexedGetter.name ? `.${this.indexedGetter.name}` : "[utils.indexedGet]";
598-
const value = indexedValue || `${O}[impl]${func}(${index})`;
601+
const value = indexedValue || `${O}[implSymbol]${func}(${index})`;
599602
return `${value} !== ${unsupportedValue}`;
600603
}
601-
return `${O}[impl][utils.supportsPropertyIndex](${index})`;
604+
return `${O}[implSymbol][utils.supportsPropertyIndex](${index})`;
602605
};
603606

604607
const supportsPropertyName = (O, P, namedValue) => {
@@ -608,10 +611,10 @@ class Interface {
608611
}
609612
if (unsupportedValue) {
610613
const func = this.namedGetter.name ? `.${this.namedGetter.name}` : "[utils.namedGet]";
611-
const value = namedValue || `${O}[impl]${func}(${P})`;
614+
const value = namedValue || `${O}[implSymbol]${func}(${P})`;
612615
return `${value} !== ${unsupportedValue}`;
613616
}
614-
return `${O}[impl][utils.supportsPropertyName](${P})`;
617+
return `${O}[implSymbol][utils.supportsPropertyName](${P})`;
615618
};
616619

617620
// "named property visibility algorithm"
@@ -648,14 +651,14 @@ class Interface {
648651
str += `
649652
const creating = !(${supportsPropertyIndex(O, "index")});
650653
if (creating) {
651-
${O}[impl][utils.indexedSetNew](index, indexedValue);
654+
${O}[implSymbol][utils.indexedSetNew](index, indexedValue);
652655
} else {
653-
${O}[impl][utils.indexedSetExisting](index, indexedValue);
656+
${O}[implSymbol][utils.indexedSetExisting](index, indexedValue);
654657
}
655658
`;
656659
} else {
657660
str += `
658-
${O}[impl].${this.indexedSetter.name}(index, indexedValue);
661+
${O}[implSymbol].${this.indexedSetter.name}(index, indexedValue);
659662
`;
660663
}
661664

@@ -679,14 +682,14 @@ class Interface {
679682
str += `
680683
const creating = !(${supportsPropertyName(O, P)});
681684
if (creating) {
682-
${O}[impl][utils.namedSetNew](${P}, namedValue);
685+
${O}[implSymbol][utils.namedSetNew](${P}, namedValue);
683686
} else {
684-
${O}[impl][utils.namedSetExisting](${P}, namedValue);
687+
${O}[implSymbol][utils.namedSetExisting](${P}, namedValue);
685688
}
686689
`;
687690
} else {
688691
str += `
689-
${O}[impl].${this.namedSetter.name}(${P}, namedValue);
692+
${O}[implSymbol].${this.namedSetter.name}(${P}, namedValue);
690693
`;
691694
}
692695

@@ -749,14 +752,14 @@ class Interface {
749752
`;
750753
if (this.supportsIndexedProperties) {
751754
this.str += `
752-
for (const key of target[impl][utils.supportedPropertyIndices]) {
755+
for (const key of target[implSymbol][utils.supportedPropertyIndices]) {
753756
keys.add(\`\${key}\`);
754757
}
755758
`;
756759
}
757760
if (this.supportsNamedProperties) {
758761
this.str += `
759-
for (const key of target[impl][utils.supportedPropertyNames]) {
762+
for (const key of target[implSymbol][utils.supportedPropertyNames]) {
760763
if (${namedPropertyVisible("key", "target", true)}) {
761764
keys.add(\`\${key}\`);
762765
}
@@ -789,10 +792,10 @@ class Interface {
789792
let preamble = "";
790793
let condition;
791794
if (utils.getExtAttr(this.indexedGetter.extAttrs, "WebIDL2JSValueAsUnsupported")) {
792-
this.str += `const indexedValue = target[impl]${func}(index);`;
795+
this.str += `const indexedValue = target[implSymbol]${func}(index);`;
793796
condition = supportsPropertyIndex("target", "index", "indexedValue");
794797
} else {
795-
preamble = `const indexedValue = target[impl]${func}(index);`;
798+
preamble = `const indexedValue = target[implSymbol]${func}(index);`;
796799
condition = supportsPropertyIndex("target", "index");
797800
}
798801

@@ -817,13 +820,13 @@ class Interface {
817820
const conditions = [];
818821
if (utils.getExtAttr(this.namedGetter.extAttrs, "WebIDL2JSValueAsUnsupported")) {
819822
this.str += `
820-
const namedValue = target[impl]${func}(P);
823+
const namedValue = target[implSymbol]${func}(P);
821824
`;
822825
conditions.push(supportsPropertyName("target", "index", "namedValue"));
823826
conditions.push(namedPropertyVisible("P", "target", true));
824827
} else {
825828
preamble = `
826-
const namedValue = target[impl]${func}(P);
829+
const namedValue = target[implSymbol]${func}(P);
827830
`;
828831
conditions.push(namedPropertyVisible("P", "target", false));
829832
}
@@ -899,10 +902,10 @@ class Interface {
899902
let preamble = "";
900903
let condition;
901904
if (utils.getExtAttr(this.indexedGetter.extAttrs, "WebIDL2JSValueAsUnsupported")) {
902-
this.str += `const indexedValue = target[impl]${func}(index);`;
905+
this.str += `const indexedValue = target[implSymbol]${func}(index);`;
903906
condition = supportsPropertyIndex("target", "index", "indexedValue");
904907
} else {
905-
preamble = `const indexedValue = target[impl]${func}(index);`;
908+
preamble = `const indexedValue = target[implSymbol]${func}(index);`;
906909
condition = supportsPropertyIndex("target", "index");
907910
}
908911

@@ -1068,11 +1071,11 @@ class Interface {
10681071
const func = this.namedDeleter.name ? `.${this.namedDeleter.name}` : "[utils.namedDelete]";
10691072
if (this.namedDeleter.idlType.idlType === "bool") {
10701073
this.str += `
1071-
return target[impl]${func}(P);
1074+
return target[implSymbol]${func}(P);
10721075
`;
10731076
} else {
10741077
this.str += `
1075-
target[impl]${func}(P);
1078+
target[implSymbol]${func}(P);
10761079
return true;
10771080
`;
10781081
}
@@ -1100,59 +1103,63 @@ class Interface {
11001103

11011104
generateIface() {
11021105
this.str += `
1103-
exports.create = function create(globalObject, constructorArgs, privateData) {
1104-
if (globalObject[ctorRegistry] === undefined) {
1106+
function createWrapper(impl) {
1107+
const globalObject = impl[globalObjectSymbol];
1108+
1109+
if (globalObject[ctorRegistrySymbol] === undefined) {
11051110
throw new Error('Internal error: invalid global object');
11061111
}
11071112
1108-
const ctor = globalObject[ctorRegistry]["${this.name}"];
1113+
const ctor = globalObject[ctorRegistrySymbol]["${this.name}"];
11091114
if (ctor === undefined) {
11101115
throw new Error('Internal error: constructor ${this.name} is not installed on the passed global object');
11111116
}
11121117
1113-
let obj = Object.create(ctor.prototype);
1114-
obj = exports.setup(obj, globalObject, constructorArgs, privateData);
1115-
return obj;
1116-
};
1117-
exports.createImpl = function createImpl(globalObject, constructorArgs, privateData) {
1118-
const obj = exports.create(globalObject, constructorArgs, privateData);
1119-
return utils.implForWrapper(obj);
1120-
};
1121-
exports._internalSetup = function _internalSetup(obj) {
1118+
let wrapper = Object.create(ctor.prototype);
1119+
exports._internalSetup(wrapper);
11221120
`;
11231121

1124-
if (this.idl.inheritance) {
1122+
if (this.isLegacyPlatformObj) {
11251123
this.str += `
1126-
${this.idl.inheritance}._internalSetup(obj);
1124+
wrapper = new Proxy(wrapper, proxyHandler);
11271125
`;
11281126
}
11291127

1130-
this.generateOnInstance();
1131-
11321128
this.str += `
1129+
impl[wrapperSymbol] = wrapper;
1130+
wrapper[implSymbol] = impl;
1131+
return wrapper;
1132+
};
1133+
exports.create = function create(globalObject, constructorArgs, privateData) {
1134+
const impl = exports.createImpl(globalObject, constructorArgs, privateData);
1135+
return utils.wrapperForImpl(impl);
11331136
};
1134-
exports.setup = function setup(obj, globalObject, constructorArgs = [], privateData = {}) {
1135-
privateData.wrapper = obj;
1137+
exports.createImpl = function createImpl(globalObject, constructorArgs = [], privateData = {}) {
1138+
const impl = new Impl.implementation(globalObject, constructorArgs, privateData);
11361139
1137-
exports._internalSetup(obj);
1138-
Object.defineProperty(obj, impl, {
1139-
value: new Impl.implementation(globalObject, constructorArgs, privateData),
1140-
configurable: true
1141-
});
1140+
impl[wrapperSymbol] = null;
1141+
impl[globalObjectSymbol] = globalObject;
1142+
impl[createWrapperSymbol] = createWrapper;
1143+
1144+
return impl;
1145+
};
1146+
exports.setup = function setup(wrapper, globalObject, constructorArgs = [], privateData = {}) {
1147+
const impl = exports.createImpl(globalObject, constructorArgs, privateData);
1148+
impl[wrapperSymbol] = wrapper;
1149+
wrapper[implSymbol] = impl;
1150+
return wrapper;
1151+
};
1152+
exports._internalSetup = function _internalSetup(obj) {
11421153
`;
11431154

1144-
if (this.isLegacyPlatformObj) {
1155+
if (this.idl.inheritance) {
11451156
this.str += `
1146-
obj = new Proxy(obj, proxyHandler);
1157+
${this.idl.inheritance}._internalSetup(obj);
11471158
`;
11481159
}
1160+
this.generateOnInstance();
11491161

11501162
this.str += `
1151-
obj[impl][utils.wrapperSymbol] = obj;
1152-
if (Impl.init) {
1153-
Impl.init(obj[impl], privateData);
1154-
}
1155-
return obj;
11561163
};
11571164
`;
11581165
}
@@ -1438,10 +1445,10 @@ class Interface {
14381445
this.generateOffInstanceAfterClass();
14391446

14401447
this.str += `
1441-
if (globalObject[ctorRegistry] === undefined) {
1442-
globalObject[ctorRegistry] = Object.create(null);
1448+
if (globalObject[ctorRegistrySymbol] === undefined) {
1449+
globalObject[ctorRegistrySymbol] = Object.create(null);
14431450
}
1444-
globalObject[ctorRegistry]["${name}"] = ${name};
1451+
globalObject[ctorRegistrySymbol]["${name}"] = ${name};
14451452
14461453
Object.defineProperty(globalObject, "${name}", {
14471454
configurable: true,

lib/constructs/operation.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class Operation {
6767
`;
6868
}
6969

70-
const callOn = this.static ? "Impl.implementation" : "this[impl]";
70+
const callOn = this.static ? "Impl.implementation" : "this[implSymbol]";
7171
// In case of stringifiers, use the named implementation function rather than hardcoded "toString".
7272
// All overloads will have the same name, so pick the first one.
7373
const implFunc = this.idls[0].name || this.name;

lib/output/utils.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ function hasOwn(obj, prop) {
1111

1212
const wrapperSymbol = Symbol("wrapper");
1313
const implSymbol = Symbol("impl");
14+
const globalObjectSymbol = Symbol("global object");
15+
const createWrapperSymbol = Symbol("create wrapper");
1416
const sameObjectCaches = Symbol("SameObject caches");
1517
const ctorRegistrySymbol = Symbol.for("[webidl2js] constructor registry");
1618

@@ -28,7 +30,16 @@ function getSameObject(wrapper, prop, creator) {
2830
}
2931

3032
function wrapperForImpl(impl) {
31-
return impl ? impl[wrapperSymbol] : null;
33+
if (!impl) {
34+
return null;
35+
}
36+
37+
const wrapper = impl[wrapperSymbol];
38+
if (wrapper === undefined || wrapper !== null) {
39+
return wrapper;
40+
}
41+
42+
return impl[createWrapperSymbol](impl);
3243
}
3344

3445
function implForWrapper(wrapper) {
@@ -91,6 +102,8 @@ module.exports = exports = {
91102
hasOwn,
92103
wrapperSymbol,
93104
implSymbol,
105+
globalObjectSymbol,
106+
createWrapperSymbol,
94107
getSameObject,
95108
ctorRegistrySymbol,
96109
wrapperForImpl,

lib/reflector.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
module.exports.boolean = {
44
get(objName, attrName) {
5-
return `return this[impl].hasAttributeNS(null, "${attrName}");`;
5+
return `return this[implSymbol].hasAttributeNS(null, "${attrName}");`;
66
},
77
set(objName, attrName) {
88
return `
99
if (V) {
10-
this[impl].setAttributeNS(null, "${attrName}", "");
10+
this[implSymbol].setAttributeNS(null, "${attrName}", "");
1111
} else {
12-
this[impl].removeAttributeNS(null, "${attrName}");
12+
this[implSymbol].removeAttributeNS(null, "${attrName}");
1313
}
1414
`;
1515
}
@@ -18,35 +18,35 @@ module.exports.boolean = {
1818
module.exports.DOMString = {
1919
get(objName, attrName) {
2020
return `
21-
const value = this[impl].getAttributeNS(null, "${attrName}");
21+
const value = this[implSymbol].getAttributeNS(null, "${attrName}");
2222
return value === null ? "" : value;
2323
`;
2424
},
2525
set(objName, attrName) {
26-
return `this[impl].setAttributeNS(null, "${attrName}", V);`;
26+
return `this[implSymbol].setAttributeNS(null, "${attrName}", V);`;
2727
}
2828
};
2929

3030
module.exports.long = {
3131
get(objName, attrName) {
3232
return `
33-
const value = parseInt(this[impl].getAttributeNS(null, "${attrName}"));
33+
const value = parseInt(this[implSymbol].getAttributeNS(null, "${attrName}"));
3434
return isNaN(value) || value < -2147483648 || value > 2147483647 ? 0 : value
3535
`;
3636
},
3737
set(objName, attrName) {
38-
return `this[impl].setAttributeNS(null, "${attrName}", String(V));`;
38+
return `this[implSymbol].setAttributeNS(null, "${attrName}", String(V));`;
3939
}
4040
};
4141

4242
module.exports["unsigned long"] = {
4343
get(objName, attrName) {
4444
return `
45-
const value = parseInt(this[impl].getAttributeNS(null, "${attrName}"));
45+
const value = parseInt(this[implSymbol].getAttributeNS(null, "${attrName}"));
4646
return isNaN(value) || value < 0 || value > 2147483647 ? 0 : value
4747
`;
4848
},
4949
set(objName, attrName) {
50-
return `this[impl].setAttributeNS(null, "${attrName}", String(V > 2147483647 ? 0 : V));`;
50+
return `this[implSymbol].setAttributeNS(null, "${attrName}", String(V > 2147483647 ? 0 : V));`;
5151
}
5252
};

0 commit comments

Comments
 (0)