Skip to content

Commit

Permalink
Add new() export for interfaces
Browse files Browse the repository at this point in the history
Closes #193.
  • Loading branch information
domenic committed Apr 17, 2020
1 parent 923e1e9 commit d872e38
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 d872e38

Please sign in to comment.