From 4761d07e0bc3d4c53e0c9c72fc9c322c95cb090e Mon Sep 17 00:00:00 2001 From: alexlamsl Date: Tue, 20 Sep 2016 22:23:27 +0800 Subject: [PATCH] Optimize unmodified variables --- README.md | 3 + lib/compress.js | 3 +- lib/scope.js | 12 ++- test/compress/reduce_vars.js | 171 +++++++++++++++++++++++++++++++++++ 4 files changed, 184 insertions(+), 5 deletions(-) create mode 100644 test/compress/reduce_vars.js diff --git a/README.md b/README.md index e0aa4ba9..ed2630f1 100644 --- a/README.md +++ b/README.md @@ -348,6 +348,9 @@ to set `true`; it's effectively a shortcut for `foo=true`). - `collapse_vars` -- default `false`. Collapse single-use `var` and `const` definitions when possible. +- `reduce_vars` -- default `false`. Improve optimization on variables assigned + with and used as constant values. + - `warnings` -- display warnings when dropping unreachable code or unused declarations etc. diff --git a/lib/compress.js b/lib/compress.js index 8a08572f..14fb8f1e 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -67,6 +67,7 @@ function Compressor(options, false_by_default) { if_return : !false_by_default, join_vars : !false_by_default, collapse_vars : false, + reduce_vars : false, cascade : !false_by_default, side_effects : !false_by_default, pure_getters : false, @@ -1107,7 +1108,7 @@ merge(Compressor.prototype, { this._evaluating = true; try { var d = this.definition(); - if (d && d.constant && d.init) { + if (d && (d.constant || compressor.option("reduce_vars") && !d.modified) && d.init) { return ev(d.init, compressor); } } finally { diff --git a/lib/scope.js b/lib/scope.js index 606a5a2f..fb583291 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -197,7 +197,8 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ } if (node instanceof AST_SymbolRef) { var name = node.name; - if (name == "eval" && tw.parent() instanceof AST_Call) { + var parent = tw.parent(); + if (name == "eval" && parent instanceof AST_Call) { for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) { s.uses_eval = true; } @@ -213,12 +214,15 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ g.global = true; globals.set(name, g); } - node.thedef = g; + sym = g; if (func && name == "arguments") { func.uses_arguments = true; } - } else { - node.thedef = sym; + } + node.thedef = sym; + if (parent instanceof AST_Unary && (parent.operator === '++' || parent.operator === '--') + || parent instanceof AST_Assign && parent.left === node) { + sym.modified = true; } node.reference(); return true; diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js new file mode 100644 index 00000000..a1d05012 --- /dev/null +++ b/test/compress/reduce_vars.js @@ -0,0 +1,171 @@ +reduce_vars: { + options = { + conditionals : true, + evaluate : true, + global_defs : { + C : 0 + }, + reduce_vars : true, + unused : true + } + input: { + var A = 1; + (function f0() { + var a = 2; + console.log(a - 5); + console.log(A - 5); + })(); + (function f1() { + var a = 2; + console.log(a - 5); + eval("console.log(a);"); + })(); + (function f2(eval) { + var a = 2; + console.log(a - 5); + eval("console.log(a);"); + })(eval); + (function f3() { + var b = typeof C !== "undefined"; + var c = 4; + if (b) { + return 'yes'; + } else { + return 'no'; + } + })(); + console.log(A + 1); + } + expect: { + var A = 1; + (function() { + console.log(-3); + console.log(-4); + })(); + (function f1() { + var a = 2; + console.log(-3); + eval("console.log(a);"); + })(); + (function f2(eval) { + var a = 2; + console.log(-3); + eval("console.log(a);"); + })(eval); + (function() { + return "yes"; + })(); + console.log(2); + } +} + +modified: { + options = { + conditionals : true, + evaluate : true, + reduce_vars : true, + unused : true + } + input: { + function f0() { + var a = 1, b = 2; + b++; + console.log(a + 1); + console.log(b + 1); + } + + function f1() { + var a = 1, b = 2; + --b; + console.log(a + 1); + console.log(b + 1); + } + + function f2() { + var a = 1, b = 2, c = 3; + b = c; + console.log(a + b); + console.log(b + c); + console.log(a + c); + console.log(a + b + c); + } + + function f3() { + var a = 1, b = 2, c = 3; + b *= c; + console.log(a + b); + console.log(b + c); + console.log(a + c); + console.log(a + b + c); + } + + function f4() { + var a = 1, b = 2, c = 3; + if (a) { + b = c; + } else { + c = b; + } + console.log(a + b); + console.log(b + c); + // TODO: as "modified" is determined in "figure_out_scope", + // even "passes" wouldn't improve this any further + console.log(a + c); + console.log(a + b + c); + } + + function f5(a) { + B = a; + console.log(A ? 'yes' : 'no'); + console.log(B ? 'yes' : 'no'); + } + } + expect: { + function f0() { + var b = 2; + b++; + console.log(2); + console.log(b + 1); + } + + function f1() { + var b = 2; + --b; + console.log(2); + console.log(b + 1); + } + + function f2() { + var a = 1, b = 2, c = 3; + b = c; + console.log(a + b); + console.log(b + c); + console.log(4); + console.log(a + b + c); + } + + function f3() { + var a = 1, b = 2, c = 3; + b *= c; + console.log(a + b); + console.log(b + c); + console.log(4); + console.log(a + b + c); + } + + function f4() { + var a = 1, b = 2, c = 3; + b = c; + console.log(a + b); + console.log(b + c); + console.log(a + c); + console.log(a + b + c); + } + + function f5(a) { + B = a; + console.log(A ? 'yes' : 'no'); + console.log(B ? 'yes' : 'no'); + } + } +} \ No newline at end of file -- 2.34.1