Skip to content

Commit

Permalink
Add new() export for interfaces
Browse files Browse the repository at this point in the history
Since I had to do some refactoring to reduce duplication anyway, this also includes general cleanup to the output, such as replacing obj with either value or wrapper, using arrow functions for our exports, and such. Closes #54.

Closes #193.
  • Loading branch information
domenic committed Apr 17, 2020
1 parent 0f5d2ff commit e403a3a
Show file tree
Hide file tree
Showing 4 changed files with 2,789 additions and 1,477 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@
"linebreak-style": ["error", "unix"],
"lines-around-comment": "off",
"max-depth": "off",
"max-len": ["error", 120, { "ignoreUrls": true }],
"max-len": ["error", 120, { "ignoreUrls": true, "ignoreTemplateLiterals": true }],
"max-nested-callbacks": "off",
"max-params": "off",
"max-statements": "off",
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,12 @@ Creates a new instance of the wrapper class and corresponding implementation cla

This is useful inside implementation class files, where it is easiest to only deal with impls, not wrappers.

### `new(globalObject)`

Creates a new instance of the wrapper class and corresponding implementation class, but without invoking the implementation class constructor logic. Then returns the implementation class.

This corresponds to the [Web IDL "new" algorithm](https://heycam.github.io/webidl/#new), and is useful when implementing specifications that initialize objects in different ways than their constructors do.

#### `setup(obj, globalObject, constructorArgs, privateData)`

This function is mostly used internally, and almost never should be called by your code. The one exception is if you need to inherit from a wrapper class corresponding to an interface without a `constructor`, from a non-webidl2js-generated class. Then, you can call `SuperClass.setup(this, globalObject, [], privateData)` as a substitute for doing `super()` (which would throw).
Expand Down
116 changes: 70 additions & 46 deletions lib/constructs/interface.js
Original file line number Diff line number Diff line change
Expand Up @@ -523,23 +523,23 @@ class Interface {

generateExport() {
this.str += `
exports.is = function is(obj) {
return utils.isObject(obj) && utils.hasOwn(obj, implSymbol) && obj[implSymbol] instanceof Impl.implementation;
exports.is = value => {
return utils.isObject(value) && utils.hasOwn(value, implSymbol) && value[implSymbol] instanceof Impl.implementation;
};
exports.isImpl = function isImpl(obj) {
return utils.isObject(obj) && obj instanceof Impl.implementation;
exports.isImpl = value => {
return utils.isObject(value) && value instanceof Impl.implementation;
};
exports.convert = function convert(obj, { context = "The provided value" } = {}) {
if (exports.is(obj)) {
return utils.implForWrapper(obj);
exports.convert = (value, { context = "The provided value" } = {}) => {
if (exports.is(value)) {
return utils.implForWrapper(value);
}
throw new TypeError(\`\${context} is not of type '${this.name}'.\`);
};
`;

if (this.iterable && this.iterable.isPair) {
this.str += `
exports.createDefaultIterator = function createDefaultIterator(target, kind) {
exports.createDefaultIterator = (target, kind) => {
const iterator = Object.create(IteratorPrototype);
Object.defineProperty(iterator, utils.iterInternalSymbol, {
value: { target, kind, index: 0 },
Expand Down Expand Up @@ -1125,7 +1125,7 @@ class Interface {

generateIface() {
this.str += `
exports.create = function create(globalObject, constructorArgs, privateData) {
function makeWrapper(globalObject) {
if (globalObject[ctorRegistrySymbol] === undefined) {
throw new Error('Internal error: invalid global object');
}
Expand All @@ -1135,60 +1135,84 @@ class Interface {
throw new Error('Internal error: constructor ${this.name} is not installed on the passed global object');
}
let obj = Object.create(ctor.prototype);
obj = exports.setup(obj, globalObject, constructorArgs, privateData);
return obj;
return Object.create(ctor.prototype);
}
`;

let setWrapperToProxy = ``;
if (this.isLegacyPlatformObj) {
setWrapperToProxy = `
wrapper = new Proxy(wrapper, proxyHandler);`;

if (this.needsPerGlobalProxyHandler) {
this.str += `
function makeProxy(wrapper, globalObject) {
let proxyHandler = proxyHandlerCache.get(globalObject);
if (proxyHandler === undefined) {
proxyHandler = new ProxyHandler(globalObject);
proxyHandlerCache.set(globalObject, proxyHandler);
}
return new Proxy(wrapper, proxyHandler);
}
`;

setWrapperToProxy = `
wrapper = makeProxy(wrapper, globalObject);`;
}
}

this.str += `
exports.create = (globalObject, constructorArgs, privateData) => {
const wrapper = makeWrapper(globalObject);
return exports.setup(wrapper, globalObject, constructorArgs, privateData);
};
exports.createImpl = function createImpl(globalObject, constructorArgs, privateData) {
const obj = exports.create(globalObject, constructorArgs, privateData);
return utils.implForWrapper(obj);
exports.createImpl = (globalObject, constructorArgs, privateData) => {
const wrapper = exports.create(globalObject, constructorArgs, privateData);
return utils.implForWrapper(wrapper);
};
exports._internalSetup = function _internalSetup(obj, globalObject) {
exports._internalSetup = (wrapper, globalObject) => {
`;

if (this.idl.inheritance) {
this.str += `
${this.idl.inheritance}._internalSetup(obj, globalObject);
${this.idl.inheritance}._internalSetup(wrapper, globalObject);
`;
}

this.generateOnInstance();

this.str += `
};
exports.setup = function setup(obj, globalObject, constructorArgs = [], privateData = {}) {
privateData.wrapper = obj;
exports._internalSetup(obj, globalObject);
Object.defineProperty(obj, implSymbol, {
exports.setup = (wrapper, globalObject, constructorArgs = [], privateData = {}) => {
privateData.wrapper = wrapper;
exports._internalSetup(wrapper, globalObject);
Object.defineProperty(wrapper, implSymbol, {
value: new Impl.implementation(globalObject, constructorArgs, privateData),
configurable: true
});
`;
${setWrapperToProxy}
if (this.isLegacyPlatformObj) {
if (this.needsPerGlobalProxyHandler) {
this.str += `
{
let proxyHandler = proxyHandlerCache.get(globalObject);
if (proxyHandler === undefined) {
proxyHandler = new ProxyHandler(globalObject);
proxyHandlerCache.set(globalObject, proxyHandler);
}
obj = new Proxy(obj, proxyHandler);
}
`;
} else {
this.str += `
obj = new Proxy(obj, proxyHandler);
`;
}
}

this.str += `
obj[implSymbol][utils.wrapperSymbol] = obj;
return obj;
wrapper[implSymbol][utils.wrapperSymbol] = wrapper;
return wrapper;
};
exports.new = globalObject => {
const wrapper = makeWrapper(globalObject);
exports._internalSetup(wrapper, globalObject);
Object.defineProperty(wrapper, implSymbol, {
value: Object.create(Impl.implementation.prototype),
configurable: true
});
${setWrapperToProxy}
wrapper[implSymbol][utils.wrapperSymbol] = wrapper;
return wrapper[implSymbol];
}
`;
}

Expand Down Expand Up @@ -1441,15 +1465,15 @@ class Interface {
if (methods.length > 0) {
this.str += `
Object.defineProperties(
obj,
wrapper,
Object.getOwnPropertyDescriptors({ ${methods.join(", ")} })
);
`;
}
if (propStrs.length > 0) {
this.str += `
Object.defineProperties(
obj,
wrapper,
{ ${propStrs.join(", ")} }
);
`;
Expand Down
Loading

0 comments on commit e403a3a

Please sign in to comment.