enhance `dead_code` (#3990)
authorAlex Lam S.L <alexlamsl@gmail.com>
Thu, 11 Jun 2020 18:16:13 +0000 (19:16 +0100)
committerGitHub <noreply@github.com>
Thu, 11 Jun 2020 18:16:13 +0000 (02:16 +0800)
lib/compress.js
test/compress/dead-code.js

index 8d8dffa..2a1399d 100644 (file)
@@ -571,7 +571,14 @@ merge(Compressor.prototype, {
         });
         def(AST_Assign, function(tw, descend, compressor) {
             var node = this;
+            var eq = node.operator == "=";
             var sym = node.left;
+            if (eq && sym.equivalent_to(node.right) && !sym.has_side_effects(compressor)) {
+                node.right.walk(tw);
+                walk_prop(sym);
+                node.__drop = true;
+                return true;
+            }
             if (!(sym instanceof AST_SymbolRef)) {
                 mark_assignment_to_arguments(sym);
                 return;
@@ -579,7 +586,6 @@ merge(Compressor.prototype, {
             var d = sym.definition();
             d.assignments++;
             var fixed = d.fixed;
-            var eq = node.operator == "=";
             var value = eq ? node.right : node;
             if (is_modified(compressor, tw, node, value, 0)) return;
             var safe = eq || safe_to_read(tw, d);
@@ -609,6 +615,21 @@ merge(Compressor.prototype, {
                 d.fixed = false;
             }
             return true;
+
+            function walk_prop(node) {
+                if (node instanceof AST_Dot) {
+                    walk_prop(node.expression);
+                } else if (node instanceof AST_Sub) {
+                    walk_prop(node.expression);
+                    node.property.walk(tw);
+                } else if (node instanceof AST_SymbolRef) {
+                    var d = node.definition();
+                    push_ref(d, node);
+                    node.fixed = d.fixed;
+                } else {
+                    node.walk(tw);
+                }
+            }
         });
         def(AST_Binary, function(tw) {
             if (!lazy_op[this.operator]) return;
@@ -7843,8 +7864,15 @@ merge(Compressor.prototype, {
         if (compressor.option("dead_code")) {
             if (self.left instanceof AST_PropAccess) {
                 if (self.operator == "=") {
-                    if (self.left.equivalent_to(self.right)
-                        && (self.left instanceof AST_Dot || !self.left.property.has_side_effects(compressor))) {
+                    if (self.__drop) {
+                        var props = [];
+                        flatten(self.left, props);
+                        flatten(self.right, props);
+                        return props.length == 0 ? make_node(AST_Number, self, {
+                            value: 0
+                        }) : make_sequence(self, props).optimize(compressor);
+                    }
+                    if (self.left.equivalent_to(self.right) && !self.left.has_side_effects(compressor)) {
                         return self.right;
                     }
                     var exp = self.left.expression;
@@ -7934,6 +7962,11 @@ merge(Compressor.prototype, {
         }
         return try_evaluate(compressor, self);
 
+        function flatten(node, props) {
+            if (!(node.expression instanceof AST_SymbolRef)) props.push(node.expression);
+            if (node instanceof AST_Sub) props.push(node.property);
+        }
+
         function in_try(level, node) {
             var right = self.right;
             self.right = make_node(AST_Null, right);
index 5506b95..a2ebf37 100644 (file)
@@ -1169,29 +1169,131 @@ redundant_assignments: {
     expect_stdout: "PASS PASS"
 }
 
-self_assignments: {
+self_assignments_1: {
     options = {
         dead_code: true,
     }
     input: {
-        var a = "PASS", b = 0, l = [ "FAIL", "PASS" ], o = { p: "PASS" };
+        var a = "PASS";
         a = a;
-        l[0] = l[0];
-        l[b] = l[b];
-        l[b++] = l[b++];
-        o.p = o.p;
-        console.log(a, b, l[0], o.p);
+        console.log(a);
     }
     expect: {
-        var a = "PASS", b = 0, l = [ "FAIL", "PASS" ], o = { p: "PASS" };
+        var a = "PASS";
         a;
-        l[0];
-        l[b];
-        l[b++] = l[b++];
-        o.p;
-        console.log(a, b, l[0], o.p);
+        console.log(a);
+    }
+    expect_stdout: "PASS"
+}
+
+self_assignments_2: {
+    options = {
+        dead_code: true,
+        pure_getters: "strict",
+        reduce_vars: true,
+        side_effects: true,
+        toplevel: true,
+    }
+    input: {
+        var a = "q", o = {
+            p: "PASS",
+        };
+        o.p = o.p;
+        o[a] = o[a];
+        console.log(o.p, o[a]);
+    }
+    expect: {
+        var a = "q", o = {
+            p: "PASS",
+        };
+        console.log(o.p, o[a]);
+    }
+    expect_stdout: "PASS undefined"
+}
+
+self_assignments_3: {
+    options = {
+        dead_code: true,
+        pure_getters: "strict",
+        reduce_vars: true,
+        side_effects: true,
+        toplevel: true,
+    }
+    input: {
+        var a = "q", o = {
+            p: "FAIL",
+            get q() {
+                return "PASS";
+            },
+            set q(v) {
+                this.p = v;
+            },
+        };
+        o.p = o.p;
+        o[a] = o[a];
+        console.log(o.p, o[a]);
+    }
+    expect: {
+        var a = "q", o = {
+            p: "FAIL",
+            get q() {
+                return "PASS";
+            },
+            set q(v) {
+                this.p = v;
+            },
+        };
+        o.p = o.p;
+        o[a] = o[a];
+        console.log(o.p, o[a]);
+    }
+    expect_stdout: "PASS PASS"
+}
+
+self_assignments_4: {
+    options = {
+        dead_code: true,
+        pure_getters: "strict",
+        reduce_vars: true,
+        side_effects: true,
+        toplevel: true,
+    }
+    input: {
+        var i = 0, l = [ "PASS" ];
+        l[0] = l[0];
+        l[i] = l[i];
+        console.log(l[0], i);
+    }
+    expect: {
+        var i = 0, l = [ "PASS" ];
+        console.log(l[0], i);
+    }
+    expect_stdout: "PASS 0"
+}
+
+self_assignments_5: {
+    options = {
+        dead_code: true,
+        evaluate: true,
+        passes: 3,
+        pure_getters: "strict",
+        reduce_vars: true,
+        side_effects: true,
+        toplevel: true,
+    }
+    input: {
+        var i = 0, l = [ "FAIL", "PASS" ];
+        l[0] = l[0];
+        l[i] = l[i];
+        l[i++] = l[i++];
+        console.log(l[0], i);
+    }
+    expect: {
+        var i = 0, l = [ "FAIL", "PASS" ];
+        l[0] = l[1];
+        console.log(l[0], 2);
     }
-    expect_stdout: "PASS 2 PASS PASS"
+    expect_stdout: "PASS 2"
 }
 
 issue_3967: {