aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Lam S.L <alexlamsl@gmail.com>2019-12-10 12:57:47 +0000
committerGitHub <noreply@github.com>2019-12-10 12:57:47 +0000
commit036bca980caad83edbabca860c69515d8007bd11 (patch)
tree2232cc3d6fa3c72916f244a5f6440f733f868f3e
parent18c2b1841bf0bfa6c827eed53238d0499c143b23 (diff)
downloadtracifyjs-036bca980caad83edbabca860c69515d8007bd11.tar.gz
tracifyjs-036bca980caad83edbabca860c69515d8007bd11.zip
enhance `loops` (#3633)
-rw-r--r--lib/compress.js45
-rw-r--r--test/compress/loops.js130
2 files changed, 162 insertions, 13 deletions
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"
+}