aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMihai Bazon <mihai@bazon.net>2012-10-18 15:14:57 +0300
committerMihai Bazon <mihai@bazon.net>2012-10-18 15:14:57 +0300
commitafb7faa6fadee46a6ab46232eddba2121c77549b (patch)
tree5d472b568192e15812dbf74c63790abf78ce984b
parent6aa56f92fe07edfc677d390a2e26b37c98da0968 (diff)
downloadtracifyjs-afb7faa6fadee46a6ab46232eddba2121c77549b.tar.gz
tracifyjs-afb7faa6fadee46a6ab46232eddba2121c77549b.zip
more optimizations for some break/continue cases
-rw-r--r--lib/ast.js8
-rw-r--r--lib/compress.js45
-rw-r--r--lib/utils.js6
-rw-r--r--test/compress/labels.js163
-rwxr-xr-xtest/run-tests.js3
5 files changed, 216 insertions, 9 deletions
diff --git a/lib/ast.js b/lib/ast.js
index 177bd1d7..44cbed12 100644
--- a/lib/ast.js
+++ b/lib/ast.js
@@ -929,10 +929,10 @@ TreeWalker.prototype = {
} else {
for (var i = stack.length; --i >= 0;) {
var x = stack[i];
- 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);
- }
+ if (x instanceof AST_Switch
+ || x instanceof AST_For
+ || x instanceof AST_ForIn
+ || x instanceof AST_DWLoop) return x;
}
}
}
diff --git a/lib/compress.js b/lib/compress.js
index 1af4a2d6..cde4b6f7 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -186,6 +186,14 @@ merge(Compressor.prototype, {
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 {
@@ -303,8 +311,13 @@ merge(Compressor.prototype, {
}
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();
@@ -320,8 +333,13 @@ merge(Compressor.prototype, {
}
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, {
@@ -347,11 +365,26 @@ merge(Compressor.prototype, {
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;
@@ -795,6 +828,10 @@ merge(Compressor.prototype, {
});
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;
});
@@ -1227,7 +1264,7 @@ merge(Compressor.prototype, {
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;
diff --git a/lib/utils.js b/lib/utils.js
index 79039665..4d3d60f6 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -166,6 +166,12 @@ function string_template(text, props) {
});
};
+function remove(array, el) {
+ for (var i = array.length; --i >= 0;) {
+ if (array[i] === el) array.splice(i, 1);
+ }
+};
+
function mergeSort(array, cmp) {
if (array.length < 2) return array.slice();
function merge(a, b) {
diff --git a/test/compress/labels.js b/test/compress/labels.js
new file mode 100644
index 00000000..044b7a7e
--- /dev/null
+++ b/test/compress/labels.js
@@ -0,0 +1,163 @@
+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;
+ }
+ }
+}
diff --git a/test/run-tests.js b/test/run-tests.js
index 001140f3..0568c6a7 100755
--- a/test/run-tests.js
+++ b/test/run-tests.js
@@ -73,12 +73,13 @@ function run_compress_tests() {
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
});