From 9b6bc67c3393507d0621eb3debbe8845b0eff52d Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Tue, 4 Apr 2017 23:48:22 +0800 Subject: [PATCH] optimise `do{...}while(false)` (#1785) - better heuristics to avoid issues like #1532 - fix `TreeWalker.loopcontrol_target()` - `continue` cannot refer to `switch` blocks --- lib/ast.js | 10 +++++----- lib/compress.js | 32 ++++++++++++++++++++------------ test/compress/loops.js | 26 ++++++++++++++++++++++++-- 3 files changed, 49 insertions(+), 19 deletions(-) diff --git a/lib/ast.js b/lib/ast.js index 092a9590..ba1330f4 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -1035,16 +1035,16 @@ TreeWalker.prototype = { self = p; } }, - loopcontrol_target: function(label) { + loopcontrol_target: function(node) { var stack = this.stack; - if (label) for (var i = stack.length; --i >= 0;) { + if (node.label) for (var i = stack.length; --i >= 0;) { var x = stack[i]; - if (x instanceof AST_LabeledStatement && x.label.name == label.name) { + if (x instanceof AST_LabeledStatement && x.label.name == node.label.name) return x.body; - } } else for (var i = stack.length; --i >= 0;) { var x = stack[i]; - if (x instanceof AST_Switch || x instanceof AST_IterationStatement) + if (x instanceof AST_IterationStatement + || node instanceof AST_Break && x instanceof AST_Switch) return x; } } diff --git a/lib/compress.js b/lib/compress.js index 79e7d4d2..14083fe8 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -893,7 +893,7 @@ merge(Compressor.prototype, { } var ab = aborts(stat.body); - var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null; + var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null; if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda) || (ab instanceof AST_Continue && self === loop_body(lct)) || (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) { @@ -915,7 +915,7 @@ merge(Compressor.prototype, { } var ab = aborts(stat.alternative); - var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null; + var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null; if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda) || (ab instanceof AST_Continue && self === loop_body(lct)) || (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) { @@ -964,7 +964,7 @@ merge(Compressor.prototype, { extract_declarations_from_unreachable_code(compressor, stat, a); } else { if (stat instanceof AST_LoopControl) { - var lct = compressor.loopcontrol_target(stat.label); + var lct = compressor.loopcontrol_target(stat); if ((stat instanceof AST_Break && !(lct instanceof AST_IterationStatement) && loop_body(lct) === self) || (stat instanceof AST_Continue @@ -1746,7 +1746,7 @@ merge(Compressor.prototype, { OPT(AST_LabeledStatement, function(self, compressor){ if (self.body instanceof AST_Break - && compressor.loopcontrol_target(self.body.label) === self.body) { + && compressor.loopcontrol_target(self.body) === self.body) { return make_node(AST_EmptyStatement, self); } return self.label.references.length == 0 ? self.body : self; @@ -2314,13 +2314,21 @@ merge(Compressor.prototype, { return make_node(AST_For, self, { body: self.body }); - } else if (compressor.option("dead_code") && self instanceof AST_While) { + } + if (compressor.option("dead_code") && self instanceof AST_While) { var a = []; extract_declarations_from_unreachable_code(compressor, self.body, a); return make_node(AST_BlockStatement, self, { body: a }); - } else { - cond = make_node_from_constant(cond, self.condition).transform(compressor); - self.condition = best_of_expression(cond, self.condition); + } + if (self instanceof AST_Do) { + 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; + }); + self.walk(tw); + if (!has_loop_control) return self.body; } } if (self instanceof AST_While) { @@ -2346,7 +2354,7 @@ merge(Compressor.prototype, { var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body; if (first instanceof AST_If) { if (first.body instanceof AST_Break - && compressor.loopcontrol_target(first.body.label) === compressor.self()) { + && compressor.loopcontrol_target(first.body) === compressor.self()) { if (self.condition) { self.condition = make_node(AST_Binary, self.condition, { left: self.condition, @@ -2359,7 +2367,7 @@ merge(Compressor.prototype, { drop_it(first.alternative); } else if (first.alternative instanceof AST_Break - && compressor.loopcontrol_target(first.alternative.label) === compressor.self()) { + && compressor.loopcontrol_target(first.alternative) === compressor.self()) { if (self.condition) { self.condition = make_node(AST_Binary, self.condition, { left: self.condition, @@ -2590,7 +2598,7 @@ merge(Compressor.prototype, { self.body = body; while (branch = body[body.length - 1]) { var stat = branch.body[branch.body.length - 1]; - if (stat instanceof AST_Break && compressor.loopcontrol_target(stat.label) === self) + if (stat instanceof AST_Break && compressor.loopcontrol_target(stat) === self) branch.body.pop(); if (branch.body.length || branch instanceof AST_Case && (default_branch || branch.expression.has_side_effects(compressor))) break; @@ -2609,7 +2617,7 @@ merge(Compressor.prototype, { if (has_break || node instanceof AST_Lambda || node instanceof AST_SimpleStatement) return true; - if (node instanceof AST_Break && tw.loopcontrol_target(node.label) === self) + if (node instanceof AST_Break && tw.loopcontrol_target(node) === self) has_break = true; }); self.walk(tw); diff --git a/test/compress/loops.js b/test/compress/loops.js index c8d77840..f13f5cc5 100644 --- a/test/compress/loops.js +++ b/test/compress/loops.js @@ -215,8 +215,7 @@ evaluate: { a(); for(;;) c(); - // rule disabled due to issue_1532 - do d(); while (false); + d(); } } @@ -458,3 +457,26 @@ issue_1648: { } expect_exact: "function f(){for(x();1;);}" } + +do_switch: { + options = { + evaluate: true, + loops: true, + } + input: { + do { + switch (a) { + case b: + continue; + } + } while (false); + } + expect: { + do { + switch (a) { + case b: + continue; + } + } while (false); + } +} -- 2.34.1