enhance `loops` (#4180)
authorAlex Lam S.L <alexlamsl@gmail.com>
Mon, 5 Oct 2020 00:26:59 +0000 (01:26 +0100)
committerGitHub <noreply@github.com>
Mon, 5 Oct 2020 00:26:59 +0000 (08:26 +0800)
lib/compress.js
test/compress/loops.js

index cb8b0ca..4534d9c 100644 (file)
@@ -1180,6 +1180,16 @@ merge(Compressor.prototype, {
             || node instanceof AST_Undefined;
     }
 
+    function declarations_only(node) {
+        return all(node.definitions, function(var_def) {
+            return !var_def.value;
+        });
+    }
+
+    function is_declaration(stat) {
+        return stat instanceof AST_Defun || stat instanceof AST_Var && declarations_only(stat);
+    }
+
     function tighten_body(statements, compressor) {
         var in_loop, in_try, scope;
         find_loop_scope_try();
@@ -2407,16 +2417,6 @@ merge(Compressor.prototype, {
             });
         }
 
-        function declarations_only(node) {
-            return all(node.definitions, function(var_def) {
-                return !var_def.value;
-            });
-        }
-
-        function is_declaration(stat) {
-            return stat instanceof AST_Defun || stat instanceof AST_Var && declarations_only(stat);
-        }
-
         function sequencesize(statements, compressor) {
             if (statements.length < 2) return;
             var seq = [], n = 0;
@@ -4268,13 +4268,17 @@ merge(Compressor.prototype, {
         return self;
     });
 
+    function trim_block(stat) {
+        switch (stat.body.length) {
+          case 1: return stat.body[0];
+          case 0: return make_node(AST_EmptyStatement, stat);
+        }
+        return stat;
+    }
+
     OPT(AST_BlockStatement, function(self, compressor) {
         self.body = tighten_body(self.body, compressor);
-        switch (self.body.length) {
-          case 1: return self.body[0];
-          case 0: return make_node(AST_EmptyStatement, self);
-        }
-        return self;
+        return trim_block(self);
     });
 
     OPT(AST_Function, function(self, compressor) {
@@ -5847,6 +5851,33 @@ merge(Compressor.prototype, {
                 }).optimize(compressor);
             }
         }
+        if (self.body instanceof AST_BlockStatement) {
+            var body = self.body.body;
+            for (var i = body.length; --i >= 0;) {
+                var stat = body[i];
+                if (stat instanceof AST_If
+                    && !stat.alternative
+                    && stat.body instanceof AST_Break
+                    && compressor.loopcontrol_target(stat.body) === compressor.self()) {
+                    self.condition = make_node(AST_Binary, self, {
+                        operator: "&&",
+                        left: stat.condition.negate(compressor),
+                        right: self.condition,
+                    });
+                    body.splice(i, 1);
+                } else if (stat instanceof AST_SimpleStatement) {
+                    self.condition = make_sequence(self, [
+                        stat.body,
+                        self.condition,
+                    ]);
+                    body.splice(i, 1);
+                } else if (!is_declaration(stat)) {
+                    break;
+                }
+            }
+            self.body = trim_block(self.body);
+        }
+        if (self.body instanceof AST_EmptyStatement) return make_node(AST_For, self, self).optimize(compressor);
         if (self.body instanceof AST_SimpleStatement) return make_node(AST_For, self, {
             condition: make_sequence(self.condition, [
                 self.body.body,
index cbc1166..b8d351a 100644 (file)
@@ -201,7 +201,7 @@ evaluate: {
     }
 }
 
-issue_1532: {
+issue_1532_1: {
     options = {
         evaluate: true,
         loops: true,
@@ -210,18 +210,56 @@ issue_1532: {
         function f(x, y) {
             do {
                 if (x) break;
-                foo();
+                console.log(y);
             } while (false);
         }
+        f(null, "PASS");
+        f(42, "FAIL");
     }
     expect: {
+        function f(x, y) {
+            for (; !x && (console.log(y), false););
+        }
+        f(null, "PASS");
+        f(42, "FAIL");
+    }
+    expect_stdout: "PASS"
+}
+
+issue_1532_2: {
+    options = {
+        evaluate: true,
+        loops: true,
+    }
+    input: {
         function f(x, y) {
             do {
-                if (x) break;
-                foo();
+                if (x) {
+                    console.log(x);
+                    break;
+                }
+                console.log(y);
             } while (false);
         }
+        f(null, "PASS");
+        f(42, "FAIL");
+    }
+    expect: {
+        function f(x, y) {
+            do {
+                if (x) {
+                    console.log(x);
+                    break;
+                }
+            } while (console.log(y), false);
+        }
+        f(null, "PASS");
+        f(42, "FAIL");
     }
+    expect_stdout: [
+        "PASS",
+        "42",
+    ]
 }
 
 issue_186: {