enhance `join_vars` (#5162)
authorAlex Lam S.L <alexlamsl@gmail.com>
Tue, 2 Nov 2021 03:33:24 +0000 (11:33 +0800)
committerGitHub <noreply@github.com>
Tue, 2 Nov 2021 03:33:24 +0000 (11:33 +0800)
lib/compress.js
test/compress/join_vars.js

index edc45b8..dbd75a6 100644 (file)
@@ -3476,15 +3476,26 @@ merge(Compressor.prototype, {
             var exprs = extract_exprs(body);
             if (!exprs) return;
             var trimmed = false;
-            for (var i = exprs.length - 1; --i >= 0;) {
+            for (var i = exprs.length; --i >= 0;) {
                 var expr = exprs[i];
-                if (!(expr instanceof AST_Assign)) continue;
-                if (expr.operator != "=") continue;
-                if (!(expr.left instanceof AST_SymbolRef)) continue;
-                var tail = exprs.slice(i + 1);
+                if (!can_trim(expr)) continue;
+                var tail;
+                if (expr.left instanceof AST_SymbolRef) {
+                    tail = exprs.slice(i + 1);
+                } else if (expr.left instanceof AST_PropAccess && can_trim(expr.left.expression)) {
+                    tail = exprs.slice(i + 1);
+                    var flattened = expr.clone();
+                    expr = expr.left.expression;
+                    flattened.left = flattened.left.clone();
+                    flattened.left.expression = expr.left.clone();
+                    tail.unshift(flattened);
+                } else {
+                    continue;
+                }
+                if (tail.length == 0) continue;
                 if (!trim_assigns(expr.left, expr.right, tail)) continue;
                 trimmed = true;
-                exprs = exprs.slice(0, i + 1).concat(tail);
+                exprs = exprs.slice(0, i).concat(expr, tail);
             }
             if (defn instanceof AST_Definitions) {
                 keep = keep || 0;
@@ -3498,6 +3509,10 @@ merge(Compressor.prototype, {
                 if (defn instanceof AST_Var && join_var_assign(defn.definitions, exprs, keep)) trimmed = true;
             }
             return trimmed && exprs;
+
+            function can_trim(node) {
+                return node instanceof AST_Assign && node.operator == "=";
+            }
         }
 
         function merge_assigns(prev, defn) {
@@ -3554,22 +3569,35 @@ merge(Compressor.prototype, {
         }
 
         function trim_assigns(name, value, exprs) {
+            var names = Object.create(null);
+            names[name.name] = true;
+            while (value instanceof AST_Assign && value.operator == "=") {
+                if (value.left instanceof AST_SymbolRef) names[value.left.name] = true;
+                value = value.right;
+            }
             if (!(value instanceof AST_Object)) return;
             var trimmed = false;
             do {
-                var node = exprs[0];
-                if (!(node instanceof AST_Assign)) break;
-                if (node.operator != "=") break;
-                if (!(node.left instanceof AST_PropAccess)) break;
+                if (!try_join(exprs[0])) break;
+                exprs.shift();
+                trimmed = true;
+            } while (exprs.length);
+            return trimmed;
+
+            function try_join(node) {
+                if (!(node instanceof AST_Assign)) return;
+                if (node.operator != "=") return;
+                if (!(node.left instanceof AST_PropAccess)) return;
                 var sym = node.left.expression;
-                if (!(sym instanceof AST_SymbolRef)) break;
-                if (name.name != sym.name) break;
-                if (!node.right.is_constant_expression(scope)) break;
+                if (!(sym instanceof AST_SymbolRef)) return;
+                if (!names[sym.name]) return;
+                if (!node.right.is_constant_expression(scope)) return;
                 var prop = node.left.property;
                 if (prop instanceof AST_Node) {
+                    if (try_join(prop)) prop = node.left.property = prop.right;
                     prop = prop.evaluate(compressor);
                 }
-                if (prop instanceof AST_Node) break;
+                if (prop instanceof AST_Node) return;
                 prop = "" + prop;
                 var diff = prop == "__proto__" || compressor.has_directive("use strict") ? function(node) {
                     var key = node.key;
@@ -3581,15 +3609,13 @@ merge(Compressor.prototype, {
                     }
                     return key !== "__proto__";
                 };
-                if (!all(value.properties, diff)) break;
+                if (!all(value.properties, diff)) return;
                 value.properties.push(make_node(AST_ObjectKeyVal, node, {
                     key: prop,
                     value: node.right
                 }));
-                exprs.shift();
-                trimmed = true;
-            } while (exprs.length);
-            return trimmed;
+                return true;
+            }
         }
 
         function join_consecutive_vars(statements) {
index 65e18f8..f7dd4ae 100644 (file)
@@ -489,6 +489,91 @@ join_object_assignments_regex: {
     expect_stdout: "1"
 }
 
+chained_assignments: {
+    options = {
+        join_vars: true,
+    }
+    input: {
+        var a, b = a = {};
+        b.p = "PASS";
+        console.log(a.p);
+    }
+    expect: {
+        var a, b = a = {
+            p: "PASS",
+        };
+        console.log(a.p);
+    }
+    expect_stdout: "PASS"
+}
+
+folded_assignments: {
+    options = {
+        evaluate: true,
+        join_vars: true,
+    }
+    input: {
+        var a = {};
+        a[a.PASS = 42] = "PASS";
+        console.log(a[42], a.PASS);
+    }
+    expect: {
+        var a = {
+            PASS: 42,
+            42: "PASS",
+        };
+        console.log(a[42], a.PASS);
+    }
+    expect_stdout: "PASS 42"
+}
+
+inlined_assignments: {
+    options = {
+        join_vars: true,
+        unused: true,
+    }
+    input: {
+        var a;
+        (a = {}).p = "PASS";
+        console.log(a.p);
+    }
+    expect: {
+        var a = {
+            p: "PASS",
+        };
+        console.log(a.p);
+    }
+    expect_stdout: "PASS"
+}
+
+typescript_enum: {
+    rename = true
+    options = {
+        assignments: true,
+        evaluate: true,
+        hoist_props: true,
+        inline: true,
+        join_vars: true,
+        passes: 4,
+        reduce_vars: true,
+        sequences: true,
+        side_effects: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        var Enum;
+        (function (Enum) {
+            Enum[Enum.PASS = 42] = "PASS";
+        })(Enum || (Enum = {}));
+        console.log(Enum[42], Enum.PASS);
+    }
+    expect: {
+        console.log("PASS", 42);
+    }
+    expect_stdout: "PASS 42"
+}
+
 issue_2816: {
     options = {
         join_vars: true,