From cbfca488d0345d3a6f801b33874bdc87f28ee2ee Mon Sep 17 00:00:00 2001 From: Timothy Gu Date: Tue, 7 Feb 2017 21:06:36 -0800 Subject: [PATCH] WIP Overhaul parameters/type handling Fixes #5. --- lib/constructs/attribute.js | 7 ++-- lib/constructs/dictionary.js | 2 +- lib/constructs/interface.js | 16 ++++----- lib/constructs/operation.js | 2 +- lib/constructs/typedef.js | 2 +- lib/overloads.js | 3 +- lib/parameters.js | 64 +++++++++++++++++---------------- lib/transformer.js | 5 +-- lib/types.js | 70 +++++++++++++++++++++++++----------- 9 files changed, 103 insertions(+), 68 deletions(-) diff --git a/lib/constructs/attribute.js b/lib/constructs/attribute.js index 24155d84..4b35f00a 100644 --- a/lib/constructs/attribute.js +++ b/lib/constructs/attribute.js @@ -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 () { @@ -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 += ` diff --git a/lib/constructs/dictionary.js b/lib/constructs/dictionary.js index b5ed9af7..7631b211 100644 --- a/lib/constructs/dictionary.js +++ b/lib/constructs/dictionary.js @@ -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; } diff --git a/lib/constructs/interface.js b/lib/constructs/interface.js index ff2e6368..22b723ce 100644 --- a/lib/constructs/interface.js +++ b/lib/constructs/interface.js @@ -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"); } @@ -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); @@ -126,7 +126,7 @@ 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`; @@ -134,7 +134,7 @@ Interface.prototype.generateRequires = function () { 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")`; } } @@ -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 '); } } @@ -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 + "'"); @@ -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); diff --git a/lib/constructs/operation.js b/lib/constructs/operation.js index 41bb3f56..fe930b00 100644 --- a/lib/constructs/operation.js +++ b/lib/constructs/operation.js @@ -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])) { diff --git a/lib/constructs/typedef.js b/lib/constructs/typedef.js index 1488aae9..2b7afe78 100644 --- a/lib/constructs/typedef.js +++ b/lib/constructs/typedef.js @@ -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) { diff --git a/lib/overloads.js b/lib/overloads.js index 4e4f9f12..d2739fb4 100644 --- a/lib/overloads.js +++ b/lib/overloads.js @@ -93,6 +93,7 @@ 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; } } @@ -100,4 +101,4 @@ module.exports.proveSimiliarity = function (overloads) { } return typeConversions; -}; \ No newline at end of file +}; diff --git a/lib/parameters.js b/lib/parameters.js index 9f00f8ec..8d35dd0f 100644 --- a/lib/parameters.js +++ b/lib/parameters.js @@ -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 { @@ -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]; @@ -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; } diff --git a/lib/transformer.js b/lib/transformer.js index febfb48a..c6602e25 100644 --- a/lib/transformer.js +++ b/lib/transformer.js @@ -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); @@ -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; diff --git a/lib/types.js b/lib/types.js index 83833671..2ca9fde4 100644 --- a/lib/types.js +++ b/lib/types.js @@ -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; @@ -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 += ` }`; } @@ -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; @@ -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(); @@ -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; @@ -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"); @@ -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}"); + }`; + } };