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.)
This commit is contained in:
@@ -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;
|
||||
});
|
||||
|
||||
|
||||
123
test/compress/loops.js
Normal file
123
test/compress/loops.js
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user