From: Alex Lam S.L Date: Tue, 10 Dec 2019 12:57:47 +0000 (+0000) Subject: enhance `loops` (#3633) X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=036bca980caad83edbabca860c69515d8007bd11;p=UglifyJS.git enhance `loops` (#3633) --- diff --git a/lib/compress.js b/lib/compress.js index 6d398de2..fb7e66de 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -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); } } diff --git a/test/compress/loops.js b/test/compress/loops.js index 7b6002a2..5f3b927d 100644 --- a/test/compress/loops.js +++ b/test/compress/loops.js @@ -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" +}