Skip to content

Commit

Permalink
Treat interfaces as first-class types
Browse files Browse the repository at this point in the history
Throw a TypeError when the object that is supposed to implement an
interface does not.
  • Loading branch information
TimothyGu committed May 2, 2017
1 parent ac5ee84 commit 013432e
Show file tree
Hide file tree
Showing 8 changed files with 37 additions and 28 deletions.
5 changes: 2 additions & 3 deletions lib/constructs/attribute.js
Expand Up @@ -28,10 +28,9 @@ Attribute.prototype.generate = function () {
definedOn = `obj`;
}
let getterBody = `return utils.tryWrapperForImpl(${objName}[impl].${this.idl.name});`;
let setterBody = `${objName}[impl].${this.idl.name} = utils.tryImplForWrapper(V);`;
let setterBody = `${objName}[impl].${this.idl.name} = V;`;
if (conversions[this.idl.idlType.idlType]) {
getterBody = `return ${objName}[impl].${this.idl.name};`;
setterBody = `${objName}[impl].${this.idl.name} = V;`;
}

if (this.idl.static) {
Expand All @@ -55,7 +54,7 @@ Attribute.prototype.generate = function () {
${getterBody}
},`;
if (!this.idl.readonly) {
const conv = Types.generateTypeConversion(this.ctx, "V", this.idl.idlType, this.idl.extAttrs);
const conv = Types.generateTypeConversion(this.ctx, "V", this.idl.idlType, this.idl.extAttrs, this.interface.name);
Object.assign(requires, conv.requires);
let conversion = conv.body.replace(/\n/g, "\n ");
str += `
Expand Down
2 changes: 1 addition & 1 deletion lib/constructs/dictionary.js
Expand Up @@ -33,7 +33,7 @@ class Dictionary {
if (value !== undefined) {`;
const argAttrs = field.extAttrs;

const conv = Types.generateTypeConversion(this.ctx, "value", typeConversion, argAttrs);
const conv = Types.generateTypeConversion(this.ctx, "value", typeConversion, argAttrs, this.name);
for (let key in conv.requires) {
this.str = `const ${key} = ${conv.requires[key]};\n` + this.str;
}
Expand Down
12 changes: 11 additions & 1 deletion lib/constructs/interface.js
Expand Up @@ -91,7 +91,7 @@ Interface.prototype.generateConstructor = function () {
}
}

const conversions = Parameters.generateOverloadConversions(this.ctx, overloads);
const conversions = Parameters.generateOverloadConversions(this.ctx, overloads, this.name);
Object.assign(this.requires, conversions.requires);

minConstructor.nameList = minConstructor.nameList.map((name) => (keywords.has(name) ? "_" : "") + name);
Expand Down Expand Up @@ -186,6 +186,16 @@ Interface.prototype.generateExport = function () {
}
}
return false;
},
convert(obj) {
// One day... one day... we'll be able to get rid of this branch...
if (module.exports.isImpl(obj)) {
return obj;
}
if (module.exports.is(obj)) {
return utils.implForWrapper(obj);
}
throw new TypeError("The value provided is not a ${this.name}");
},`;

if (this.iterable) {
Expand Down
2 changes: 1 addition & 1 deletion lib/constructs/operation.js
Expand Up @@ -53,7 +53,7 @@ Operation.prototype.generate = function () {

const callOn = this.idl.static ? "Impl" : "this[impl]";

const parameterConversions = Parameters.generateOverloadConversions(this.ctx, overloads);
const parameterConversions = Parameters.generateOverloadConversions(this.ctx, overloads, this.interface.name);
const argsSpread = parameterConversions.hasArgs ? "...args" : "";
Object.assign(requires, parameterConversions.requires);
str += parameterConversions.body;
Expand Down
2 changes: 1 addition & 1 deletion lib/context.js
Expand Up @@ -7,7 +7,7 @@ class Context {
}

initialize() {
this.customTypes = new Set();
this.customTypes = new Map();
this.interfaces = Object.create(null);
this.dictionaries = Object.create(null);
}
Expand Down
12 changes: 6 additions & 6 deletions lib/parameters.js
Expand Up @@ -22,22 +22,22 @@ function getDefault(dflt) {
throw new Error("Unexpected default type: " + dflt.type);
}

function generateVarConversion(ctx, name, conversion, argAttrs) {
function generateVarConversion(ctx, name, conversion, argAttrs, parentName) {
const { customTypes } = ctx;
const requires = {};
let str = "";
const idlType = conversion.type;

if (conversion.optional && !customTypes.has(idlType.idlType)) { // always (try to) force-convert dictionaries
if (conversion.optional && customTypes.get(idlType.idlType) !== "dictionary") { // always (try to) force-convert dictionaries
str += `
if (${name} !== undefined) {`;
}

const conv = Types.generateTypeConversion(ctx, name, idlType, argAttrs);
const conv = Types.generateTypeConversion(ctx, name, idlType, argAttrs, parentName);
Object.assign(requires, conv.requires);
str += conv.body;

if (conversion.optional && !customTypes.has(idlType.idlType)) {
if (conversion.optional && customTypes.get(idlType.idlType) !== "dictionary") {
str += `
}`;
if (conversion.default) {
Expand All @@ -53,7 +53,7 @@ if (${name} !== undefined) {`;
};
};

module.exports.generateOverloadConversions = function (ctx, overloads) {
module.exports.generateOverloadConversions = function (ctx, overloads, parentName) {
const { customTypes } = ctx;
const requires = {};
let str = ``;
Expand Down Expand Up @@ -89,7 +89,7 @@ module.exports.generateOverloadConversions = function (ctx, overloads) {
continue;
}

const conv = generateVarConversion(ctx, `args[${i}]`, typeConversions[i], maxConstructor.operation.arguments[i].extAttrs);
const conv = generateVarConversion(ctx, `args[${i}]`, typeConversions[i], maxConstructor.operation.arguments[i].extAttrs, parentName);
Object.assign(requires, conv.requires);
str += conv.body;
}
Expand Down
10 changes: 5 additions & 5 deletions lib/transformer.js
Expand Up @@ -11,7 +11,7 @@ const Interface = require("./constructs/interface");
const Dictionary = require("./constructs/dictionary");

class Transformer {
constructor(opts) {
constructor(opts = {}) {
this.ctx = new Context({
implSuffix: opts.implSuffix
});
Expand Down Expand Up @@ -75,9 +75,8 @@ class Transformer {
impl: content.impl
}));

const interfaces = this.ctx.interfaces = Object.create(null);
const dictionaries = this.ctx.dictionaries = Object.create(null);
const customTypes = this.ctx.customTypes = new Set();
this.ctx.initialize();
const { interfaces, dictionaries, customTypes } = this.ctx;

// first we're gathering all full interfaces and ignore partial ones
for (const file of parsed) {
Expand All @@ -93,6 +92,7 @@ class Transformer {
implDir: path.resolve(outputDir, file.impl)
});
interfaces[obj.name] = obj;
customTypes.set(obj.name, "interface");
break;
case "implements":
break; // handled later
Expand All @@ -103,7 +103,7 @@ class Transformer {

obj = new Dictionary(this.ctx, instruction);
dictionaries[obj.name] = obj;
customTypes.add(instruction.name);
customTypes.set(obj.name, "dictionary");
break;
default:
if (!this.options.suppressErrors) {
Expand Down
20 changes: 10 additions & 10 deletions lib/types.js
Expand Up @@ -4,7 +4,7 @@ const conversions = require("webidl-conversions");

const utils = require("./utils");

function generateTypeConversion(ctx, name, idlType, argAttrs) {
function generateTypeConversion(ctx, name, idlType, argAttrs, parentName) {
const { customTypes } = ctx;
const requires = {};
let str = "";
Expand Down Expand Up @@ -33,9 +33,15 @@ function generateTypeConversion(ctx, name, idlType, argAttrs) {
generateGeneric(`conversions["${idlType.idlType}"]`);
} else if (customTypes.has(idlType.idlType)) {
// dictionaries or interfaces
const varName = `convert${idlType.idlType}`;
requires[varName] = `require("./${idlType.idlType}").convert`;
generateGeneric(varName);
let fn;
// Avoid requiring the interface itself
if (idlType.idlType !== parentName) {
fn = `convert${idlType.idlType}`;
requires[fn] = `require("./${idlType.idlType}").convert`;
} else {
fn = `module.exports.convert`;
}
generateGeneric(fn);
} else {
// unknown
// Try to get the impl anyway.
Expand Down Expand Up @@ -137,12 +143,6 @@ function generateTypeConversion(ctx, name, idlType, argAttrs) {
} else if (treatNullAs && treatNullAs.rhs.value === "EmptyString") {
optString = `, { treatNullAsEmptyString: true }`;
}
if (conversions[idlType.idlType]) {
conversionFn = `conversions["${idlType.idlType}"]`;
} else {
requires[`convert${idlType.idlType}`] = `require("./${idlType.idlType}").convert`;
conversionFn = `convert${idlType.idlType}`;
}
if (idlType.array) {
str += `
for (let i = 0; i < ${name}.length; ++i) {
Expand Down

0 comments on commit 013432e

Please sign in to comment.