allow symbol replacement on multiple occurrences (#2472)

- all-or-nothing replacement
- avoid unmangleable names

fixes #2436
This commit is contained in:
Alex Lam S.L
2017-11-12 22:31:47 +08:00
committed by GitHub
parent 346fa12e0e
commit c6cfa04d10
3 changed files with 481 additions and 12 deletions

View File

@@ -796,8 +796,8 @@ merge(Compressor.prototype, {
});
function drop_decl(def) {
def._eliminiated = (def._eliminiated || 0) + 1;
if (def.orig.length == def._eliminiated) {
def.eliminated++;
if (def.orig.length == def.eliminated) {
def.scope.functions.del(def.name);
def.scope.variables.del(def.name);
}
@@ -854,10 +854,14 @@ merge(Compressor.prototype, {
// Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate);
if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false;
var one_off = lhs instanceof AST_Symbol && lhs.definition().references.length == 1;
var replace_all = candidate.multiple;
if (!replace_all && lhs instanceof AST_SymbolRef) {
var def = lhs.definition();
replace_all = def.references.length - def.replaced == 1;
}
var side_effects = value_has_side_effects(candidate);
var hit = candidate.name instanceof AST_SymbolFunarg;
var abort = false, replaced = false, can_replace = !args || !hit;
var abort = false, replaced = 0, can_replace = !args || !hit;
var tt = new TreeTransformer(function(node, descend) {
if (abort) return node;
// Skip nodes before `candidate` as quickly as possible
@@ -886,7 +890,8 @@ merge(Compressor.prototype, {
&& !(node instanceof AST_SymbolDeclaration)
&& !is_lhs(node, parent)
&& lhs.equivalent_to(node)) {
CHANGED = replaced = abort = true;
CHANGED = abort = true;
replaced++;
compressor.info("Collapsing {name} [{file}:{line},{col}]", {
name: node.print_to_string(),
file: node.start.file,
@@ -897,8 +902,13 @@ merge(Compressor.prototype, {
return make_node(AST_UnaryPrefix, candidate, candidate);
}
if (candidate instanceof AST_VarDef) {
if (candidate.multiple) {
abort = false;
return node;
}
var def = candidate.name.definition();
if (def.references.length == 1 && !compressor.exposed(def)) {
if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) {
def.replaced++;
return maintain_this_binding(parent, node, candidate.value);
}
return make_node(AST_Assign, candidate, {
@@ -922,7 +932,7 @@ merge(Compressor.prototype, {
|| side_effects && !references_in_scope(node.definition()))
|| (sym = lhs_or_def(node))
&& (sym instanceof AST_PropAccess || sym.name in lvalues)
|| (side_effects || !one_off)
|| (side_effects || !replace_all)
&& (parent instanceof AST_Binary && lazy_op(parent.operator)
|| parent instanceof AST_Case
|| parent instanceof AST_Conditional
@@ -935,7 +945,7 @@ merge(Compressor.prototype, {
if (node instanceof AST_Default || node instanceof AST_Scope) return node;
});
if (!can_replace) {
for (var j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; j < args.length; j++) {
for (var j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; !abort && j < args.length; j++) {
args[j].transform(tt);
}
can_replace = true;
@@ -943,6 +953,33 @@ merge(Compressor.prototype, {
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(tt);
}
if (candidate.multiple) {
var def = candidate.name.definition();
if (abort && def.references.length > replaced) replaced = false;
else {
abort = false;
hit = candidate.name instanceof AST_SymbolFunarg;
var value_def = candidate.value.definition();
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(new TreeTransformer(function(node) {
if (abort) return node;
if (!hit) {
if (node === candidate) {
hit = true;
return node;
}
return;
}
if (node instanceof AST_SymbolRef && node.name == def.name) {
def.replaced++;
value_def.replaced--;
if (!--replaced) abort = true;
return candidate.value;
}
}));
}
}
}
if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
}
}
@@ -956,7 +993,7 @@ merge(Compressor.prototype, {
&& (iife = compressor.parent()) instanceof AST_Call
&& iife.expression === fn) {
var fn_strict = compressor.has_directive("use strict");
if (fn_strict && fn.body.indexOf(fn_strict) < 0) fn_strict = false;
if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
var len = fn.argnames.length;
args = iife.args.slice(len);
var names = Object.create(null);
@@ -1012,12 +1049,22 @@ merge(Compressor.prototype, {
}
}
function mangleable_var(expr) {
var value = expr.value;
if (!(value instanceof AST_SymbolRef)) return false;
if (value.name == "arguments") return false;
if (value.definition().undeclared) return false;
expr.multiple = true;
return true;
}
function get_lhs(expr) {
if (expr instanceof AST_VarDef) {
var def = expr.name.definition();
if (def.orig.length - (def._eliminiated || 0) > 1
&& !(expr.name instanceof AST_SymbolFunarg)
|| def.references.length == 1 && !compressor.exposed(def)) {
var declared = def.orig.length - def.eliminated;
var referenced = def.references.length - def.replaced;
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
|| (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
} else {