return false;
};
+ function loop_body(x) {
+ if (x instanceof AST_Switch) return x;
+ if (x instanceof AST_For || x instanceof AST_ForIn || x instanceof AST_DWLoop) {
+ return (x.body instanceof AST_BlockStatement ? x.body : x);
+ }
+ return x;
+ };
+
function tighten_body(statements, compressor) {
var CHANGED;
do {
}
var ab = aborts(stat.body);
+ var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null;
if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
- || (ab instanceof AST_Continue && self === compressor.loopcontrol_target(ab.label)))) {
+ || (ab instanceof AST_Continue && self === loop_body(lct))
+ || (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
+ if (ab.label) {
+ remove(ab.label.thedef.references, ab.label);
+ }
CHANGED = true;
var body = as_statement_array(stat.body).slice(0, -1);
stat = stat.clone();
}
var ab = aborts(stat.alternative);
+ var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null;
if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
- || (ab instanceof AST_Continue && self === compressor.loopcontrol_target(ab.label)))) {
+ || (ab instanceof AST_Continue && self === loop_body(lct))
+ || (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
+ if (ab.label) {
+ remove(ab.label.thedef.references, ab.label);
+ }
CHANGED = true;
stat = stat.clone();
stat.body = make_node(AST_BlockStatement, stat.body, {
function eliminate_dead_code(statements, compressor) {
var has_quit = false;
var orig = statements.length;
+ var self = compressor.self();
statements = statements.reduce(function(a, stat){
if (has_quit) {
extract_declarations_from_unreachable_code(compressor, stat, a);
} else {
- a.push(stat);
+ if (stat instanceof AST_LoopControl) {
+ var lct = compressor.loopcontrol_target(stat.label);
+ if ((stat instanceof AST_Break
+ && lct instanceof AST_BlockStatement
+ && loop_body(lct) === self) || (stat instanceof AST_Continue
+ && loop_body(lct) === self)) {
+ if (stat.label) {
+ remove(stat.label.thedef.references, stat.label);
+ }
+ } else {
+ a.push(stat);
+ }
+ } else {
+ a.push(stat);
+ }
if (aborts(stat)) has_quit = true;
}
return a;
});
OPT(AST_LabeledStatement, function(self, compressor){
+ if (self.body instanceof AST_Break
+ && compressor.loopcontrol_target(self.body.label) === self.body) {
+ return make_node(AST_EmptyStatement, self);
+ }
return self.label.references.length == 0 ? self.body : self;
});
var last_branch = self.body[self.body.length - 1];
if (last_branch) {
var stat = last_branch.body[last_branch.body.length - 1]; // last statement
- if (stat instanceof AST_Break && compressor.loopcontrol_target(stat.label) === self)
+ if (stat instanceof AST_Break && loop_body(compressor.loopcontrol_target(stat.label)) === self)
last_branch.body.pop();
}
return self;
--- /dev/null
+labels_1: {
+ options = { if_return: true, conditionals: true, dead_code: true };
+ input: {
+ out: {
+ if (foo) break out;
+ console.log("bar");
+ }
+ };
+ expect: {
+ foo || console.log("bar");
+ }
+}
+
+labels_2: {
+ options = { if_return: true, conditionals: true, dead_code: true };
+ input: {
+ out: {
+ if (foo) print("stuff");
+ else break out;
+ console.log("here");
+ }
+ };
+ expect: {
+ if (foo) {
+ print("stuff");
+ console.log("here");
+ }
+ }
+}
+
+labels_3: {
+ options = { if_return: true, conditionals: true, dead_code: true };
+ input: {
+ for (var i = 0; i < 5; ++i) {
+ if (i < 3) continue;
+ console.log(i);
+ }
+ };
+ expect: {
+ for (var i = 0; i < 5; ++i)
+ i < 3 || console.log(i);
+ }
+}
+
+labels_4: {
+ options = { if_return: true, conditionals: true, dead_code: true };
+ input: {
+ out: for (var i = 0; i < 5; ++i) {
+ if (i < 3) continue out;
+ console.log(i);
+ }
+ };
+ expect: {
+ for (var i = 0; i < 5; ++i)
+ i < 3 || console.log(i);
+ }
+}
+
+labels_5: {
+ options = { if_return: true, conditionals: true, dead_code: true };
+ // should keep the break-s in the following
+ input: {
+ while (foo) {
+ if (bar) break;
+ console.log("foo");
+ }
+ out: while (foo) {
+ if (bar) break out;
+ console.log("foo");
+ }
+ };
+ expect: {
+ while (foo) {
+ if (bar) break;
+ console.log("foo");
+ }
+ out: while (foo) {
+ if (bar) break out;
+ console.log("foo");
+ }
+ }
+}
+
+labels_6: {
+ input: {
+ out: break out;
+ };
+ expect: {}
+}
+
+labels_7: {
+ options = { if_return: true, conditionals: true, dead_code: true };
+ input: {
+ while (foo) {
+ x();
+ y();
+ continue;
+ }
+ };
+ expect: {
+ while (foo) {
+ x();
+ y();
+ }
+ }
+}
+
+labels_8: {
+ options = { if_return: true, conditionals: true, dead_code: true };
+ input: {
+ while (foo) {
+ x();
+ y();
+ break;
+ }
+ };
+ expect: {
+ while (foo) {
+ x();
+ y();
+ break;
+ }
+ }
+}
+
+labels_9: {
+ options = { if_return: true, conditionals: true, dead_code: true };
+ input: {
+ out: while (foo) {
+ x();
+ y();
+ continue out;
+ z();
+ k();
+ }
+ };
+ expect: {
+ while (foo) {
+ x();
+ y();
+ }
+ }
+}
+
+labels_10: {
+ options = { if_return: true, conditionals: true, dead_code: true };
+ input: {
+ out: while (foo) {
+ x();
+ y();
+ break out;
+ z();
+ k();
+ }
+ };
+ expect: {
+ out: while (foo) {
+ x();
+ y();
+ break out;
+ }
+ }
+}
var cmp = new U.Compressor(options, true);
var expect = make_code(as_toplevel(test.expect), false);
var input = as_toplevel(test.input);
+ var input_code = make_code(test.input);
var output = input.transform(cmp);
output.figure_out_scope();
output = make_code(output, false);
if (expect != output) {
log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", {
- input: make_code(test.input),
+ input: input_code,
output: output,
expected: expect
});