enhance collapse_vars (#2952)

- `a = b, b` => `a = b`
- `a.b = c, c()` => `(a.b = c)()`
This commit is contained in:
Alex Lam S.L
2018-02-28 15:19:32 +08:00
committed by GitHub
parent ace5811691
commit 36bca6934d
4 changed files with 782 additions and 25 deletions

View File

@@ -890,6 +890,11 @@ merge(Compressor.prototype, {
return x;
};
function root_expr(prop) {
while (prop instanceof AST_PropAccess) prop = prop.expression;
return prop;
}
function is_iife_call(node) {
if (node.TYPE != "Call") return false;
return node.expression instanceof AST_Function || is_iife_call(node.expression);
@@ -994,17 +999,19 @@ merge(Compressor.prototype, {
return node;
}
// Stop only if candidate is found within conditional branches
if (!stop_if_hit && (!lhs_local || !replace_all)
if (!stop_if_hit
&& (parent instanceof AST_Binary && lazy_op(parent.operator) && parent.left !== node
|| parent instanceof AST_Conditional && parent.condition !== node
|| parent instanceof AST_If && parent.condition !== node)) {
stop_if_hit = parent;
}
// Replace variable with assignment when found
var hit_lhs, hit_rhs;
if (can_replace
&& !(node instanceof AST_SymbolDeclaration)
&& lhs.equivalent_to(node)) {
if (stop_if_hit) {
&& (scan_lhs && (hit_lhs = lhs.equivalent_to(node))
|| scan_rhs && (hit_rhs = rhs.equivalent_to(node)))) {
if (stop_if_hit && (hit_rhs || !lhs_local || !replace_all)) {
abort = true;
return node;
}
@@ -1056,7 +1063,7 @@ merge(Compressor.prototype, {
|| node instanceof AST_PropAccess
&& (side_effects || node.expression.may_throw_on_access(compressor))
|| node instanceof AST_SymbolRef
&& (lvalues[node.name] || side_effects && may_modify(node))
&& (symbol_in_lvalues(node) || side_effects && may_modify(node))
|| node instanceof AST_VarDef && node.value
&& (node.name.name in lvalues || side_effects && may_modify(node.name))
|| (sym = is_lhs(node.left, node))
@@ -1111,12 +1118,15 @@ merge(Compressor.prototype, {
var stop_after = null;
var stop_if_hit = null;
var lhs = get_lhs(candidate);
if (!lhs || is_lhs_read_only(lhs) || lhs.has_side_effects(compressor)) continue;
var rhs = get_rhs(candidate);
var side_effects = lhs && lhs.has_side_effects(compressor);
var scan_lhs = lhs && !side_effects && !is_lhs_read_only(lhs);
var scan_rhs = rhs && foldable(rhs);
if (!scan_lhs && !scan_rhs) continue;
// Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate);
var lhs_local = is_lhs_local(lhs);
if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false;
var side_effects = value_has_side_effects(candidate);
if (!side_effects) side_effects = value_has_side_effects(candidate);
var replace_all = replace_all_symbols();
var may_throw = candidate.may_throw(compressor);
var funarg = candidate.name instanceof AST_SymbolFunarg;
@@ -1221,9 +1231,7 @@ merge(Compressor.prototype, {
function extract_candidates(expr) {
hit_stack.push(expr);
if (expr instanceof AST_Assign) {
if (!expr.left.has_side_effects(compressor)) {
candidates.push(hit_stack.slice());
}
candidates.push(hit_stack.slice());
extract_candidates(expr.right);
} else if (expr instanceof AST_Binary) {
extract_candidates(expr.left);
@@ -1361,21 +1369,47 @@ merge(Compressor.prototype, {
}
}
function get_rhs(expr) {
if (!(candidate instanceof AST_Assign && candidate.operator == "=")) return;
return candidate.right;
}
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
function foldable(expr) {
if (expr.is_constant()) return true;
if (expr instanceof AST_Array) return false;
if (expr instanceof AST_Function) return false;
if (expr instanceof AST_Object) return false;
if (expr instanceof AST_RegExp) return false;
if (expr instanceof AST_Symbol) return true;
if (!(lhs instanceof AST_SymbolRef)) return false;
if (expr.has_side_effects(compressor)) return false;
var circular;
var def = lhs.definition();
expr.walk(new TreeWalker(function(node) {
if (circular) return true;
if (node instanceof AST_SymbolRef && node.definition() === def) {
circular = true;
}
}));
return !circular;
}
function get_lvalues(expr) {
var lvalues = Object.create(null);
if (expr instanceof AST_Unary) return lvalues;
var tw = new TreeWalker(function(node, descend) {
var sym = node;
while (sym instanceof AST_PropAccess) sym = sym.expression;
if (candidate instanceof AST_VarDef) {
lvalues[candidate.name.name] = lhs;
}
var tw = new TreeWalker(function(node) {
var sym = root_expr(node);
if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
}
});
get_rvalue(expr).walk(tw);
expr.walk(tw);
return lvalues;
}
@@ -1408,11 +1442,11 @@ merge(Compressor.prototype, {
}
function is_lhs_local(lhs) {
while (lhs instanceof AST_PropAccess) lhs = lhs.expression;
return lhs instanceof AST_SymbolRef
&& lhs.definition().scope === scope
var sym = root_expr(lhs);
return sym instanceof AST_SymbolRef
&& sym.definition().scope === scope
&& !(in_loop
&& (lhs.name in lvalues
&& (sym.name in lvalues && lvalues[sym.name] !== lhs
|| candidate instanceof AST_Unary
|| candidate instanceof AST_Assign && candidate.operator != "="));
}
@@ -1434,6 +1468,13 @@ merge(Compressor.prototype, {
return false;
}
function symbol_in_lvalues(sym) {
var lvalue = lvalues[sym.name];
if (!lvalue) return;
if (lvalue !== lhs) return true;
scan_rhs = false;
}
function may_modify(sym) {
var def = sym.definition();
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return false;
@@ -3639,10 +3680,7 @@ merge(Compressor.prototype, {
return this;
}
this.write_only = true;
while (left instanceof AST_PropAccess) {
left = left.expression;
}
if (left.is_constant_expression(compressor.find_parent(AST_Scope))) {
if (root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) {
return this.right.drop_side_effect_free(compressor);
}
return this;