From: Mihai Bazon Date: Thu, 18 Oct 2012 12:14:57 +0000 (+0300) Subject: more optimizations for some break/continue cases X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=afb7faa6fadee46a6ab46232eddba2121c77549b;p=UglifyJS.git more optimizations for some break/continue cases --- 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 });