enhance `collapse_vars` (#3572)
authorAlex Lam S.L <alexlamsl@gmail.com>
Tue, 5 Nov 2019 10:15:28 +0000 (18:15 +0800)
committerGitHub <noreply@github.com>
Tue, 5 Nov 2019 10:15:28 +0000 (18:15 +0800)
lib/compress.js
test/compress/collapse_vars.js

index 87671d8..ba78981 100644 (file)
@@ -1235,7 +1235,7 @@ merge(Compressor.prototype, {
                     var lvalues = get_lvalues(candidate);
                     var lhs_local = is_lhs_local(lhs);
                     if (!side_effects) side_effects = value_has_side_effects(candidate);
-                    var replace_all = replace_all_symbols();
+                    var replace_all = replace_all_symbols(candidate);
                     var may_throw = candidate.may_throw(compressor) ? in_try ? function(node) {
                         return node.has_side_effects(compressor);
                     } : side_effects_external : return_false;
@@ -1312,6 +1312,7 @@ merge(Compressor.prototype, {
 
             function in_conditional(node, parent) {
                 if (parent instanceof AST_Binary) return lazy_op[parent.operator] && parent.left !== node;
+                if (parent instanceof AST_Case) return parent.expression !== node;
                 if (parent instanceof AST_Conditional) return parent.condition !== node;
                 return parent instanceof AST_If && parent.condition !== node;
             }
@@ -1477,19 +1478,43 @@ merge(Compressor.prototype, {
                 if (parent instanceof AST_Call) return node;
                 if (parent instanceof AST_Case) return node;
                 if (parent instanceof AST_Conditional) return node;
-                if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
+                if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
                 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_PropAccess) return node;
-                if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
-                if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
+                if (parent instanceof AST_Sequence) {
+                    return (parent.tail_node() === node ? find_stop : find_stop_unused)(parent, level + 1);
+                }
+                if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
                 if (parent instanceof AST_Switch) return node;
                 if (parent instanceof AST_Unary) return node;
                 if (parent instanceof AST_VarDef) return node;
                 return null;
             }
 
+            function find_stop_unused(node, level) {
+                var parent = scanner.parent(level);
+                if (is_last_node(node, parent)) return node;
+                if (in_conditional(node, parent)) return node;
+                if (parent instanceof AST_Assign) return find_stop_unused(parent, level + 1);
+                if (parent instanceof AST_Binary) return find_stop_unused(parent, level + 1);
+                if (parent instanceof AST_Call) return find_stop_unused(parent, level + 1);
+                if (parent instanceof AST_Case) return find_stop_unused(parent, level + 1);
+                if (parent instanceof AST_Conditional) return find_stop_unused(parent, level + 1);
+                if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
+                if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1);
+                if (parent instanceof AST_If) return find_stop_unused(parent, level + 1);
+                if (parent instanceof AST_IterationStatement) return node;
+                if (parent instanceof AST_PropAccess) return find_stop_unused(parent, level + 1);
+                if (parent instanceof AST_Sequence) return find_stop_unused(parent, level + 1);
+                if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
+                if (parent instanceof AST_Switch) return find_stop_unused(parent, level + 1);
+                if (parent instanceof AST_Unary) return find_stop_unused(parent, level + 1);
+                if (parent instanceof AST_VarDef) return find_stop_unused(parent, level + 1);
+                return null;
+            }
+
             function mangleable_var(var_def) {
                 var value = var_def.value;
                 if (!(value instanceof AST_SymbolRef)) return;
@@ -1656,7 +1681,8 @@ merge(Compressor.prototype, {
                 return get_rvalue(expr).has_side_effects(compressor);
             }
 
-            function replace_all_symbols() {
+            function replace_all_symbols(expr) {
+                if (expr instanceof AST_Unary) return false;
                 if (side_effects) return false;
                 if (value_def) return true;
                 if (lhs instanceof AST_SymbolRef) {
index 31978c2..cf4e45a 100644 (file)
@@ -6387,3 +6387,51 @@ issue_3562: {
     }
     expect_stdout: "PASS PASS"
 }
+
+dot_throw_assign_sequence: {
+    options = {
+        collapse_vars: true,
+    }
+    input: {
+        var a = "FAIL";
+        try {
+            var b;
+            b[0] = (a = "PASS", 0);
+            a = 1 + a;
+        } catch (c) {
+        }
+        console.log(a);
+    }
+    expect: {
+        var a = "FAIL";
+        try {
+            var b;
+            b[0] = (a = "PASS", 0);
+            a = 1 + a;
+        } catch (c) {
+        }
+        console.log(a);
+    }
+    expect_stdout: "PASS"
+}
+
+call_assign_order: {
+    options = {
+        collapse_vars: true,
+    }
+    input: {
+        var a, b = 1, c = 0, log = console.log;
+        (function() {
+            a = b = "PASS";
+        })((b = "FAIL", c++));
+        log(a, b);
+    }
+    expect: {
+        var a, b = 1, c = 0, log = console.log;
+        (function() {
+            a = b = "PASS";
+        })((b = "FAIL", c++));
+        log(a, b);
+    }
+    expect_stdout: "PASS PASS"
+}