Skip to content

Commit

Permalink
WIP Overhaul parameters/type handling
Browse files Browse the repository at this point in the history
Fixes #5.
  • Loading branch information
TimothyGu committed Feb 8, 2017
1 parent 05a3109 commit cbfca48
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 68 deletions.
7 changes: 4 additions & 3 deletions lib/constructs/attribute.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ const conversions = require("webidl-conversions");

const utils = require("../utils");
const reflector = require("../reflector");
const Parameters = require("../parameters");
const Types = require("../types");

function Attribute(obj, I, idl) {
function Attribute(obj, I, idl, opts) {
this.obj = obj;
this.interface = I;
this.idl = idl;
this.opts = opts;
}

Attribute.prototype.generate = function () {
Expand Down Expand Up @@ -49,7 +50,7 @@ Attribute.prototype.generate = function () {
${getterBody}
},`;
if (!this.idl.readonly) {
const conv = Parameters.generateVarConversion("V", { type: this.idl.idlType, optional: false }, this.idl.extAttrs, new Set());
const conv = Types.generateTypeConversion("V", this.idl.idlType, this.idl.extAttrs, this.opts);
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
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class Dictionary {
if (value !== undefined) {`;
const argAttrs = field.extAttrs;

const conv = Types.generateTypeConversion("value", typeConversion, argAttrs, this.opts.customTypes);
const conv = Types.generateTypeConversion("value", typeConversion, argAttrs, this.opts);
for (let key in conv.requires) {
this.str = `const ${key} = ${conv.requires[key]};\n` + this.str;
}
Expand Down
16 changes: 8 additions & 8 deletions lib/constructs/interface.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function Interface(idl, opts) {
this.mixins = [];
this.requires = {};
this.str = null;
this.opts = opts;
this.opts = Object.assign({}, opts, { curInterface: this.name });
this.iterable = this.idl.members.some(member => member.type === "iterable");
}

Expand Down Expand Up @@ -90,7 +90,7 @@ Interface.prototype.generateConstructor = function () {
}
}

const conversions = Parameters.generateOverloadConversions(overloads, this.opts.customTypes);
const conversions = Parameters.generateOverloadConversions(overloads, this.opts);
Object.assign(this.requires, conversions.requires);

minConstructor.nameList = minConstructor.nameList.map((name) => (keywords.has(name) ? "_" : "") + name);
Expand Down Expand Up @@ -126,15 +126,15 @@ Interface.prototype.generateRequires = function () {
let requireStr = ``;

if (this.idl.inheritance !== null) {
requireStr += `const ${this.idl.inheritance} = require("./${this.idl.inheritance}.js");\n`;
this.requires[this.idl.inheritance] = `require("./${this.idl.inheritance}.js")`;
}

requireStr += `const impl = utils.implSymbol;\n`;

if (this.mixins.length !== 0) {
requireStr += `const mixin = utils.mixin;\n`;
for (let i = 0; i < this.mixins.length; ++i) {
requireStr += `const ${this.mixins[i]} = require("./${this.mixins[i]}.js");\n`;
this.requires[this.mixins[i]] = `require("./${this.mixins[i]}.js")`;
}
}

Expand Down Expand Up @@ -251,7 +251,7 @@ Interface.prototype.generateIface = function () {
for (let i = 0; i < this.idl.members.length; ++i) {
const memberIdl = this.idl.members[i];
if (memberIdl.type === "attribute" && (utils.getExtAttr(memberIdl.extAttrs, "Unforgeable") || utils.isGlobal(this.idl))) {
const member = new Attribute(this, this.idl, memberIdl);
const member = new Attribute(this, this.idl, memberIdl, this.opts);
this.str += "\n " + member.generate().body.replace(/\n/g, '\n ');
}
}
Expand Down Expand Up @@ -307,14 +307,14 @@ Interface.prototype.generateOperations = function () {

switch (memberIdl.type) {
case "operation":
member = new Operation(this, this.idl, memberIdl, { customTypes: this.opts.customTypes });
member = new Operation(this, this.idl, memberIdl, this.opts);
if (done[member.name]) {
continue;
}
done[member.name] = true;
break;
case "iterable":
member = new Iterable(this, this.idl, memberIdl, { customTypes: this.opts.customTypes });
member = new Iterable(this, this.idl, memberIdl, this.opts);
break;
default:
//throw new Error("Can't handle member of type '" + memberIdl.type + "'");
Expand All @@ -339,7 +339,7 @@ Interface.prototype.generateAttributes = function () {
if (utils.getExtAttr(memberIdl.extAttrs, "Unforgeable") || utils.isGlobal(this.idl)) {
break;
}
member = new Attribute(this, this.idl, memberIdl);
member = new Attribute(this, this.idl, memberIdl, this.opts);
break;
case "const":
member = new Constant(this, this.idl, memberIdl);
Expand Down
2 changes: 1 addition & 1 deletion lib/constructs/operation.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Operation.prototype.generate = function () {

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

const parameterConversions = Parameters.generateOverloadConversions(overloads, this.obj.opts.customTypes);
const parameterConversions = Parameters.generateOverloadConversions(overloads, this.obj.opts);
Object.assign(requires, parameterConversions.requires);
str += parameterConversions.body;
if (overloads.every((overload) => conversions[overload.operation.idlType.idlType])) {
Expand Down
2 changes: 1 addition & 1 deletion lib/constructs/typedef.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ module.exports = class Typedef {
module.exports = {
convert(value) {`;

const conv = Types.generateTypeConversion("value", this.idl.idlType, [], this.opts.customTypes, {
const conv = Types.generateTypeConversion("value", this.idl.idlType, [], this.opts, {
handleNullable: true
});
for (let key in conv.requires) {
Expand Down
3 changes: 2 additions & 1 deletion lib/overloads.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,12 @@ module.exports.proveSimiliarity = function (overloads) {
if (maybeType.type.idlType !== thisType.idlType || maybeType.type.array !== thisType.array ||
maybeType.default !== overloads[j].operation.arguments[i].default) {
maybeType = null;
break;
}
}

typeConversions.push(maybeType);
}

return typeConversions;
};
};
64 changes: 34 additions & 30 deletions lib/parameters.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,46 @@ const Types = require("./types.js");

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

module.exports.generateVarConversion = function (name, conversion, argAttrs, customTypes) {
function getDefault(dflt) {
switch (dflt.type) {
case "boolean":
case "number":
case "string":
return JSON.stringify(dflt.value);
case "null":
case "NaN":
return dflt.type;
case "Infinity":
return `${dflt.negative ? "-" : ""}Infinity`;
case "sequence":
return "[]";
}
throw new Error("Unexpected default type: " + dflt.type);
}

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

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

if (Types.canHandleType(idlType, customTypes)) {
if (conversion.optional && !customTypes.has(idlType.idlType)) { // always (try to) force-convert dictionaries
str += `
if (${name} !== undefined) {`;
}
const conv = Types.generateTypeConversion(name, idlType, argAttrs, opts);
Object.assign(requires, conv.requires);
str += conv.body;

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

if (conversion.optional && !customTypes.has(idlType.idlType)) {
str += `
}`;
if (conversion.default) {
str += ` else {
${name} = ${JSON.stringify(conversion.default.value)};
}`;
}
}
}

if (idlType.nullable) {
if (conversion.optional && !customTypes.has(idlType.idlType)) {
str += `
}`;
}`;
if (conversion.default) {
str += ` else {
${name} = ${getDefault(conversion.default)};
}`;
}
}

return {
Expand All @@ -49,7 +53,7 @@ module.exports.generateVarConversion = function (name, conversion, argAttrs, cus
};
};

module.exports.generateOverloadConversions = function (overloads, customTypes) {
module.exports.generateOverloadConversions = function (overloads, opts) {
const requires = {};
let str = ``;
let maxConstructor = overloads[0];
Expand Down Expand Up @@ -77,14 +81,14 @@ module.exports.generateOverloadConversions = function (overloads, customTypes) {
str += ` && i < ${maxArguments}`;
}
str += `; ++i) {
args[i] = utils.tryImplForWrapper(arguments[i]);
args[i] = arguments[i];
}`;
for (let i = 0; i < typeConversions.length; ++i) {
if (typeConversions[i] === null) {
continue;
}

const conv = module.exports.generateVarConversion(`args[${i}]`, typeConversions[i], maxConstructor.operation.arguments[i].extAttrs, customTypes);
const conv = generateVarConversion(`args[${i}]`, typeConversions[i], maxConstructor.operation.arguments[i].extAttrs, opts);
Object.assign(requires, conv.requires);
str += conv.body;
}
Expand Down
5 changes: 3 additions & 2 deletions lib/transformer.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ class Transformer {
obj = new Interface(instruction, {
implDir: path.resolve(outputDir, file.impl),
implSuffix: this.options.implSuffix,
interfaces,
customTypes
});
interfaces.set(obj.name, obj);
Expand All @@ -105,12 +106,12 @@ class Transformer {
break;
}

obj = new Dictionary(instruction, { customTypes });
obj = new Dictionary(instruction, { interfaces, customTypes });
dictionaries.set(obj.name, obj);
customTypes.add(obj.name);
break;
case "typedef":
obj = new Typedef(instruction, { customTypes });
obj = new Typedef(instruction, { interfaces, customTypes });
typedefs.set(obj.name, obj);
customTypes.add(obj.name);
break;
Expand Down
70 changes: 49 additions & 21 deletions lib/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ const conversions = require("webidl-conversions");

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

module.exports.generateTypeConversion = function (name, idlType, argAttrs, customTypes, opts) {
opts = opts || {};
module.exports.generateTypeConversion = function (name, idlType, argAttrs, opts) {
const requires = {};
const handleNullable = Boolean(opts.handleNullable);
const customTypes = opts.customTypes;
const interfaces = opts.interfaces;
const curInterface = opts.curInterface;
let str = "";


if (handleNullable && idlType.nullable) {
if (idlType.nullable) {
str += `
if (${name} === null || ${name} === undefined) {
${name} = null;
Expand All @@ -24,22 +24,31 @@ module.exports.generateTypeConversion = function (name, idlType, argAttrs, custo
} else if (idlType.generic === "record") {
// record type
generateRecord();
} else if (idlType.generic === "Promise") {
// Promise type
generatePromise();
} else if (idlType.generic === "FrozenArray") {
// frozen array type
generateFrozenArray();
} else if (conversions[idlType.idlType]) {
// string or number type compatible with webidl-conversions
generateGeneric(`conversions["${idlType.idlType}"]`);
} else if (customTypes.has(idlType.idlType)) {
// dictionaries, interfaces, or typedefs
// dictionaries or typedefs
const varName = `convert${idlType.idlType}`;
requires[varName] = `require("./${idlType.idlType}").convert`;
generateGeneric(varName);
} else if (interfaces.has(idlType.idlType)) {
// interfaces
generateInterface();
} else {
// unknown
// Try to get the impl anyway.
// enumeration type, callback function type, or union type
// try to unwrap, if possible
str += `
${name} = utils.tryImplForWrapper(${name});`;
}

if (handleNullable && idlType.nullable) {
if (idlType.nullable) {
str += `
}`;
}
Expand All @@ -58,9 +67,7 @@ module.exports.generateTypeConversion = function (name, idlType, argAttrs, custo
const tmp = ${name};
for (let nextItem of tmp) {`;

const conv = exports.generateTypeConversion("nextItem", idlType.idlType, [], customTypes, {
handleNullable: true
});
const conv = exports.generateTypeConversion("nextItem", idlType.idlType, [], opts);
Object.assign(requires, conv.requires);
str += conv.body;

Expand All @@ -72,7 +79,7 @@ module.exports.generateTypeConversion = function (name, idlType, argAttrs, custo
}

function generateRecord() {
if (!handleNullable || !idlType.nullable) {
if (!idlType.nullable) {
str += `
if (${name} === null && ${name} === undefined) {
${name} = new Map();
Expand All @@ -92,11 +99,9 @@ module.exports.generateTypeConversion = function (name, idlType, argAttrs, custo
let typedKey = key;
let typedValue = value;`;

str += exports.generateTypeConversion("typedKey", idlType.idlType[0], [], customTypes).body;
str += exports.generateTypeConversion("typedKey", idlType.idlType[0], [], opts).body;

const conv = exports.generateTypeConversion("typedValue", idlType.idlType[1], [], customTypes, {
handleNullable: true
});
const conv = exports.generateTypeConversion("typedValue", idlType.idlType[1], [], opts);
Object.assign(requires, conv.requires);
str += conv.body;

Expand All @@ -107,12 +112,23 @@ module.exports.generateTypeConversion = function (name, idlType, argAttrs, custo
${name} = result;
}`;

if (!handleNullable || !idlType.nullable) {
if (!idlType.nullable) {
str += `
}`;
}
}

function generatePromise() {
str += `
${name} = Promise.resolve(${name});`;
}

function generateFrozenArray() {
generateSequence();
str += `
${name} = Object.freeze(${name});`
}

function generateGeneric(conversionFn) {
const enforceRange = utils.getExtAttr(argAttrs, "EnforceRange");
const clamp = utils.getExtAttr(argAttrs, "Clamp");
Expand Down Expand Up @@ -142,8 +158,20 @@ module.exports.generateTypeConversion = function (name, idlType, argAttrs, custo
${name} = ${conversionFn}(${name}${optString});`;
}
}
};

module.exports.canHandleType = function (idlType, customTypes) {
return idlType.generic === "sequence" || idlType.generic === "record" || idlType.idlType in conversions || customTypes.has(idlType.idlType);
function generateInterface() {
const ifaceName = idlType.idlType;
let ifaceRef = ifaceName;
if (ifaceName === curInterface) {
ifaceRef = "module.exports";
} else {
requires[ifaceRef] = `require("./${ifaceName}")`;
}
str += `
if (${ifaceRef}.is(${name})) {
${name} = utils.tryImplForWrapper(${name});
} else {
throw new TypeError("The value provided is not a ${ifaceName}");
}`;
}
};

0 comments on commit cbfca48

Please sign in to comment.