aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAlex Lam S.L <alexlamsl@gmail.com>2019-11-29 17:45:49 +0800
committerGitHub <noreply@github.com>2019-11-29 17:45:49 +0800
commit1b61a81b5d6c9489466fe4707c1b01810a01d03a (patch)
treeb7c4077cbc121424b57f08d80d26dbd250453a44 /lib
parent5a88c30d6598bc9ec6d3b81077a95ab6942da9cd (diff)
downloadtracifyjs-1b61a81b5d6c9489466fe4707c1b01810a01d03a.tar.gz
tracifyjs-1b61a81b5d6c9489466fe4707c1b01810a01d03a.zip
enhance `collapse_vars` (#3613)
Diffstat (limited to 'lib')
-rw-r--r--lib/compress.js129
1 files changed, 90 insertions, 39 deletions
diff --git a/lib/compress.js b/lib/compress.js
index 52c79a73..34700d4a 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -1116,7 +1116,7 @@ merge(Compressor.prototype, {
var args;
var candidates = [];
var stat_index = statements.length;
- var scanner = new TreeTransformer(function(node) {
+ var scanner = new TreeTransformer(function(node, descend) {
if (abort) return node;
// Skip nodes before `candidate` as quickly as possible
if (!hit) {
@@ -1151,11 +1151,16 @@ merge(Compressor.prototype, {
return node;
}
if (is_lhs(node, parent)) {
- if (value_def) replaced++;
+ if (value_def && !hit_rhs) {
+ assign_used = true;
+ replaced++;
+ }
+ return node;
+ } else if (value_def) {
+ if (!hit_rhs) replaced++;
return node;
} else {
replaced++;
- if (value_def) return node;
}
CHANGED = abort = true;
AST_Node.info("Collapsing {name} [{file}:{line},{col}]", {
@@ -1188,6 +1193,14 @@ merge(Compressor.prototype, {
stop_after = node;
if (node instanceof AST_Scope) abort = true;
}
+ // Scan but don't replace inside getter/setter
+ if (node instanceof AST_Accessor) {
+ var replace = can_replace;
+ can_replace = false;
+ descend(node, scanner);
+ can_replace = replace;
+ return node;
+ }
return handle_custom_scan_order(node);
}, function(node) {
if (abort) return;
@@ -1200,9 +1213,28 @@ merge(Compressor.prototype, {
if (!hit) {
if (node !== hit_stack[hit_index]) return node;
hit_index++;
- if (hit_index < hit_stack.length) return;
- hit = true;
- return node;
+ switch (hit_stack.length - hit_index) {
+ case 0:
+ hit = true;
+ if (assign_used) return node;
+ if (node instanceof AST_VarDef) return node;
+ def.replaced++;
+ var parent = multi_replacer.parent();
+ if (parent instanceof AST_Sequence && parent.tail_node() !== node) {
+ value_def.replaced++;
+ return MAP.skip;
+ }
+ return get_rvalue(candidate);
+ case 1:
+ if (!assign_used && node.body === candidate) {
+ hit = true;
+ def.replaced++;
+ value_def.replaced++;
+ return null;
+ }
+ default:
+ return;
+ }
}
// Replace variable when found
if (node instanceof AST_SymbolRef
@@ -1215,7 +1247,8 @@ merge(Compressor.prototype, {
}
// Skip (non-executed) functions and (leading) default case in switch statements
if (node instanceof AST_Default || node instanceof AST_Scope) return node;
- });
+ }, patch_sequence);
+ var force_single;
while (--stat_index >= 0) {
// Treat parameters as collapsible in IIFE, i.e.
// function(a, b){ ... }(x());
@@ -1248,7 +1281,10 @@ merge(Compressor.prototype, {
} : side_effects_external : return_false;
var funarg = candidate.name instanceof AST_SymbolFunarg;
var hit = funarg;
- var abort = false, replaced = 0, can_replace = !args || !hit;
+ var abort = false;
+ var replaced = 0;
+ var assign_used = false;
+ var can_replace = !args || !hit;
if (!can_replace) {
for (var j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; !abort && j < args.length; j++) {
args[j].transform(scanner);
@@ -1260,20 +1296,22 @@ merge(Compressor.prototype, {
}
if (value_def) {
var def = lhs.definition();
- if (abort) {
- var referenced = def.references.length - def.replaced;
- if (candidate instanceof AST_Assign) referenced--;
- if (referenced > replaced) {
- replaced = false;
- } else {
- abort = false;
- }
+ var referenced = def.references.length - def.replaced;
+ if (candidate instanceof AST_Assign) referenced--;
+ if (replaced && referenced == replaced) {
+ abort = false;
+ } else if (candidate instanceof AST_Assign) {
+ candidates.push(hit_stack);
+ force_single = true;
+ continue;
+ } else {
+ replaced = false;
}
- if (!abort) {
+ if (replaced) {
hit_index = 0;
hit = funarg;
for (var i = stat_index; !abort && i < statements.length; i++) {
- statements[i].transform(multi_replacer);
+ if (!statements[i].transform(multi_replacer)) statements.splice(i--, 1);
}
value_def.single_use = false;
}
@@ -1502,9 +1540,12 @@ merge(Compressor.prototype, {
function find_stop(node, level) {
var parent = scanner.parent(level);
- if (parent instanceof AST_Array) return node;
+ if (parent instanceof AST_Array) return value_def ? find_stop(parent, level + 1) : node;
if (parent instanceof AST_Assign) return node;
- if (parent instanceof AST_Binary) return node;
+ if (parent instanceof AST_Binary) {
+ if (!value_def || parent.left !== node) return node;
+ return find_stop(parent, level + 1);
+ }
if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Conditional) return node;
@@ -1512,7 +1553,9 @@ merge(Compressor.prototype, {
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_IterationStatement) return node;
- if (parent instanceof AST_ObjectKeyVal) return node;
+ if (parent instanceof AST_ObjectKeyVal) {
+ return value_def ? find_stop(scanner.parent(level + 1), level + 2) : node;
+ }
if (parent instanceof AST_PropAccess) return node;
if (parent instanceof AST_Sequence) {
return (parent.tail_node() === node ? find_stop : find_stop_unused)(parent, level + 1);
@@ -1557,8 +1600,11 @@ merge(Compressor.prototype, {
return null;
}
- function mangleable_var(var_def) {
- var value = var_def.value;
+ function mangleable_var(value) {
+ if (force_single) {
+ force_single = false;
+ return;
+ }
if (!(value instanceof AST_SymbolRef)) return;
var def = value.definition();
if (def.undeclared) return;
@@ -1573,11 +1619,21 @@ merge(Compressor.prototype, {
var referenced = def.references.length - def.replaced;
var declared = def.orig.length - def.eliminated;
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
- || (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) {
+ || (referenced > 1 ? mangleable_var(expr.value) : !compressor.exposed(def))) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
+ } else if (expr instanceof AST_Assign) {
+ var lhs = expr.left;
+ if (expr.operator == "=" && lhs instanceof AST_SymbolRef) {
+ var def = lhs.definition();
+ if (def.references[0] === lhs) {
+ var referenced = def.references.length - def.replaced;
+ if (referenced > 1) mangleable_var(expr.right);
+ }
+ }
+ return lhs;
} else {
- return expr[expr instanceof AST_Assign ? "left" : "expression"];
+ return expr.expression;
}
}
@@ -1689,20 +1745,15 @@ merge(Compressor.prototype, {
node.value = null;
return node;
}
- var parent = this.parent();
- if (!parent) return in_list ? MAP.skip : null;
- if (parent instanceof AST_Sequence) return MAP.skip;
- var value = expr;
- do {
- value = get_rvalue(value);
- } while (value instanceof AST_Assign);
- return value;
- }, function(node) {
- if (node instanceof AST_Sequence) switch (node.expressions.length) {
- case 0: return null;
- case 1: return node.expressions[0];
- }
- }));
+ return in_list ? MAP.skip : null;
+ }, patch_sequence));
+ }
+
+ function patch_sequence(node) {
+ if (node instanceof AST_Sequence) switch (node.expressions.length) {
+ case 0: return null;
+ case 1: return node.expressions[0];
+ }
}
function is_lhs_local(lhs) {