Merge pull request #889 from neVERberleRfellerER/rest-args-optimization-v2

Add rest parameters optimization
This commit is contained in:
Sebastian McKenzie
2015-02-26 15:03:51 +11:00
11 changed files with 170 additions and 43 deletions

View File

@@ -3,6 +3,49 @@ var t = require("../../../types");
exports.check = t.isRestElement;
var memberExpressionVisitor = {
enter(node, parent, scope, state) {
if (t.isScope(node, parent) && !scope.bindingIdentifierEquals(state.name, state.outerDeclar)) {
return this.skip();
}
if (t.isFunctionDeclaration(node) || t.isFunctionExpression(node)) {
state.isOptimizable = false;
return this.stop();
}
if (!t.isReferencedIdentifier(node, parent, { name: state.name })) return;
if (t.isMemberExpression(parent)) {
var prop = parent.property;
if (typeof prop.value === "number" ||
t.isUnaryExpression(prop) ||
t.isBinaryExpression(prop)) {
state.candidates.push({ node, parent });
return;
}
}
state.isOptimizable = false;
this.stop();
}
};
function optimizeMemberExpression(node, parent, offset) {
var newExpr;
var prop = parent.property;
if (t.isLiteral(prop)) {
node.name = "arguments";
prop.value += offset;
prop.raw = String(prop.value);
} else {
node.name = "arguments";
newExpr = t.binaryExpression("+", prop, t.literal(offset));
parent.property = newExpr;
}
}
var hasRest = function (node) {
return t.isRestElement(node.params[node.params.length - 1]);
};
@@ -17,6 +60,35 @@ exports.Function = function (node, parent, scope) {
// otherwise `arguments` will be remapped in arrow functions
argsId._ignoreAliasFunctions = true;
// support patterns
if (t.isPattern(rest)) {
var pattern = rest;
rest = scope.generateUidIdentifier("ref");
var declar = t.variableDeclaration("var", pattern.elements.map(function (elem, index) {
var accessExpr = t.memberExpression(rest, t.literal(index), true);
return t.variableDeclarator(elem, accessExpr);
}));
node.body.body.unshift(declar);
}
// check if rest is used only in member expressions
var restOuterDeclar = scope.getBindingIdentifier(rest.name);
var state = {
name: rest.name,
outerDeclar: restOuterDeclar,
isOptimizable: true,
candidates: []
};
scope.traverse(node, memberExpressionVisitor, state);
if (state.isOptimizable) {
for (let i = 0, count = state.candidates.length; i < count; ++i) {
let candidate = state.candidates[i];
optimizeMemberExpression(candidate.node, candidate.parent, node.params.length, state.strictMode);
}
return;
}
var start = t.literal(node.params.length);
var key = scope.generateUidIdentifier("key");
var len = scope.generateUidIdentifier("len");
@@ -42,22 +114,6 @@ exports.Function = function (node, parent, scope) {
);
}
// support patterns
if (t.isPattern(rest)) {
var pattern = rest;
rest = scope.generateUidIdentifier("ref");
// let the destructuring transformer handle this
var restDeclar = t.variableDeclaration("var", [
t.variableDeclarator(pattern, rest)
]);
// retain evaluation position
restDeclar._blockHoist = node.params.length + 1;
node.body.body.unshift(restDeclar);
}
scope.assignTypeGeneric(rest.name, "Array");
var loop = util.template("rest", {