From 673b0716379e261008b6cbf187e6b212104fb69e Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Mon, 1 Jan 2018 00:09:26 +0800 Subject: enhance `join_vars` & `sequences` (#2697) - nudge declarations without assignments - within `AST_BlockStatement` - across `AST_If` --- lib/compress.js | 90 +++++++++++++++++++++++++++++++++++-------- test/compress/conditionals.js | 21 ++++++++++ test/compress/functions.js | 2 +- test/compress/sequences.js | 63 ++++++++++++++++++++++++++++++ 4 files changed, 159 insertions(+), 17 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 56176e55..ee80901e 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -884,6 +884,7 @@ merge(Compressor.prototype, { } if (compressor.sequences_limit > 0) { sequencesize(statements, compressor); + sequencesize_2(statements, compressor); } if (compressor.option("join_vars")) { join_consecutive_vars(statements, compressor); @@ -1537,6 +1538,12 @@ merge(Compressor.prototype, { }); } + function declarations_only(node) { + return all(node.definitions, function(var_def) { + return !var_def.value; + }); + } + function sequencesize(statements, compressor) { if (statements.length < 2) return; var seq = [], n = 0; @@ -1553,6 +1560,9 @@ merge(Compressor.prototype, { var body = stat.body; if (seq.length > 0) body = body.drop_side_effect_free(compressor); if (body) merge_sequence(seq, body); + } else if (stat instanceof AST_Definitions && declarations_only(stat) + || stat instanceof AST_Defun) { + statements[n++] = stat; } else { push_seq(); statements[n++] = stat; @@ -1560,13 +1570,31 @@ merge(Compressor.prototype, { } push_seq(); statements.length = n; - sequencesize_2(statements, compressor); - CHANGED = statements.length != len; + if (n != len) CHANGED = true; + } + + function to_simple_statement(block, decls) { + if (!(block instanceof AST_BlockStatement)) return block; + var defs = []; + var stat = null; + for (var i = 0, len = block.body.length; i < len; i++) { + var line = block.body[i]; + if (line instanceof AST_Definitions && declarations_only(line)) { + defs.push(line); + } else if (stat) { + return false; + } else { + stat = line; + } + } + [].push.apply(decls, defs); + return stat; } function sequencesize_2(statements, compressor) { function cons_seq(right) { n--; + CHANGED = true; var left = prev.body; return make_sequence(left, [ left, right ]).transform(compressor); }; @@ -1588,6 +1616,7 @@ merge(Compressor.prototype, { else { stat.init = prev.body; n--; + CHANGED = true; } } } @@ -1607,6 +1636,22 @@ merge(Compressor.prototype, { stat.expression = cons_seq(stat.expression); } } + if (compressor.option("conditionals") && stat instanceof AST_If) { + var decls = []; + var body = to_simple_statement(stat.body, decls); + var alt = to_simple_statement(stat.alternative, decls); + if (body !== false && alt !== false && decls.length > 0) { + decls.push(make_node(AST_If, stat, { + condition: stat.condition, + body: body || make_node(AST_EmptyStatement, stat.body), + alternative: alt + })); + stat = make_node(AST_BlockStatement, stat, { + body: decls + }); + CHANGED = true; + } + } statements[n++] = stat; prev = stat instanceof AST_SimpleStatement ? stat : null; } @@ -1614,30 +1659,43 @@ merge(Compressor.prototype, { } function join_consecutive_vars(statements, compressor) { + var defs; for (var i = 0, j = -1, len = statements.length; i < len; i++) { var stat = statements[i]; var prev = statements[j]; - if (stat instanceof AST_Definitions && prev && prev.TYPE == stat.TYPE) { - prev.definitions = prev.definitions.concat(stat.definitions); - CHANGED = true; - } - else if (stat instanceof AST_For - && prev instanceof AST_Var - && (!stat.init || stat.init.TYPE == prev.TYPE)) { - CHANGED = true; - if (stat.init) { - stat.init.definitions = prev.definitions.concat(stat.init.definitions); + if (stat instanceof AST_Definitions) { + if (prev && prev.TYPE == stat.TYPE) { + prev.definitions = prev.definitions.concat(stat.definitions); + CHANGED = true; + } else if (defs && defs.TYPE == stat.TYPE && declarations_only(stat)) { + defs.definitions = defs.definitions.concat(stat.definitions); + CHANGED = true; } else { + statements[++j] = stat; + defs = stat; + } + } else if (stat instanceof AST_For) { + if (prev instanceof AST_Var && (!stat.init || stat.init.TYPE == prev.TYPE)) { + if (stat.init) { + prev.definitions = prev.definitions.concat(stat.init.definitions); + } stat.init = prev; + statements[j] = stat; + CHANGED = true; + } else if (defs && stat.init && defs.TYPE == stat.init.TYPE && declarations_only(stat.init)) { + defs.definitions = defs.definitions.concat(stat.init.definitions); + stat.init = null; + statements[++j] = stat; + CHANGED = true; + } else { + statements[++j] = stat; } - statements[j] = stat; - } - else { + } else { statements[++j] = stat; } } statements.length = j + 1; - }; + } } function extract_declarations_from_unreachable_code(compressor, stat, target) { diff --git a/test/compress/conditionals.js b/test/compress/conditionals.js index 4d61d39f..7838fdbb 100644 --- a/test/compress/conditionals.js +++ b/test/compress/conditionals.js @@ -1203,3 +1203,24 @@ issue_2560: { "2", ] } + +hoist_decl: { + options = { + conditionals: true, + join_vars: true, + sequences: true, + } + input: { + if (x()) { + var a; + y(); + } else { + z(); + var b; + } + } + expect: { + var a, b; + x() ? y() : z(); + } +} diff --git a/test/compress/functions.js b/test/compress/functions.js index e6c4301c..0e37b78a 100644 --- a/test/compress/functions.js +++ b/test/compress/functions.js @@ -800,12 +800,12 @@ issue_2601_1: { expect: { var a = "FAIL"; (function() { + var b; b = "foo", function(b) { b && b(); }(), b && (a = "PASS"); - var b; })(), console.log(a); } diff --git a/test/compress/sequences.js b/test/compress/sequences.js index 81b06881..3d12fb0b 100644 --- a/test/compress/sequences.js +++ b/test/compress/sequences.js @@ -796,3 +796,66 @@ cascade_assignment_in_return: { } } } + +hoist_defun: { + options = { + join_vars: true, + sequences: true, + } + input: { + x(); + function f() {} + y(); + } + expect: { + function f() {} + x(), y(); + } +} + +hoist_decl: { + options = { + join_vars: true, + sequences: true, + } + input: { + var a; + w(); + var b = x(); + y(); + for (var c; 0;) z(); + var d; + } + expect: { + var a; + w(); + var b = x(), c, d; + for (y(); 0;) z(); + } +} + +for_init_var: { + options = { + join_vars: true, + unused: false, + } + input: { + var a = "PASS"; + (function() { + var b = 42; + for (var c = 5; c > 0;) c--; + a = "FAIL"; + var a; + })(); + console.log(a); + } + expect: { + var a = "PASS"; + (function() { + for (var b = 42, c = 5, a; c > 0;) c--; + a = "FAIL"; + })(); + console.log(a); + } + expect_stdout: "PASS" +} -- cgit v1.2.3