enhance `collapse_vars` (#3801)
authorAlex Lam S.L <alexlamsl@gmail.com>
Sat, 18 Apr 2020 21:04:21 +0000 (22:04 +0100)
committerGitHub <noreply@github.com>
Sat, 18 Apr 2020 21:04:21 +0000 (05:04 +0800)
lib/compress.js
test/compress/collapse_vars.js

index b7a91ea..b0e25dd 100644 (file)
@@ -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;
                     }
index ac87d78..c93a146 100644 (file)
@@ -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"
+}