From eb6f32bfc3c9af65f417e2e4440115cec21ac075 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sat, 18 Apr 2020 22:04:21 +0100 Subject: [PATCH] enhance `collapse_vars` (#3801) --- lib/compress.js | 16 +++++++----- test/compress/collapse_vars.js | 45 +++++++++++++++++++++++++++++----- 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index b7a91ea5..b0e25dde 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -1122,7 +1122,10 @@ merge(Compressor.prototype, { function collapse(statements, compressor) { if (scope.pinned()) return statements; var args; + var assignments = Object.create(null); var candidates = []; + var declare_only = Object.create(null); + var force_single; var stat_index = statements.length; var scanner = new TreeTransformer(function(node, descend) { if (abort) return node; @@ -1255,7 +1258,6 @@ 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()); @@ -1264,7 +1266,6 @@ merge(Compressor.prototype, { if (stat_index == 0 && compressor.option("unused")) extract_args(); // Find collapsible assignments var hit_stack = []; - var declare_only = Object.create(null); extract_candidates(statements[stat_index]); while (candidates.length > 0) { hit_stack = candidates.pop(); @@ -1501,6 +1502,9 @@ merge(Compressor.prototype, { candidates.push(hit_stack.slice()); extract_candidates(expr.left); extract_candidates(expr.right); + if (expr.left instanceof AST_SymbolRef) { + assignments[expr.left.name] = (assignments[expr.left.name] || 0) + 1; + } } else if (expr instanceof AST_Binary) { extract_candidates(expr.left); extract_candidates(expr.right); @@ -1740,14 +1744,13 @@ merge(Compressor.prototype, { if (expr instanceof AST_VarDef) { var def = expr.name.definition(); if (!member(expr.name, def.orig)) return; - var referenced = def.references.length - def.replaced; - var declared = def.orig.length - def.eliminated; - declared -= declare_only[def.name] || 0; + var declared = def.orig.length - def.eliminated - (declare_only[def.name] || 0); + var referenced = def.references.length - def.replaced - (assignments[def.name] || 0); if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)) { mangleable_var(expr.value); return make_node(AST_SymbolRef, expr.name, expr.name); } - if (referenced > 1 ? mangleable_var(expr.value) : !compressor.exposed(def)) { + if (mangleable_var(expr.value) || referenced == 1 && !compressor.exposed(def)) { return make_node(AST_SymbolRef, expr.name, expr.name); } } else if (expr instanceof AST_Assign) { @@ -1871,6 +1874,7 @@ merge(Compressor.prototype, { found = true; if (node instanceof AST_VarDef) { node.value = null; + declare_only[node.name.name] = (declare_only[node.name.name] || 0) + 1; if (value_def) value_def.replaced++; return node; } diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js index ac87d788..c93a1465 100644 --- a/test/compress/collapse_vars.js +++ b/test/compress/collapse_vars.js @@ -803,8 +803,7 @@ collapse_vars_assignment: { expect: { function log(x) { return console.log(x), x; } function f0(c) { - var a = 3 / c; - return a = a; + return 3 / c; } function f1(c) { return 1 - 3 / c; @@ -2205,8 +2204,8 @@ var_defs: { } expect: { var f1 = function(x, y) { - var r = x + y, a = r * r - r, b = 7; - console.log(a + b); + var r = x + y, z = r * r - r, b = 7; + console.log(z + b); }; f1("1", 0); } @@ -2665,8 +2664,8 @@ double_def_1: { a(); } expect: { - var a; - (a = (a = x) && y)(); + var a = x; + (a = a && y)(); } } @@ -7921,3 +7920,37 @@ var_value_def: { } expect_stdout: "PASS" } + +mangleable_var: { + options = { + collapse_vars: true, + unused: true, + } + input: { + function f(a) { + var b = a(), c = a(), d = b; + return c.p(c, d); + } + console.log(f(function() { + return { + p: function() { + return "PASS" + }, + }; + })); + } + expect: { + function f(a) { + var b = a(), c = a(); + return c.p(c, b); + } + console.log(f(function() { + return { + p: function() { + return "PASS"; + } + }; + })); + } + expect_stdout: "PASS" +} -- 2.34.1