From 6de682120c6a9bffc7b18e125199cae7804d6bf1 Mon Sep 17 00:00:00 2001 From: alexlamsl Date: Tue, 30 Aug 2022 00:52:02 +0800 Subject: [PATCH] enhance `reduce_vars` --- lib/compress.js | 94 ++++++++++++++++++++++++--------- test/compress/collapse_vars.js | 5 +- test/compress/default-values.js | 2 +- test/compress/drop-unused.js | 12 ++--- test/compress/hoist_vars.js | 2 +- test/compress/if_return.js | 27 ++++++++++ test/compress/reduce_vars.js | 7 +-- 7 files changed, 109 insertions(+), 40 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 14a36c24832..86ea2806ec9 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -621,8 +621,10 @@ Compressor.prototype.compress = function(node) { scope.may_call_this = undefined; } - function push(tw) { - tw.safe_ids = Object.create(tw.safe_ids); + function push(tw, sequential) { + var safe_ids = Object.create(tw.safe_ids); + if (!sequential) safe_ids.seq = {}; + tw.safe_ids = safe_ids; } function pop(tw) { @@ -643,7 +645,14 @@ Compressor.prototype.compress = function(node) { var safe = tw.safe_ids[def.id]; if (safe) { var in_order = HOP(tw.safe_ids, def.id); - if (!in_order) safe.read = safe.read && safe.read !== tw.safe_ids ? true : tw.safe_ids; + if (!in_order) { + var seq = tw.safe_ids.seq; + if (!safe.read) { + safe.read = seq; + } else if (safe.read !== seq) { + safe.read = true; + } + } if (def.fixed == null) { if (is_arguments(def)) return false; if (def.global && def.name == "arguments") return false; @@ -684,7 +693,7 @@ Compressor.prototype.compress = function(node) { safe.assign = safe.assign && safe.assign !== tw.safe_ids ? true : tw.safe_ids; } if (def.fixed != null && safe.read) { - if (safe.read !== tw.safe_ids) return false; + if (safe.read !== tw.safe_ids.seq) return false; if (tw.loop_ids[def.id] !== tw.in_loop) return false; } return safe_to_read(tw, def) && all(def.orig, function(sym) { @@ -810,7 +819,7 @@ Compressor.prototype.compress = function(node) { var scanner = new TreeWalker(function(node) { if (node instanceof AST_DefaultValue) { reset_flags(node); - push(tw); + push(tw, true); node.value.walk(tw); pop(tw); var save = fixed; @@ -899,14 +908,15 @@ Compressor.prototype.compress = function(node) { var fn = this; fn.inlined = false; var iife = tw.parent(); - var hit = is_async(fn) || is_generator(fn); + var sequential = !is_async(fn) && !is_generator(fn); + var hit = !sequential; var aborts = false; fn.walk(new TreeWalker(function(node) { if (hit) return aborts = true; if (node instanceof AST_Return) return hit = true; if (node instanceof AST_Scope && node !== fn) return true; })); - if (aborts) push(tw); + if (aborts) push(tw, sequential); reset_variables(tw, compressor, fn); // Virtually turn IIFE parameters into variable definitions: // (function(a,b) {...})(c,d) ---> (function() {var a=c,b=d; ...})() @@ -991,7 +1001,7 @@ Compressor.prototype.compress = function(node) { return walk_lazy(); } var safe = safe_to_read(tw, ld); - if (lazy) push(tw); + if (lazy) push(tw, true); right.walk(tw); if (lazy) pop(tw); if (safe && !left.in_arg && safe_to_assign(tw, ld)) { @@ -1073,7 +1083,7 @@ Compressor.prototype.compress = function(node) { function walk_lazy() { if (!lazy) return; left.walk(tw); - push(tw); + push(tw, true); right.walk(tw); pop(tw); return true; @@ -1082,7 +1092,7 @@ Compressor.prototype.compress = function(node) { def(AST_Binary, function(tw) { if (!lazy_op[this.operator]) return; this.left.walk(tw); - push(tw); + push(tw, true); this.right.walk(tw); pop(tw); return true; @@ -1112,7 +1122,7 @@ Compressor.prototype.compress = function(node) { } exp.walk(tw); var optional = node.optional; - if (optional) push(tw); + if (optional) push(tw, true); node.args.forEach(function(arg) { arg.walk(tw); }); @@ -1182,7 +1192,7 @@ Compressor.prototype.compress = function(node) { }); def(AST_ClassInitBlock, function(tw, descend, compressor) { var node = this; - push(tw); + push(tw, true); reset_variables(tw, compressor, node); descend(); pop_scope(tw, node); @@ -1190,19 +1200,19 @@ Compressor.prototype.compress = function(node) { }); def(AST_Conditional, function(tw) { this.condition.walk(tw); - push(tw); + push(tw, true); this.consequent.walk(tw); pop(tw); - push(tw); + push(tw, true); this.alternative.walk(tw); pop(tw); return true; }); def(AST_DefaultValue, function(tw) { - this.name.walk(tw); - push(tw); + push(tw, true); this.value.walk(tw); pop(tw); + this.name.walk(tw); return true; }); def(AST_Do, function(tw) { @@ -1274,18 +1284,18 @@ Compressor.prototype.compress = function(node) { }); def(AST_If, function(tw) { this.condition.walk(tw); - push(tw); + push(tw, true); this.body.walk(tw); pop(tw); if (this.alternative) { - push(tw); + push(tw, true); this.alternative.walk(tw); pop(tw); } return true; }); def(AST_LabeledStatement, function(tw) { - push(tw); + push(tw, true); this.body.walk(tw); pop(tw); return true; @@ -1319,7 +1329,7 @@ Compressor.prototype.compress = function(node) { def(AST_Sub, function(tw) { if (!this.optional) return; this.expression.walk(tw); - push(tw); + push(tw, true); this.property.walk(tw); pop(tw); return true; @@ -1334,7 +1344,7 @@ Compressor.prototype.compress = function(node) { branch.expression.walk(tw); if (first) { first = false; - push(tw); + push(tw, true); } }) if (!first) pop(tw); @@ -1342,7 +1352,7 @@ Compressor.prototype.compress = function(node) { return true; }); def(AST_SwitchBranch, function(tw) { - push(tw); + push(tw, true); walk_body(this, tw); pop(tw); return true; @@ -1452,7 +1462,7 @@ Compressor.prototype.compress = function(node) { node.globals.each(function(def) { reset_def(tw, compressor, def); }); - push(tw); + push(tw, true); reset_variables(tw, compressor, node); descend(); pop_scope(tw, node); @@ -1461,11 +1471,11 @@ Compressor.prototype.compress = function(node) { def(AST_Try, function(tw, descend, compressor) { var node = this; reset_block_variables(tw, compressor, node); - push(tw); + push(tw, true); walk_body(node, tw); pop(tw); if (node.bcatch) { - push(tw); + push(tw, true); node.bcatch.walk(tw); pop(tw); } @@ -1590,6 +1600,7 @@ Compressor.prototype.compress = function(node) { // - `push()` & `pop()` when visiting conditional branches // - backup & restore via `save_ids` when visiting out-of-order sections tw.safe_ids = Object.create(null); + tw.safe_ids.seq = {}; this.walk(tw); }); @@ -3524,6 +3535,7 @@ Compressor.prototype.compress = function(node) { stat.alternative = make_node(AST_BlockStatement, stat, { body: as_statement_array(stat.alternative).concat(extract_functions(merge_jump, jump)), }); + adjust_refs(ab.value, merge_jump); statements[i] = stat; statements[i] = stat.transform(compressor); continue; @@ -3563,6 +3575,7 @@ Compressor.prototype.compress = function(node) { stat.alternative = make_node(AST_BlockStatement, stat.alternative, { body: as_statement_array_with_return(stat.alternative, alt), }); + adjust_refs(alt.value, merge_jump); statements[i] = stat; statements[i] = stat.transform(compressor); continue; @@ -3776,6 +3789,37 @@ Compressor.prototype.compress = function(node) { return body; } + function adjust_refs(value, mode) { + if (!mode) return; + if (!value) return; + switch (mode) { + case 4: + return; + case 3: + case 2: + value = value.tail_node(); + } + var fixed_by_id = new Dictionary(); + value.walk(new TreeWalker(function(node) { + if (!(node instanceof AST_SymbolRef)) return; + var def = node.definition(); + if (def.scope.resolve() !== scope) return; + var fixed = node.fixed; + if (!fixed || !fixed_by_id.has(def.id)) { + fixed_by_id.set(def.id, fixed); + } else if (fixed_by_id.get(def.id) !== fixed) { + fixed_by_id.set(def.id, false); + } + })); + if (fixed_by_id.size() > 0) jump.value.walk(new TreeWalker(function(node) { + if (!(node instanceof AST_SymbolRef)) return; + var def = node.definition(); + var fixed = node.fixed; + if (!fixed || !fixed_by_id.has(def.id)) return; + if (fixed_by_id.get(def.id) !== fixed) node.fixed = false; + })); + } + function next_index(i) { declare_only = true; for (var j = i; ++j < statements.length;) { diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js index 0e19d579570..4c705abc242 100644 --- a/test/compress/collapse_vars.js +++ b/test/compress/collapse_vars.js @@ -9321,12 +9321,11 @@ issue_4874: { })(a = 42); } expect: { - var a; null; (function(b) { - for (var c in a && a[console.log("PASS")]) + for (var c in 42, 42[console.log("PASS")]) console; - })(a = 42); + })(); } expect_stdout: "PASS" } diff --git a/test/compress/default-values.js b/test/compress/default-values.js index 49b9b7fa803..ac2be8400d7 100644 --- a/test/compress/default-values.js +++ b/test/compress/default-values.js @@ -2505,7 +2505,7 @@ issue_5463: { var a, b, b; console.log("PASS") && ( b = a = void 0, - b = [a = FAIL] = a && a + b = [a = FAIL] = a ); } expect_stdout: "PASS" diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index 1f4d2c921dd..fffa69419f3 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -1878,7 +1878,7 @@ issue_2846: { var c = function(a, b) { a = 0; b && b(a); - return a++; + return +a; }(); console.log(c); } @@ -2976,14 +2976,12 @@ issue_4025: { console.log(a, b, d); } expect: { - var c = 0; try { - console.log(c); + console.log(0); } finally { - var d = c + 1; - c = 0; + 0; } - console.log(1, 1, d); + console.log(1, 1, 1); } expect_stdout: [ "0", @@ -3696,7 +3694,7 @@ issue_5224: { (function() { var a = "FAIL 1"; null; - a = console.log(a); + console.log(a); })(function() { console.log(1 / 0); a; diff --git a/test/compress/hoist_vars.js b/test/compress/hoist_vars.js index cc24f5887a1..d0a73d49c56 100644 --- a/test/compress/hoist_vars.js +++ b/test/compress/hoist_vars.js @@ -624,7 +624,7 @@ issue_5411_2: { var b, c; b++; b = "PASS", - c = c && c[b]; + c; console.log(b); } expect_stdout: "PASS" diff --git a/test/compress/if_return.js b/test/compress/if_return.js index 7ad20043062..c6f5bef2a85 100644 --- a/test/compress/if_return.js +++ b/test/compress/if_return.js @@ -1834,6 +1834,33 @@ switch_return_5: { ] } +merged_references: { + options = { + if_return: true, + reduce_vars: true, + unused: true, + } + input: { + var a, b = "PASS"; + console.log(function(c) { + if (c = b) + return a || c; + c = FAIL; + return a || c; + }()); + } + expect: { + var a, b = "PASS"; + console.log(function(c) { + if (c = b); + else + c = FAIL; + return a || c; + }()); + } + expect_stdout: "PASS" +} + issue_5583: { options = { conditionals: true, diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index 002202617dc..885887ede31 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -2488,7 +2488,7 @@ side_effects_assign: { console.log(a); } expect: { - var a = typeof void (a && a.in); + var a = "undefined"; console.log(a); } expect_stdout: "undefined" @@ -2530,7 +2530,8 @@ pure_getters_2: { var a = a && a.b; } expect: { - var a = a && a.b; + var a; + a && a.b; } } @@ -5424,7 +5425,7 @@ issue_2774: { get a() { var b; (b = true) && b.c; - b = void 0; + void 0; } }.a); }