diff options
author | Mihai Bazon <mihai@bazon.net> | 2012-11-07 18:57:51 +0200 |
---|---|---|
committer | Mihai Bazon <mihai@bazon.net> | 2012-11-08 11:43:14 +0200 |
commit | 1a5fd3e05294a74900c6265f364f233e3f05eba0 (patch) | |
tree | 44dedf148901474390eb3b34e2a38364575b72a3 | |
parent | 5a7e54cf724d28eab424acd92f097207ee69afbd (diff) | |
download | tracifyjs-1a5fd3e05294a74900c6265f364f233e3f05eba0.tar.gz tracifyjs-1a5fd3e05294a74900c6265f364f233e3f05eba0.zip |
optimization for if/break as first statement in a loop body
for(...; x; ...) if (y) break; → for(...; x&&!y; ...);
similarly for `while` and some combinations (i.e. the `break` appears in the
`else` clause, etc.)
-rw-r--r-- | lib/compress.js | 49 | ||||
-rw-r--r-- | test/compress/loops.js | 123 |
2 files changed, 171 insertions, 1 deletions
diff --git a/lib/compress.js b/lib/compress.js index 59c9128d..1571173c 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -1118,11 +1118,57 @@ merge(Compressor.prototype, { return self; }); + function if_break_in_loop(self, compressor) { + 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)); + self.body = self.body.transform(compressor); + } else { + self.body = make_node(AST_BlockStatement, self.body, { + body: rest + }).transform(compressor); + } + if_break_in_loop(self, compressor); + } + 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) === self) { + if (self.condition) { + self.condition = make_node(AST_Binary, self.condition, { + left: self.condition, + operator: "&&", + right: first.condition.negate(compressor), + }); + } else { + self.condition = first.condition.negate(compressor); + } + drop_it(first.alternative); + } + else if (first.alternative instanceof AST_Break + && compressor.loopcontrol_target(first.alternative.label) === self) { + if (self.condition) { + self.condition = make_node(AST_Binary, self.condition, { + left: self.condition, + operator: "&&", + right: first.condition, + }); + } else { + self.condition = first.condition; + } + drop_it(first.body); + } + } + }; + OPT(AST_While, function(self, compressor) { if (!compressor.option("loops")) return self; self = AST_DWLoop.prototype.optimize.call(self, compressor); if (self instanceof AST_While) { - self = make_node(AST_For, self, self); + if_break_in_loop(self, compressor); + self = make_node(AST_For, self, self).transform(compressor); } return self; }); @@ -1151,6 +1197,7 @@ merge(Compressor.prototype, { } } } + if_break_in_loop(self, compressor); return self; }); diff --git a/test/compress/loops.js b/test/compress/loops.js new file mode 100644 index 00000000..cdf1f045 --- /dev/null +++ b/test/compress/loops.js @@ -0,0 +1,123 @@ +while_becomes_for: { + options = { loops: true }; + input: { + while (foo()) bar(); + } + expect: { + for (; foo(); ) bar(); + } +} + +drop_if_break_1: { + options = { loops: true }; + input: { + for (;;) + if (foo()) break; + } + expect: { + for (; !foo();); + } +} + +drop_if_break_2: { + options = { loops: true }; + input: { + for (;bar();) + if (foo()) break; + } + expect: { + for (; bar() && !foo();); + } +} + +drop_if_break_3: { + options = { loops: true }; + input: { + for (;bar();) { + if (foo()) break; + stuff1(); + stuff2(); + } + } + expect: { + for (; bar() && !foo();) { + stuff1(); + stuff2(); + } + } +} + +drop_if_break_4: { + options = { loops: true, sequences: true }; + input: { + for (;bar();) { + x(); + y(); + if (foo()) break; + z(); + k(); + } + } + expect: { + for (; bar() && (x(), y(), !foo());) z(), k(); + } +} + +drop_if_else_break_1: { + options = { loops: true }; + input: { + for (;;) if (foo()) bar(); else break; + } + expect: { + for (; foo(); ) bar(); + } +} + +drop_if_else_break_2: { + options = { loops: true }; + input: { + for (;bar();) { + if (foo()) baz(); + else break; + } + } + expect: { + for (; bar() && foo();) baz(); + } +} + +drop_if_else_break_3: { + options = { loops: true }; + input: { + for (;bar();) { + if (foo()) baz(); + else break; + stuff1(); + stuff2(); + } + } + expect: { + for (; bar() && foo();) { + baz(); + stuff1(); + stuff2(); + } + } +} + +drop_if_else_break_4: { + options = { loops: true, sequences: true }; + input: { + for (;bar();) { + x(); + y(); + if (foo()) baz(); + else break; + z(); + k(); + } + } + expect: { + for (; bar() && (x(), y(), foo());) baz(), z(), k(); + } +} |