handle flow control in loops with `reduce_vars` (#3069)
authorAlex Lam S.L <alexlamsl@gmail.com>
Mon, 9 Apr 2018 22:51:03 +0000 (06:51 +0800)
committerGitHub <noreply@github.com>
Mon, 9 Apr 2018 22:51:03 +0000 (06:51 +0800)
fixes #3068

lib/compress.js
test/compress/reduce_vars.js

index 73f4632..5323a86 100644 (file)
@@ -530,11 +530,16 @@ merge(Compressor.prototype, {
             tw.safe_ids = save_ids;
             return true;
         });
-        def(AST_DWLoop, function(tw, descend) {
+        def(AST_Do, function(tw) {
             var saved_loop = tw.in_loop;
             tw.in_loop = this;
             push(tw);
-            descend();
+            this.body.walk(tw);
+            if (has_break_or_continue(this)) {
+                pop(tw);
+                push(tw);
+            }
+            this.condition.walk(tw);
             pop(tw);
             tw.in_loop = saved_loop;
             return true;
@@ -543,19 +548,17 @@ merge(Compressor.prototype, {
             if (this.init) this.init.walk(tw);
             var saved_loop = tw.in_loop;
             tw.in_loop = this;
-            if (this.condition) {
-                push(tw);
-                this.condition.walk(tw);
-                pop(tw);
-            }
             push(tw);
+            if (this.condition) this.condition.walk(tw);
             this.body.walk(tw);
-            pop(tw);
             if (this.step) {
-                push(tw);
+                if (has_break_or_continue(this)) {
+                    pop(tw);
+                    push(tw);
+                }
                 this.step.walk(tw);
-                pop(tw);
             }
+            pop(tw);
             tw.in_loop = saved_loop;
             return true;
         });
@@ -713,6 +716,15 @@ merge(Compressor.prototype, {
                 }
             }
         });
+        def(AST_While, function(tw, descend) {
+            var saved_loop = tw.in_loop;
+            tw.in_loop = this;
+            push(tw);
+            descend();
+            pop(tw);
+            tw.in_loop = saved_loop;
+            return true;
+        });
     })(function(node, func){
         node.DEFMETHOD("reduce_vars", func);
     });
@@ -3837,6 +3849,20 @@ merge(Compressor.prototype, {
         return compressor.option("loops") ? make_node(AST_For, self, self).optimize(compressor) : self;
     });
 
+    function has_break_or_continue(loop, parent) {
+        var found = false;
+        var tw = new TreeWalker(function(node) {
+            if (found || node instanceof AST_Scope) return true;
+            if (node instanceof AST_LoopControl && tw.loopcontrol_target(node) === loop) {
+                return found = true;
+            }
+        });
+        if (parent instanceof AST_LabeledStatement) tw.push(parent);
+        tw.push(loop);
+        loop.body.walk(tw);
+        return found;
+    }
+
     OPT(AST_Do, function(self, compressor){
         if (!compressor.option("loops")) return self;
         var cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
@@ -3851,22 +3877,16 @@ merge(Compressor.prototype, {
                     ]
                 })
             }).optimize(compressor);
-            var has_loop_control = false;
-            var tw = new TreeWalker(function(node) {
-                if (node instanceof AST_Scope || has_loop_control) return true;
-                if (node instanceof AST_LoopControl && tw.loopcontrol_target(node) === self)
-                    return has_loop_control = true;
-            });
-            var parent = compressor.parent();
-            (parent instanceof AST_LabeledStatement ? parent : self).walk(tw);
-            if (!has_loop_control) return make_node(AST_BlockStatement, self.body, {
-                body: [
-                    self.body,
-                    make_node(AST_SimpleStatement, self.condition, {
-                        body: self.condition
-                    })
-                ]
-            }).optimize(compressor);
+            if (!has_break_or_continue(self, compressor.parent())) {
+                return make_node(AST_BlockStatement, self.body, {
+                    body: [
+                        self.body,
+                        make_node(AST_SimpleStatement, self.condition, {
+                            body: self.condition
+                        })
+                    ]
+                }).optimize(compressor);
+            }
         }
         if (self.body instanceof AST_SimpleStatement) return make_node(AST_For, self, {
             condition: make_sequence(self.condition, [
index 59a99b9..63a17e7 100644 (file)
@@ -5654,3 +5654,59 @@ issue_3042_2: {
         "true",
     ]
 }
+
+issue_3068_1: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+    }
+    input: {
+        (function() {
+            do {
+                continue;
+                var b = "defined";
+            } while (b && b.c);
+        })();
+    }
+    expect: {
+        (function() {
+            do {
+                continue;
+                var b = "defined";
+            } while (b && b.c);
+        })();
+    }
+    expect_stdout: true
+}
+
+issue_3068_2: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+    }
+    input: {
+        (function() {
+            do {
+                try {
+                    while ("" == typeof a);
+                } finally {
+                    continue;
+                }
+                var b = "defined";
+            } while (b && b.c);
+        })();
+    }
+    expect: {
+        (function() {
+            do {
+                try {
+                    while ("" == typeof a);
+                } finally {
+                    continue;
+                }
+                var b = "defined";
+            } while (b && b.c);
+        })();
+    }
+    expect_stdout: true
+}