make dead code elimination smarter and eliminate non-referenced "pure" nodes

This commit is contained in:
Sebastian McKenzie 2015-05-01 09:31:28 +01:00
parent e9bcccffbd
commit 4e87809ff9
5 changed files with 78 additions and 8 deletions

View File

@ -21,6 +21,38 @@ export var metadata = {
optional: true
};
export function Identifier(node, parent, scope) {
if (!this.isReferenced()) return;
var binding = scope.getBinding(node.name);
if (!binding || binding.references > 1 || !binding.constant) return;
var replacement = binding.path.node;
if (t.isVariableDeclarator(replacement)) {
replacement = replacement.init;
}
t.toExpression(replacement);
scope.removeBinding(node.name);
binding.path.remove();
return replacement;
}
export function FunctionDeclaration(node, parent, scope) {
var bindingInfo = scope.getBinding(node.id.name);
if (bindingInfo && !bindingInfo.referenced) {
this.remove();
}
}
export { FunctionDeclaration as ClassDeclaration };
export function VariableDeclarator(node, parent, scope) {
if (!t.isIdentifier(node.id) || !scope.isPure(node.init)) return;
FunctionDeclaration.apply(this, arguments);
}
export function ConditionalExpression(node, parent, scope) {
var evaluateTest = this.get("test").evaluateTruthy();
if (evaluateTest === true) {

View File

@ -3,6 +3,8 @@ import * as t from "../types";
export default class Binding {
constructor({ identifier, scope, path, kind }) {
this.identifier = identifier;
this.references = 0;
this.referenced = false;
this.constant = true;
this.scope = scope;
this.path = path;
@ -58,6 +60,15 @@ export default class Binding {
}
}
/**
* Description
*/
reference() {
this.referenced = true;
this.references++;
}
/**
* Description
*/

View File

@ -319,6 +319,12 @@ export default class TraversalPath {
var parent = this.parent;
if (!parentPath) return;
// we've just removed the last declarator of a variable declaration so there's no point in
// keeping it
if (parentPath.isVariableDeclaration() && parent.declarations.length === 0) {
return parentPath.remove();
}
// we're the child of an expression statement so we should remove the parent
if (parentPath.isExpressionStatement()) {
return parentPath.remove();
@ -650,6 +656,10 @@ export default class TraversalPath {
return this.shouldStop;
}
/**
* Description
*/
getSibling(key) {
return TraversalPath.get(this.parentPath, null, this.parent, this.container, key, this.file);
}

View File

@ -40,8 +40,12 @@ var functionVariableVisitor = {
var programReferenceVisitor = {
enter(node, parent, scope, state) {
if (t.isReferencedIdentifier(node, parent) && !scope.hasBinding(node.name)) {
state.addGlobal(node);
if (t.isReferencedIdentifier(node, parent)) {
var bindingInfo = scope.getBinding(node.name);
if (bindingInfo) {
state.addGlobal(node);
bindingInfo.reference();
}
} else if (t.isLabeledStatement(node)) {
state.addGlobal(node);
} else if (t.isAssignmentExpression(node)) {
@ -482,6 +486,19 @@ export default class Scope {
this.crawl();
}
/**
* Description
*/
isPure(node) {
if (t.isIdentifier(node)) {
var bindingInfo = this.getBinding(node.name);
return bindingInfo.constant;
} else {
return t.isPure(node);
}
}
/**
* Description
*/

View File

@ -26,9 +26,9 @@
"ExportNamedDeclaration": ["Statement", "Declaration", "ModuleDeclaration", "ExportDeclaration"],
"ImportDeclaration": ["Statement", "Declaration", "ModuleDeclaration"],
"ArrowFunctionExpression": ["Scopable", "Function", "Expression"],
"FunctionDeclaration": ["Scopable", "Function", "Statement", "Declaration"],
"FunctionExpression": ["Scopable", "Function", "Expression"],
"ArrowFunctionExpression": ["Scopable", "Function", "Expression", "Pure"],
"FunctionDeclaration": ["Scopable", "Function", "Statement", "Pure", "Declaration"],
"FunctionExpression": ["Scopable", "Function", "Expression", "Pure"],
"BlockStatement": ["Scopable", "Statement"],
"Program": ["Scopable"],
@ -41,8 +41,8 @@
"SpreadProperty": ["UnaryLike"],
"SpreadElement": ["UnaryLike"],
"ClassDeclaration": ["Scopable", "Class", "Statement", "Declaration"],
"ClassExpression": ["Scopable", "Class", "Expression"],
"ClassDeclaration": ["Scopable", "Class", "Pure", "Statement", "Declaration"],
"ClassExpression": ["Scopable", "Class", "Pure", "Expression"],
"ForOfStatement": ["Scopable", "Statement", "For", "Loop"],
"ForInStatement": ["Scopable", "Statement", "For", "Loop"],
@ -62,7 +62,7 @@
"ConditionalExpression": ["Expression"],
"DoExpression": ["Expression"],
"Identifier": ["Expression"],
"Literal": ["Expression"],
"Literal": ["Expression", "Pure"],
"MemberExpression": ["Expression"],
"MetaProperty": ["Expression"],
"NewExpression": ["Expression"],