microoptimizations

This commit is contained in:
Sebastian McKenzie
2014-12-15 13:59:54 +11:00
parent 2cd49b08ec
commit 2fb6c7820c
21 changed files with 184 additions and 137 deletions

View File

@@ -81,8 +81,8 @@ _.each({
_declarations: require("./transformers/_declarations"),
// spec
specPropertyLiterals: require("./transformers/spec-property-literals"),
specMemberExpressioLiterals: require("./transformers/spec-member-expression-literals"),
specPropertyLiterals: require("./transformers/spec-property-literals"),
specMemberExpressionLiterals: require("./transformers/spec-member-expression-literals"),
// wrap up
_aliasFunctions: require("./transformers/_alias-functions"),

View File

@@ -18,6 +18,13 @@ Transformer.normalise = function (transformer) {
if (type[0] === "_") return;
if (_.isFunction(fns)) fns = { enter: fns };
transformer[type] = fns;
var aliases = t.FLIPPED_ALIAS_KEYS[type];
if (aliases) {
_.each(aliases, function (alias) {
transformer[alias] = fns;
});
}
});
return transformer;
@@ -42,16 +49,7 @@ Transformer.prototype.transform = function (file) {
var build = function (exit) {
return function (node, parent, scope) {
// add any node type aliases that exist
var types = [node.type].concat(t.ALIAS_KEYS[node.type] || []);
var fns = transformer.all;
_.each(types, function (type) {
fns = transformer[type] || fns;
});
// this transformer cannot deal with this node type
var fns = transformer[node.type];
if (!fns) return;
var fn = fns.enter;

View File

@@ -1,11 +1,12 @@
var t = require("../../types");
var _ = require("lodash");
exports.BlockStatement =
exports.Program = function (node) {
var kinds = {};
_.each(node._declarations, function (declar) {
for (var i in node._declarations) {
var declar = node._declarations[i];
var kind = declar.kind || "var";
var declarNode = t.variableDeclarator(declar.id, declar.init);
@@ -15,9 +16,9 @@ exports.Program = function (node) {
} else {
node.body.unshift(t.variableDeclaration(kind, [declarNode]));
}
});
}
_.each(kinds, function (declars, kind) {
node.body.unshift(t.variableDeclaration(kind, declars));
});
for (var kind in kinds) {
node.body.unshift(t.variableDeclaration(kind, kinds[kind]));
}
};

View File

@@ -1,5 +1,4 @@
var util = require("../../util");
var _ = require("lodash");
exports.Property = function (node) {
if (node.method) node.method = false;
@@ -7,9 +6,11 @@ exports.Property = function (node) {
exports.ObjectExpression = function (node, parent, file) {
var mutatorMap = {};
var hasAny = false;
node.properties = node.properties.filter(function (prop) {
if (prop.kind === "get" || prop.kind === "set") {
hasAny = true;
util.pushMutatorMap(mutatorMap, prop.key, prop.kind, prop.value);
return false;
} else {
@@ -17,7 +18,7 @@ exports.ObjectExpression = function (node, parent, file) {
}
});
if (_.isEmpty(mutatorMap)) return;
if (!hasAny) return;
var objId = util.getUid(parent, file);

View File

@@ -1,7 +1,6 @@
var traverse = require("../../traverse");
var util = require("../../util");
var t = require("../../types");
var _ = require("lodash");
exports.ClassDeclaration = function (node, parent, file, scope) {
return new Class(node, file, scope, false).run();
@@ -26,6 +25,9 @@ function Class(node, file, scope, closure) {
this.node = node;
this.file = file;
this.hasInstanceMutators = false;
this.hasStaticMutators = false;
this.instanceMutatorMap = {};
this.staticMutatorMap = {};
this.hasConstructor = false;
@@ -108,7 +110,8 @@ Class.prototype.buildBody = function () {
var body = this.body;
var self = this;
_.each(classBody, function (node) {
for (var i in classBody) {
var node = classBody[i];
if (t.isMethodDefinition(node)) {
self.replaceInstanceSuperReferences(node);
@@ -121,7 +124,7 @@ Class.prototype.buildBody = function () {
self.closure = true;
body.unshift(node);
}
});
}
if (!this.hasConstructor && superName) {
constructor.body.body.push(util.template("class-super-constructor-call", {
@@ -132,7 +135,7 @@ Class.prototype.buildBody = function () {
var instanceProps;
var staticProps;
if (!_.isEmpty(this.instanceMutatorMap)) {
if (this.hasInstanceMutators) {
var protoId = util.template("prototype-identifier", {
CLASS_NAME: className
});
@@ -140,7 +143,7 @@ Class.prototype.buildBody = function () {
instanceProps = util.buildDefineProperties(this.instanceMutatorMap, protoId);
}
if (!_.isEmpty(this.staticMutatorMap)) {
if (this.hasStaticMutators) {
staticProps = util.buildDefineProperties(this.staticMutatorMap, className);
}
@@ -174,11 +177,18 @@ Class.prototype.pushMethod = function (node) {
if (!node.static) className = t.memberExpression(className, t.identifier("prototype"));
methodName = t.memberExpression(className, methodName, node.computed);
this.body.push(t.expressionStatement(t.assignmentExpression("=", methodName, node.value)));
var expr = t.expressionStatement(t.assignmentExpression("=", methodName, node.value));
t.inheritsComments(expr, node);
this.body.push(expr);
} else {
// mutator
var mutatorMap = this.instanceMutatorMap;
if (node.static) mutatorMap = this.staticMutatorMap;
if (node.static) {
this.hasStaticMutators = true;
mutatorMap = this.staticMutatorMap;
} else {
this.hasInstanceMutators = true;
}
util.pushMutatorMap(mutatorMap, methodName, kind, node);
}
};
@@ -265,6 +275,7 @@ Class.prototype.pushConstructor = function (method) {
var fn = method.value;
this.hasConstructor = true;
t.inherits(construct, fn);
t.inheritsComments(construct, method);

View File

@@ -1,6 +1,5 @@
var util = require("../../util");
var t = require("../../types");
var _ = require("lodash");
exports.ObjectExpression = function (node, parent, file) {
var hasComputed = false;
@@ -31,7 +30,8 @@ exports.ObjectExpression = function (node, parent, file) {
containerCallee._aliasFunction = true;
_.each(computed, function (prop) {
for (var i in computed) {
var prop = computed[i];
containerBody.unshift(
t.expressionStatement(
t.assignmentExpression(
@@ -41,7 +41,7 @@ exports.ObjectExpression = function (node, parent, file) {
)
)
);
});
}
return container;
};

View File

@@ -7,6 +7,7 @@ exports.BlockStatement =
exports.ForInStatement =
exports.ForOfStatement =
exports.ForStatement = function (node, parent, file) {
var hasConstants = false;
var constants = {};
/**
@@ -15,17 +16,18 @@ exports.ForStatement = function (node, parent, file) {
*/
var check = function (parent, names, scope) {
_.each(names, function (nameNode, name) {
if (!_.has(constants, name)) return;
if (parent && t.isBlockStatement(parent) && parent !== constants[name]) return;
for (var name in names) {
var nameNode = names[name];
if (!_.has(constants, name)) continue;
if (parent && t.isBlockStatement(parent) && parent !== constants[name]) continue;
if (scope) {
var defined = scope.get(name);
if (defined && defined === nameNode) return;
if (defined && defined === nameNode) continue;
}
throw file.errorWithNode(nameNode, name + " is read-only");
});
}
};
var getIds = function (node) {
@@ -38,24 +40,30 @@ exports.ForStatement = function (node, parent, file) {
_.each(node.body, function (child, parent) {
if (child && t.isVariableDeclaration(child, { kind: "const" })) {
_.each(child.declarations, function (declar) {
_.each(getIds(declar), function (nameNode, name) {
for (var i in child.declarations) {
var declar = child.declarations[i];
var ids = getIds(declar);
for (var name in ids) {
var nameNode = ids[name];
var names = {};
names[name] = nameNode;
check(parent, names);
constants[name] = parent;
});
hasConstants = true;
}
declar._ignoreConstant = true;
});
}
child._ignoreConstant = true;
child.kind = "let";
}
});
if (_.isEmpty(constants)) return;
if (!hasConstants) return;
traverse(node, function (child, parent, scope) {
if (child._ignoreConstant) return;

View File

@@ -1,4 +1,5 @@
var traverse = require("../../traverse");
var Scope = require("../../traverse/scope");
var util = require("../../util");
var t = require("../../types");
var _ = require("lodash");
@@ -13,8 +14,9 @@ exports.Function = function (node, parent, file, scope) {
var iife = false;
_.each(node.defaults, function (def, i) {
if (!def) return;
for (var i in node.defaults) {
var def = node.defaults[i];
if (!def) continue;
var param = node.params[i];
@@ -42,18 +44,19 @@ exports.Function = function (node, parent, file, scope) {
if (has && !_.contains(node.params, has)) {
iife = true;
}
});
}
var body = [];
_.each(node.defaults, function (def, i) {
if (!def) return;
for (var i in node.defaults) {
var def = node.defaults[i];
if (!def) continue;
body.push(util.template("if-undefined-set-to", {
VARIABLE: node.params[i],
DEFAULT: def
}, true));
});
}
if (iife) {
var container = t.functionExpression(null, [], node.body, node.generator);

View File

@@ -1,7 +1,6 @@
// TODO: Clean up
var t = require("../../types");
var _ = require("lodash");
var buildVariableAssign = function (opts, id, init) {
var op = opts.operator;
@@ -27,20 +26,23 @@ var push = function (opts, nodes, elem, parentId) {
};
var pushObjectPattern = function (opts, nodes, pattern, parentId) {
_.each(pattern.properties, function (prop, i) {
for (var i in pattern.properties) {
var prop = pattern.properties[i];
if (t.isSpreadProperty(prop)) {
// get all the keys that appear in this object before the current spread
var keys = [];
_.each(pattern.properties, function (prop2, i2) {
if (i2 >= i) return false;
if (t.isSpreadProperty(prop2)) return;
for (var i2 in pattern.properties) {
var prop2 = pattern.properties[i2];
if (i2 >= i) break;
if (t.isSpreadProperty(prop2)) continue;
var key = prop2.key;
if (t.isIdentifier(key)) {
key = t.literal(prop2.key.name);
}
keys.push(key);
});
}
keys = t.arrayExpression(keys);
var value = t.callExpression(opts.file.addDeclaration("object-spread"), [parentId, keys]);
@@ -55,7 +57,7 @@ var pushObjectPattern = function (opts, nodes, pattern, parentId) {
nodes.push(buildVariableAssign(opts, pattern2, patternId2));
}
}
});
}
};
var pushArrayPattern = function (opts, nodes, pattern, parentId) {
@@ -65,15 +67,18 @@ var pushArrayPattern = function (opts, nodes, pattern, parentId) {
]));
parentId = _parentId;
_.each(pattern.elements, function (elem, i) {
if (!elem) return;
for (var i in pattern.elements) {
var elem = pattern.elements[i];
if (!elem) continue;
i = +i;
var newPatternId;
if (t.isSpreadElement(elem)) {
newPatternId = opts.file.toArray(parentId);
if (+i > 0) {
if (i > 0) {
newPatternId = t.callExpression(t.memberExpression(newPatternId, t.identifier("slice")), [t.literal(i)]);
}
@@ -83,7 +88,7 @@ var pushArrayPattern = function (opts, nodes, pattern, parentId) {
}
push(opts, nodes, elem, newPatternId);
});
}
};
var pushPattern = function (opts) {
@@ -217,15 +222,18 @@ exports.VariableDeclaration = function (node, parent, file, scope) {
var nodes = [];
var hasPattern = false;
_.each(node.declarations, function (declar) {
for (var i in node.declarations) {
var declar = node.declarations[i];
if (t.isPattern(declar.id)) {
hasPattern = true;
return false;
break;
}
});
}
if (!hasPattern) return;
_.each(node.declarations, function (declar) {
for (var i in node.declarations) {
var declar = node.declarations[i];
var patternId = declar.init;
var pattern = declar.id;
var opts = {
@@ -241,12 +249,13 @@ exports.VariableDeclaration = function (node, parent, file, scope) {
} else {
nodes.push(buildVariableAssign(opts, declar.id, declar.init));
}
});
}
if (!t.isProgram(parent) && !t.isBlockStatement(parent)) {
var declar;
_.each(nodes, function (node) {
for (var i in nodes) {
var node = nodes[i];
declar = declar || t.variableDeclaration(node.kind, []);
if (!t.isVariableDeclaration(node) && declar.kind !== node.kind) {
@@ -254,7 +263,7 @@ exports.VariableDeclaration = function (node, parent, file, scope) {
}
declar.declarations = declar.declarations.concat(node.declarations);
});
}
return declar;
}

View File

@@ -1 +1,3 @@
module.exports = require("./visit").transform;
var t = require("../../../types");
exports.ast = require("./visit").transform;

View File

@@ -18,9 +18,9 @@ var isVar = function (node) {
};
var standardiseLets = function (declars) {
_.each(declars, function (declar) {
delete declar._let;
});
for (var i in declars) {
delete declars[i]._let;
}
};
exports.VariableDeclaration = function (node) {
@@ -160,8 +160,9 @@ LetScoping.prototype.noClosure = function () {
LetScoping.prototype.remap = function () {
var replacements = this.info.duplicates;
var block = this.block;
var file = this.file;
if (_.isEmpty(replacements)) return;
if (!this.info.hasDuplicates) return;
var replace = function (node, parent, scope) {
if (!t.isIdentifier(node)) return;
@@ -209,6 +210,8 @@ LetScoping.prototype.getInfo = function () {
// all references with it
duplicates: {},
hasDuplicates: false,
// array of `Identifier` names of let variables that are accessible within
// the current block
keys: []
@@ -221,28 +224,31 @@ LetScoping.prototype.getInfo = function () {
// there's a variable with this exact name in an upper scope so we need
// to generate a new name
opts.duplicates[key] = id.name = file.generateUid(key, scope);
opts.hasDuplicates = true;
}
};
_.each(opts.declarators, function (declar) {
for (var i in opts.declarators) {
var declar = opts.declarators[i];
opts.declarators.push(declar);
var keys = t.getIds(declar, true);
_.each(keys, duplicates);
keys = _.keys(keys);
keys = Object.keys(keys);
opts.outsideKeys = opts.outsideKeys.concat(keys);
opts.keys = opts.keys.concat(keys);
});
}
_.each(block.body, function (declar) {
if (!isLet(declar)) return;
for (var i in block.body) {
var declar = block.body[i];
if (!isLet(declar)) continue;
_.each(t.getIds(declar, true), function (id, key) {
duplicates(id, key);
opts.keys.push(key);
});
});
}
return opts;
};
@@ -417,12 +423,13 @@ LetScoping.prototype.pushDeclar = function (node) {
var replace = [];
_.each(node.declarations, function (declar) {
if (!declar.init) return;
for (var i in node.declarations) {
var declar = node.declarations[i];
if (!declar.init) continue;
var expr = t.assignmentExpression("=", declar.id, declar.init);
replace.push(t.inherits(expr, declar));
});
}
return replace;
};

View File

@@ -1,5 +1,4 @@
var t = require("../../types");
var _ = require("lodash");
var inheritsComments = function (node, nodes) {
if (nodes.length) {
@@ -11,9 +10,9 @@ exports.ImportDeclaration = function (node, parent, file) {
var nodes = [];
if (node.specifiers.length) {
_.each(node.specifiers, function (specifier) {
file.moduleFormatter.importSpecifier(specifier, node, nodes, parent);
});
for (var i in node.specifiers) {
file.moduleFormatter.importSpecifier(node.specifiers[i], node, nodes, parent);
}
} else {
file.moduleFormatter.import(node, nodes, parent);
}
@@ -36,9 +35,9 @@ exports.ExportDeclaration = function (node, parent, file) {
file.moduleFormatter.export(node, nodes, parent);
} else {
_.each(node.specifiers, function (specifier) {
file.moduleFormatter.exportSpecifier(specifier, node, nodes, parent);
});
for (var i in node.specifiers) {
file.moduleFormatter.exportSpecifier(node.specifiers[i], node, nodes, parent);
}
}
inheritsComments(node, nodes);

View File

@@ -1,19 +1,16 @@
var t = require("../../types");
var _ = require("lodash");
var getSpreadLiteral = function (spread, file) {
return file.toArray(spread.argument);
};
var hasSpread = function (nodes) {
var has = false;
_.each(nodes, function (node) {
if (t.isSpreadElement(node)) {
has = true;
return false;
for (var i in nodes) {
if (t.isSpreadElement(nodes[i])) {
return true;
}
});
return has;
}
return false;
};
var build = function (props, file) {
@@ -27,14 +24,15 @@ var build = function (props, file) {
_props = [];
};
_.each(props, function (prop) {
for (var i in props) {
var prop = props[i];
if (t.isSpreadElement(prop)) {
push();
nodes.push(getSpreadLiteral(prop, file));
} else {
_props.push(prop);
}
});
}
push();

View File

@@ -1,5 +1,4 @@
var t = require("../../types");
var _ = require("lodash");
var buildBinaryExpression = function (left, right) {
return t.binaryExpression("+", left, right);
@@ -12,10 +11,11 @@ exports.TaggedTemplateExpression = function (node, parent, file) {
var strings = [];
var raw = [];
_.each(quasi.quasis, function (elem) {
for (var i in quasi.quasis) {
var elem = quasi.quasis[i];
strings.push(t.literal(elem.value.cooked));
raw.push(t.literal(elem.value.raw));
});
}
args.push(t.callExpression(file.addDeclaration("tagged-template-literal"), [
t.arrayExpression(strings),
@@ -30,7 +30,9 @@ exports.TaggedTemplateExpression = function (node, parent, file) {
exports.TemplateLiteral = function (node) {
var nodes = [];
_.each(node.quasis, function (elem) {
for (var i in node.quasis) {
var elem = node.quasis[i];
nodes.push(t.literal(elem.value.cooked));
var expr = node.expressions.shift();
@@ -38,18 +40,18 @@ exports.TemplateLiteral = function (node) {
if (t.isBinary(expr)) expr = t.parenthesizedExpression(expr);
nodes.push(expr);
}
});
}
if (nodes.length > 1) {
// remove redundant '' at the end of the expression
var last = _.last(nodes);
var last = nodes[nodes.length - 1];
if (t.isLiteral(last, { value: "" })) nodes.pop();
var root = buildBinaryExpression(nodes.shift(), nodes.shift());
_.each(nodes, function (node) {
root = buildBinaryExpression(root, node);
});
for (var i in nodes) {
root = buildBinaryExpression(root, nodes[i]);
}
return root;
} else {

View File

@@ -6,7 +6,7 @@ exports.Literal = function (node) {
if (!regex) return;
var flags = regex.flags.split("");
if (!_.contains(regex.flags, "u")) return;
if (regex.flags.indexOf("u") < 0) return;
_.pull(flags, "u");
regex.pattern = rewritePattern(regex.pattern, regex.flags);

View File

@@ -1,6 +1,5 @@
var util = require("../../util");
var t = require("../../types");
var _ = require("lodash");
var single = function (node, file) {
var block = node.blocks[0];
@@ -15,11 +14,13 @@ var single = function (node, file) {
KEY: block.left
});
_.each([result.callee.object, result], function (call) {
var aliasPossibles = [result.callee.object, result];
for (var i in aliasPossibles) {
var call = aliasPossibles[i];
if (t.isCallExpression(call)) {
call.arguments[0]._aliasFunction = true;
}
});
}
return result;
};

View File

@@ -1,16 +1,16 @@
// https://github.com/sebmarkbage/ecmascript-rest-spread
var t = require("../../types");
var _ = require("lodash");
exports.ObjectExpression = function (node) {
var hasSpread = false;
_.each(node.properties, function (prop) {
for (var i in node.properties) {
var prop = node.properties[i];
if (t.isSpreadProperty(prop)) {
hasSpread = true;
return false;
break;
}
});
}
if (!hasSpread) return;
var args = [];
@@ -22,14 +22,15 @@ exports.ObjectExpression = function (node) {
props = [];
};
_.each(node.properties, function (prop) {
for (var i in node.properties) {
var prop = node.properties[i];
if (t.isSpreadProperty(prop)) {
push();
args.push(prop.argument);
} else {
props.push(prop);
}
});
}
push();

View File

@@ -5,7 +5,6 @@
var esutils = require("esutils");
var t = require("../../types");
var _ = require("lodash");
exports.XJSIdentifier = function (node) {
if (esutils.keyword.isIdentifierName(node.name)) {
@@ -49,7 +48,7 @@ exports.XJSOpeningElement = {
tagName = tagExpr.value;
}
if (tagName && (/[a-z]/.exec(tagName[0]) || _.contains(tagName, "-"))) {
if (tagName && (/[a-z]/.exec(tagName[0]) || tagName.indexOf("-") >= 0)) {
args.push(t.literal(tagName));
} else {
args.push(tagExpr);
@@ -106,13 +105,17 @@ exports.XJSElement = {
exit: function (node) {
var callExpr = node.openingElement;
_.each(node.children, function (child) {
for (var i in node.children) {
var child = node.children[i];
if (t.isLiteral(child)) {
var lines = child.value.split(/\r\n|\n|\r/);
_.each(lines, function (line, i) {
var isFirstLine = i === 0;
var isLastLine = i === lines.length - 1;
for (var i in lines) {
var line = lines[i];
var isFirstLine = i == 0;
var isLastLine = i == lines.length - 1;
// replace rendered whitespace tabs with spaces
var trimmedLine = line.replace(/\t/g, ' ');
@@ -130,15 +133,15 @@ exports.XJSElement = {
if (trimmedLine) {
callExpr.arguments.push(t.literal(trimmedLine));
}
});
}
return;
continue;
} else if (t.isXJSEmptyExpression(child)) {
return;
continue;
}
callExpr.arguments.push(child);
});
}
return t.inherits(callExpr, node);
}
@@ -171,11 +174,13 @@ var addDisplayName = function (id, call) {
var props = first.properties;
var safe = true;
_.each(props, function (prop) {
for (var i in props) {
var prop = props[i];
if (t.isIdentifier(prop.key, { name: "displayName" })) {
return safe = false;
safe = false;
break;
}
});
}
if (safe) {
props.unshift(t.property("init", t.identifier("displayName"), t.literal(id)));

View File

@@ -1,11 +1,11 @@
var t = require("../../types");
var _ = require("lodash");
exports.ObjectExpression = function (node, parent, file) {
var keys = [];
_.each(node.properties, function (prop) {
if (prop.computed || prop.kind !== "init") return;
for (var i in node.properties) {
var prop = node.properties[i];
if (prop.computed || prop.kind !== "init") continue;
var key = prop.key;
if (t.isIdentifier(key)) {
@@ -13,13 +13,13 @@ exports.ObjectExpression = function (node, parent, file) {
} else if (t.isLiteral(key)) {
key = key.value;
} else {
return;
continue;
}
if (_.contains(keys, key)) {
if (keys.indexOf(key) >= 0) {
throw file.errorWithNode(prop.key, "Duplicate property key");
} else {
keys.push(key);
}
});
}
};

View File

@@ -1,5 +1,6 @@
{
"ArrayExpression": ["elements"],
"ArrowFunctionExpression": ["params", "body"],
"AssignmentExpression": ["operator", "left", "right"],
"BinaryExpression": ["operator", "left", "right"],
"BlockStatement": ["body"],

View File

@@ -45,20 +45,20 @@ _.each(t.BUILDER_KEYS, function (keys, type) {
t.ALIAS_KEYS = require("./alias-keys");
var _aliases = {};
t.FLIPPED_ALIAS_KEYS = {};
_.each(t.ALIAS_KEYS, function (aliases, type) {
_.each(aliases, function (alias) {
var types = _aliases[alias] = _aliases[alias] || [];
var types = t.FLIPPED_ALIAS_KEYS[alias] = t.FLIPPED_ALIAS_KEYS[alias] || [];
types.push(type);
});
});
_.each(_aliases, function (types, type) {
_.each(t.FLIPPED_ALIAS_KEYS, function (types, type) {
t[type.toUpperCase() + "_TYPES"] = types;
var is = t["is" + type] = function (node, opts) {
return node && _.contains(types, node.type) && t.shallowEqual(node, opts);
return node && types.indexOf(node.type) >= 0 && t.shallowEqual(node, opts);
};
addAssert(type, is);