enhance `loops` (#3633)
authorAlex Lam S.L <alexlamsl@gmail.com>
Tue, 10 Dec 2019 12:57:47 +0000 (12:57 +0000)
committerGitHub <noreply@github.com>
Tue, 10 Dec 2019 12:57:47 +0000 (12:57 +0000)
lib/compress.js
test/compress/loops.js

index 6d398de..fb7e66d 100644 (file)
@@ -4718,8 +4718,8 @@ merge(Compressor.prototype, {
     });
 
     function if_break_in_loop(self, compressor) {
-        var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body;
-        if (compressor.option("dead_code") && is_break(first)) {
+        var first = first_statement(self.body);
+        if (compressor.option("dead_code") && (breaks(first) || first instanceof AST_Exit)) {
             var body = [];
             if (self.init instanceof AST_Statement) {
                 body.push(self.init);
@@ -4728,10 +4728,19 @@ merge(Compressor.prototype, {
                     body: self.init
                 }));
             }
-            if (self.condition) {
+            var retain = external_target(first) || first instanceof AST_Exit;
+            if (self.condition && retain) {
+                body.push(make_node(AST_If, self, {
+                    condition: self.condition,
+                    body: first,
+                    alternative: null
+                }));
+            } else if (self.condition) {
                 body.push(make_node(AST_SimpleStatement, self.condition, {
                     body: self.condition
                 }));
+            } else if (retain) {
+                body.push(first);
             }
             extract_declarations_from_unreachable_code(self.body, body);
             return make_node(AST_BlockStatement, self, {
@@ -4739,7 +4748,8 @@ merge(Compressor.prototype, {
             });
         }
         if (first instanceof AST_If) {
-            if (is_break(first.body)) {
+            var ab = first_statement(first.body);
+            if (breaks(ab)) {
                 if (self.condition) {
                     self.condition = make_node(AST_Binary, self.condition, {
                         left: self.condition,
@@ -4749,8 +4759,12 @@ merge(Compressor.prototype, {
                 } else {
                     self.condition = first.condition.negate(compressor);
                 }
-                drop_it(first.alternative);
-            } else if (is_break(first.alternative)) {
+                var body = as_statement_array(first.alternative);
+                extract_declarations_from_unreachable_code(first.body, body);
+                return drop_it(body);
+            }
+            ab = first_statement(first.alternative);
+            if (breaks(ab)) {
                 if (self.condition) {
                     self.condition = make_node(AST_Binary, self.condition, {
                         left: self.condition,
@@ -4760,18 +4774,27 @@ merge(Compressor.prototype, {
                 } else {
                     self.condition = first.condition;
                 }
-                drop_it(first.body);
+                var body = as_statement_array(first.body);
+                extract_declarations_from_unreachable_code(first.alternative, body);
+                return drop_it(body);
             }
         }
         return self;
 
-        function is_break(node) {
+        function first_statement(body) {
+            return body instanceof AST_BlockStatement ? body.body[0] : body;
+        }
+
+        function external_target(node) {
+            return compressor.loopcontrol_target(node) !== compressor.self();
+        }
+
+        function breaks(node) {
             return node instanceof AST_Break
-                && compressor.loopcontrol_target(node) === compressor.self();
+                || node instanceof AST_Continue && external_target(node);
         }
 
         function drop_it(rest) {
-            rest = as_statement_array(rest);
             if (self.body instanceof AST_BlockStatement) {
                 self.body = self.body.clone();
                 self.body.body = rest.concat(self.body.body.slice(1));
@@ -4781,7 +4804,7 @@ merge(Compressor.prototype, {
                     body: rest
                 }).transform(compressor);
             }
-            self = if_break_in_loop(self, compressor);
+            return if_break_in_loop(self, compressor);
         }
     }
 
index 7b6002a..5f3b927 100644 (file)
@@ -574,9 +574,11 @@ issue_2740_3: {
         console.log(x, y);
     }
     expect: {
-        L1: for (var x = 0; x < 3; x++)
-            for (var y = 0; y < 2; y++)
+        L1: for (var x = 0; x < 3; x++) {
+            var y = 0;
+            if (y < 2)
                 break L1;
+        }
         console.log(x, y);
     }
     expect_stdout: "0 0"
@@ -753,3 +755,127 @@ empty_for_in_side_effects: {
         "WARN: Side effects in object of for-in loop [test/compress/loops.js:1,17]",
     ]
 }
+
+issue_3631_1: {
+    options = {
+        dead_code: true,
+        evaluate: true,
+        loops: true,
+        reduce_vars: true,
+        toplevel: true,
+    }
+    input: {
+        var c = 0;
+        L: do {
+            for (;;) continue L;
+            var b = 1;
+        } while (b && c++);
+        console.log(c);
+    }
+    expect: {
+        var c = 0;
+        do {
+            var b;
+        } while (b && c++);
+        console.log(c);
+    }
+    expect_stdout: "0"
+}
+
+issue_3631_2: {
+    options = {
+        dead_code: true,
+        evaluate: true,
+        loops: true,
+        reduce_vars: true,
+        toplevel: true,
+    }
+    input: {
+        L: for (var a = 1; a--; console.log(b)) {
+            for (;;) continue L;
+            var b = "FAIL";
+        }
+    }
+    expect: {
+        for (var a = 1; a--; console.log(b))
+            var b;
+    }
+    expect_stdout: "undefined"
+}
+
+loop_if_break: {
+    options = {
+        dead_code: true,
+        loops: true,
+    }
+    input: {
+        function f(a, b) {
+            try {
+                while (a) {
+                    if (b) {
+                        break;
+                        var c = 42;
+                        console.log(c);
+                    } else {
+                        var d = false;
+                        throw d;
+                    }
+                }
+            } catch (e) {
+                console.log("E:", e);
+            }
+            console.log(a, b, c, d);
+        }
+        f(0, 0);
+        f(0, 1);
+        f(1, 0);
+        f(1, 1);
+    }
+    expect: {
+        function f(a, b) {
+            try {
+                for (;a && !b;) {
+                    var d = false;
+                    throw d;
+                    var c;
+                }
+            } catch (e) {
+                console.log("E:", e);
+            }
+            console.log(a, b, c, d);
+        }
+        f(0, 0);
+        f(0, 1);
+        f(1, 0);
+        f(1, 1);
+    }
+    expect_stdout: [
+        "0 0 undefined undefined",
+        "0 1 undefined undefined",
+        "E: false",
+        "1 0 undefined false",
+        "1 1 undefined undefined",
+    ]
+}
+
+loop_return: {
+    options = {
+        dead_code: true,
+        loops: true,
+    }
+    input: {
+        function f(a) {
+            while (a) return 42;
+            return "foo";
+        }
+        console.log(f(0), f(1));
+    }
+    expect: {
+        function f(a) {
+            if (a) return 42;
+            return "foo";
+        }
+        console.log(f(0), f(1));
+    }
+    expect_stdout: "foo 42"
+}