diff --git a/lib/types.js b/lib/types.js index 8c3dd992..43f3e7d7 100644 --- a/lib/types.js +++ b/lib/types.js @@ -715,11 +715,18 @@ Type.prototype._createBranchConstructor = function () { if (name === 'null') { return null; } - var attr = ~name.indexOf('.') ? 'this[\'' + name + '\']' : 'this.' + name; - var body = 'return function Branch$(val) { ' + attr + ' = val; };'; - var Branch = (new Function(body))(); + function ConstructorFunction() { + return function Branch$(val) { + if (~name.indexOf('.')) { + this[`${name}`] = val; + } else { + this[name] = val; + } + }; + } + var Branch = ConstructorFunction(); Branch.type = this; - Branch.prototype.unwrap = new Function('return ' + attr + ';'); + Branch.prototype.unwrap = function() { return this[`${name}`]; }; Branch.prototype.unwrapped = Branch.prototype.unwrap; // Deprecated. return Branch; }; @@ -2124,11 +2131,9 @@ RecordType.prototype._getConstructorName = function () { RecordType.prototype._createConstructor = function (errorStackTraces) { // jshint -W054 - var outerArgs = []; - var innerArgs = []; - var ds = []; // Defaults. - var innerBody = ''; + var ds = {}; // Defaults. var i, l, field, name, defaultValue, hasDefault, stackField; + for (i = 0, l = this.fields.length; i < l; i++) { field = this.fields[i]; defaultValue = field.defaultValue; @@ -2142,36 +2147,42 @@ RecordType.prototype._createConstructor = function (errorStackTraces) { // particular, without a default) to populate a stack trace below. stackField = field; } - innerArgs.push('v' + i); - innerBody += ' '; - if (!hasDefault) { - innerBody += 'this.' + name + ' = v' + i + ';\n'; - } else { - innerBody += 'if (v' + i + ' === undefined) { '; - innerBody += 'this.' + name + ' = d' + ds.length + '(); '; - innerBody += '} else { this.' + name + ' = v' + i + '; }\n'; - outerArgs.push('d' + ds.length); - ds.push(defaultValue); - } - } - if (stackField) { - // We should populate a stack trace. - innerBody += ' if (this.stack === undefined) { '; - /* istanbul ignore else */ - if (typeof Error.captureStackTrace == 'function') { - // v8 runtimes, the easy case. - innerBody += 'Error.captureStackTrace(this, this.constructor);'; - } else { - // A few other runtimes (e.g. SpiderMonkey), might not work everywhere. - innerBody += 'this.stack = Error().stack;'; + if (hasDefault) { + ds[i] = defaultValue; } - innerBody += ' }\n'; } - var outerBody = 'return function ' + this._getConstructorName() + '('; - outerBody += innerArgs.join() + ') {\n' + innerBody + '};'; - var Record = new Function(outerArgs.join(), outerBody).apply(undefined, ds); var self = this; + function ConstructorFunction(outerArgs) { + const constructorName = self._getConstructorName(); + var innerFunction = ({ + [constructorName]: function(...innerArgs) { + for(var i = 0, l = self.fields.length; i < l; i++) { + let f = self.fields[i]; + if (f.defaultValue() === undefined) { + this[f.name] = innerArgs[i]; + } else { + if (innerArgs[i] === undefined) { + this[f.name] = outerArgs[i](); + } else { + this[f.name] = innerArgs[i]; + } + } + } + if (stackField) { + if (this.stack === undefined) { + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, this.constructor); + } else { + this.stack = Error().stack; + } + } + } + } + })[constructorName]; + return innerFunction; + } + var Record = ConstructorFunction(ds); Record.getType = function () { return self; }; Record.type = self; if (this._isError) { @@ -2190,125 +2201,136 @@ RecordType.prototype._createConstructor = function (errorStackTraces) { RecordType.prototype._createChecker = function () { // jshint -W054 - var names = []; - var values = []; - var name = this._getConstructorName(); - var body = 'return function check' + name + '(v, f, h, p) {\n'; - body += ' if (\n'; - body += ' v === null ||\n'; - body += ' typeof v != \'object\' ||\n'; - body += ' (f && !this._checkFields(v))\n'; - body += ' ) {\n'; - body += ' if (h) { h(v, this); }\n'; - body += ' return false;\n'; - body += ' }\n'; - if (!this.fields.length) { - // Special case, empty record. We handle this directly. - body += ' return true;\n'; - } else { - for (i = 0, l = this.fields.length; i < l; i++) { - field = this.fields[i]; - names.push('t' + i); - values.push(field.type); - if (field.defaultValue() !== undefined) { - body += ' var v' + i + ' = v.' + field.name + ';\n'; - } - } - body += ' if (h) {\n'; - body += ' var b = 1;\n'; - body += ' var j = p.length;\n'; - body += ' p.push(\'\');\n'; - var i, l, field; - for (i = 0, l = this.fields.length; i < l; i++) { - field = this.fields[i]; - body += ' p[j] = \'' + field.name + '\';\n'; - body += ' b &= '; - if (field.defaultValue() === undefined) { - body += 't' + i + '._check(v.' + field.name + ', f, h, p);\n'; - } else { - body += 'v' + i + ' === undefined || '; - body += 't' + i + '._check(v' + i + ', f, h, p);\n'; + const values = []; + this.fields.forEach(field => { + values.push(field.type); + }); + var self = this; + function ConstructorFunction(outerArgs) { + var constructorName = self._getConstructorName(); + var innerFunction = ({ + [constructorName]: function (v, f, h, p) { + if (v === null || typeof v !== 'object' || (f && !this._checkFields(v))) { + if (h) { + h(v, this); + } + return false; + } + const vArray = []; + if (!self.fields.length) { + return true; + } else { + for (var i =0, l = self.fields.length; i < l; i++) { + let f = self.fields[i]; + if (f.defaultValue() !== undefined) { + vArray.push(v[f.name]); + } + } + } + if (h) { + var b = 1; + var j = p.length; + p.push(''); + for (var i =0, l = self.fields.length; i < l; i++) { + let f = self.fields[i]; + p[j] = f.name; + b &= f.defaultValue() === undefined ? + outerArgs[i]._check(v[f.name], f, h, p) + : + vArray[i] === undefined || outerArgs[i]._check(vArray[i], f, h, p); + } + p.pop(); + return !!b; + } else { + return self.fields.reduce((prevVal, currentVal, i) => { + return prevVal && ( + currentVal.defaultValue() === undefined ? + outerArgs[i]._check(v[currentVal.name]) + : + vArray[i] === undefined || outerArgs[i]._check(vArray[i], f) + ); + }, true); + } } - } - body += ' p.pop();\n'; - body += ' return !!b;\n'; - body += ' } else {\n return (\n '; - body += this.fields.map(function (field, i) { - return field.defaultValue() === undefined ? - 't' + i + '._check(v.' + field.name + ', f)' : - '(v' + i + ' === undefined || t' + i + '._check(v' + i + ', f))'; - }).join(' &&\n '); - body += '\n );\n }\n'; + })[constructorName]; + return innerFunction; } - body += '};'; - return new Function(names.join(), body).apply(undefined, values); + return ConstructorFunction(values); }; RecordType.prototype._createReader = function () { // jshint -W054 - var names = []; var values = [this.recordConstructor]; var i, l; for (i = 0, l = this.fields.length; i < l; i++) { - names.push('t' + i); values.push(this.fields[i].type); } - var name = this._getConstructorName(); - var body = 'return function read' + name + '(t) {\n'; - body += ' return new ' + name + '(\n '; - body += names.map(function (s) { return s + '._read(t)'; }).join(',\n '); - body += '\n );\n};'; - names.unshift(name); - // We can do this since the JS spec guarantees that function arguments are - // evaluated from left to right. - return new Function(names.join(), body).apply(undefined, values); + var self = this; + function ConstructorFunction(outerArgs) { + var constructorName = self._getConstructorName(); + var innerFunction = ({ + [constructorName]: function (t) { + return new outerArgs[0](...outerArgs.slice(1).map(ftype => ftype['_read'](t))); + } + })[constructorName]; + return innerFunction; + } + return ConstructorFunction(values); }; RecordType.prototype._createSkipper = function () { // jshint -W054 - var args = []; - var body = 'return function skip' + this._getConstructorName() + '(t) {\n'; var values = []; - var i, l; - for (i = 0, l = this.fields.length; i < l; i++) { - args.push('t' + i); - values.push(this.fields[i].type); - body += ' t' + i + '._skip(t);\n'; + this.fields.forEach((field) => { + values.push(field.type); + }); + var self = this; + function ConstructorFunction(outerArgs) { + var constructorName = self._getConstructorName(); + var innerFunction = ({ + [constructorName]: function (t) { + outerArgs.forEach(arg => arg._skip(t)); + } + })[constructorName]; + return innerFunction; } - body += '}'; - return new Function(args.join(), body).apply(undefined, values); + return ConstructorFunction(values); }; RecordType.prototype._createWriter = function () { // jshint -W054 // We still do default handling here, in case a normal JS object is passed. - var args = []; - var name = this._getConstructorName(); - var body = 'return function write' + name + '(t, v) {\n'; + var values = []; - var i, l, field, value; - for (i = 0, l = this.fields.length; i < l; i++) { - field = this.fields[i]; - args.push('t' + i); + var dValues = {}; + this.fields.forEach((field, i) => { values.push(field.type); - body += ' '; - if (field.defaultValue() === undefined) { - body += 't' + i + '._write(t, v.' + field.name + ');\n'; - } else { - value = field.type.toBuffer(field.defaultValue()).toString('binary'); - // Convert the default value to a binary string ahead of time. We aren't - // converting it to a buffer to avoid retaining too much memory. If we - // had our own buffer pool, this could be an idea in the future. - args.push('d' + i); - values.push(value); - body += 'var v' + i + ' = v.' + field.name + ';\n'; - body += 'if (v' + i + ' === undefined) {\n'; - body += ' t.writeBinary(d' + i + ', ' + value.length + ');\n'; - body += ' } else {\n t' + i + '._write(t, v' + i + ');\n }\n'; + if (field.defaultValue() !== undefined) { + dValues[i] = field.type.toBuffer(field.defaultValue()).toString('binary'); } + }); + var self = this; + function ConstructorFunction(vv, dv) { + var constructorName = self._getConstructorName(); + var innerFunction = ({ + [constructorName]: function (t, v) { + for (var i=0, l = self.fields.length; i= lazyIndex) ? ' ' : ' '; - args.push('r' + i); - values.push(field.type); - body += 'r' + i + '._skip(t);\n'; - } else { - j = resolvers[name].length; - while (j--) { - body += (~lazyIndex && i >= lazyIndex) ? ' ' : ' '; - args.push('r' + i + 'f' + j); - fieldResolver = resolvers[name][j]; - values.push(fieldResolver.resolver); - body += 'var ' + fieldResolver.name + ' = '; - body += 'r' + i + 'f' + j + '._' + (j ? 'peek' : 'read') + '(t);\n'; + var self = this; + function ConstructorFunction() { + var constructorName = self._getConstructorName(); + var innerFunction = ({ + [constructorName]: function (t, b) { + if (b) return; + var innerArgs = []; // Arguments for reader constructor. + for (i = 0; i < rFields.length; i++) { + field = rFields[i]; + names = getAliases(field); + matches = []; + for (j = 0; j < names.length; j++) { + name = names[j]; + if (wFieldsMap[name]) { + matches.push(name); + } + } + if (!matches.length) { + innerArgs.push(undefined); + } + } + for (var i = 0; i < wFields.length; i++) { + let wf = type.fields[i]; + name = wf.name; + if (resolvers[name] === undefined) { + wf.type._skip(t); + } else { + var j = resolvers[name].length; + while (j--) { + innerArgs.unshift(j ? resolvers[name][j].resolver._peek(t) : resolvers[name][j].resolver._read(t)) + } + } + } + return new self.recordConstructor(...innerArgs); } - } + })[constructorName]; + return innerFunction; } - if (~lazyIndex) { - body += ' }\n'; - } - body += ' return new ' + uname + '(' + innerArgs.join() + ');\n};'; - - resolver._read = new Function(args.join(), body).apply(undefined, values); + resolver._read = ConstructorFunction(); + }; RecordType.prototype._match = function (tap1, tap2) {