diff options
author | Alex Lam S.L <alexlamsl@gmail.com> | 2017-10-09 12:25:06 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-10-09 12:25:06 +0800 |
commit | b810e2f8da4bfd42a8876b64d067e83dfd340aa1 (patch) | |
tree | d86ae7aaa8d83f58cd35ba01b9e413f3f4d5c84b | |
parent | 1abe14296e9a11d30935dba3f27682a3e67885f8 (diff) | |
download | tracifyjs-b810e2f8da4bfd42a8876b64d067e83dfd340aa1.tar.gz tracifyjs-b810e2f8da4bfd42a8876b64d067e83dfd340aa1.zip |
perform `reduce_vars` on safe literals (#2351)
- constant expression
- single reference
- same scope
- not across loop body
-rw-r--r-- | lib/compress.js | 63 | ||||
-rw-r--r-- | test/compress/collapse_vars.js | 40 | ||||
-rw-r--r-- | test/compress/drop-unused.js | 12 | ||||
-rw-r--r-- | test/compress/evaluate.js | 14 | ||||
-rw-r--r-- | test/compress/reduce_vars.js | 193 |
5 files changed, 286 insertions, 36 deletions
diff --git a/lib/compress.js b/lib/compress.js index 33b4ef00..c3876f9d 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -297,6 +297,8 @@ merge(Compressor.prototype, { if (node instanceof AST_SymbolRef) d.references.push(node); d.fixed = false; }); + var in_loop = null; + var loop_ids = Object.create(null); var tw = new TreeWalker(function(node, descend) { node._squeezed = false; node._optimized = false; @@ -307,7 +309,7 @@ merge(Compressor.prototype, { var d = node.definition(); d.references.push(node); if (d.fixed === undefined || !safe_to_read(d) - || is_modified(node, 0, is_immutable(node.fixed_value()))) { + || is_modified(node, 0, is_immutable(node))) { d.fixed = false; } else { var parent = tw.parent(); @@ -329,6 +331,7 @@ merge(Compressor.prototype, { d.fixed = function() { return node.value; }; + loop_ids[d.id] = in_loop; mark(d, false); descend(); } else { @@ -384,6 +387,7 @@ merge(Compressor.prototype, { d.fixed = function() { return iife.args[i] || make_node(AST_Undefined, iife); }; + loop_ids[d.id] = in_loop; mark(d, true); } else { d.fixed = false; @@ -431,10 +435,13 @@ merge(Compressor.prototype, { return true; } if (node instanceof AST_DWLoop) { + var saved_loop = in_loop; + in_loop = node; push(); node.condition.walk(tw); node.body.walk(tw); pop(); + in_loop = saved_loop; return true; } if (node instanceof AST_LabeledStatement) { @@ -445,6 +452,8 @@ merge(Compressor.prototype, { } if (node instanceof AST_For) { if (node.init) node.init.walk(tw); + var saved_loop = in_loop; + in_loop = node; if (node.condition) { push(); node.condition.walk(tw); @@ -458,14 +467,18 @@ merge(Compressor.prototype, { node.step.walk(tw); pop(); } + in_loop = saved_loop; return true; } if (node instanceof AST_ForIn) { node.init.walk(suppressor); node.object.walk(tw); + var saved_loop = in_loop; + in_loop = node; push(); node.body.walk(tw); pop(); + in_loop = saved_loop; return true; } if (node instanceof AST_Try) { @@ -535,10 +548,23 @@ merge(Compressor.prototype, { } def.references = []; def.should_replace = undefined; - } - - function is_immutable(value) { - return value && value.is_constant() || value instanceof AST_Lambda; + def.single_use = undefined; + } + + function is_immutable(node) { + var value = node.fixed_value(); + if (!value) return false; + if (value.is_constant()) return true; + if (compressor.option("unused")) { + var d = node.definition(); + if (d.single_use === undefined) { + d.single_use = loop_ids[d.id] === in_loop + && d.scope === node.scope + && value.is_constant_expression(); + } + if (d.references.length == 1 && d.single_use) return true; + } + return value instanceof AST_Lambda; } function is_modified(node, level, immutable) { @@ -2115,6 +2141,22 @@ merge(Compressor.prototype, { } def(AST_Node, return_false); def(AST_Constant, return_true); + def(AST_Function, function(){ + var self = this; + var result = true; + self.walk(new TreeWalker(function(node) { + if (!result) return true; + if (node instanceof AST_SymbolRef) { + var def = node.definition(); + if (self.enclosed.indexOf(def) >= 0 + && self.variables.get(def.name) !== def) { + result = false; + return true; + } + } + })); + return result; + }); def(AST_Unary, function(){ return this.expression.is_constant_expression(); }); @@ -4078,12 +4120,13 @@ merge(Compressor.prototype, { d.fixed = fixed = make_node(AST_Function, fixed, fixed); } if (compressor.option("unused") - && fixed instanceof AST_Function && d.references.length == 1 - && !(d.scope.uses_arguments && d.orig[0] instanceof AST_SymbolFunarg) - && !d.scope.uses_eval - && compressor.find_parent(AST_Scope) === fixed.parent_scope) { - return fixed.clone(true); + && (d.single_use || fixed instanceof AST_Function + && !(d.scope.uses_arguments && d.orig[0] instanceof AST_SymbolFunarg) + && !d.scope.uses_eval + && compressor.find_parent(AST_Scope) === fixed.parent_scope)) { + var value = fixed.optimize(compressor); + return value === fixed ? fixed.clone(true) : value; } if (compressor.option("evaluate") && fixed) { if (d.should_replace === undefined) { diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js index 631f5b29..7d66f7c6 100644 --- a/test/compress/collapse_vars.js +++ b/test/compress/collapse_vars.js @@ -1268,22 +1268,21 @@ collapse_vars_short_circuited_conditions: { collapse_vars_regexp: { options = { + booleans: true, + cascade: true, collapse_vars: true, - loops: false, - sequences: true, - dead_code: true, - conditionals: true, comparisons: true, + conditionals: true, + dead_code: true, evaluate: true, - booleans: true, - unused: true, - hoist_funs: true, - keep_fargs: true, if_return: true, join_vars: true, - cascade: true, - side_effects: true, + hoist_funs: true, + keep_fargs: true, + loops: false, reduce_vars: true, + side_effects: true, + unused: true, } input: { function f1() { @@ -1292,12 +1291,12 @@ collapse_vars_regexp: { return [rx, k]; } function f2() { - var rx = /[abc123]+/; + var rx = /ab*/g; return function(s) { return rx.exec(s); }; } - (function(){ + (function() { var result; var s = 'acdabcdeabbb'; var rx = /ab*/g; @@ -1305,22 +1304,35 @@ collapse_vars_regexp: { console.log(result[0]); } })(); + (function() { + var result; + var s = 'acdabcdeabbb'; + var rx = f2(); + while (result = rx(s)) { + console.log(result[0]); + } + })(); } expect: { function f1() { return [/[A-Z]+/, 9]; } function f2() { - var rx = /[abc123]+/; + var rx = /ab*/g; return function(s) { return rx.exec(s); }; } - (function(){ + (function() { var result, rx = /ab*/g; while (result = rx.exec("acdabcdeabbb")) console.log(result[0]); })(); + (function() { + var result, rx = f2(); + while (result = rx("acdabcdeabbb")) + console.log(result[0]); + })(); } expect_stdout: true } diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index c9048540..4ce8d2eb 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -751,12 +751,12 @@ issue_1583: { expect: { function m(t) { (function(e) { - t = e(); - })(function() { - return (function(a) { - return a; - })(function(a) {}); - }); + t = function() { + return (function(a) { + return function(a) {}; + })(); + }(); + })(); } } } diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js index 1c737060..5f5a4a93 100644 --- a/test/compress/evaluate.js +++ b/test/compress/evaluate.js @@ -1021,6 +1021,7 @@ issue_1964_1: { input: { function f() { var long_variable_name = /\s/; + console.log(long_variable_name.source); return "a b c".split(long_variable_name)[1]; } console.log(f()); @@ -1028,11 +1029,15 @@ issue_1964_1: { expect: { function f() { var long_variable_name = /\s/; + console.log(long_variable_name.source); return "a b c".split(long_variable_name)[1]; } console.log(f()); } - expect_stdout: "b" + expect_stdout: [ + "\\s", + "b", + ] } issue_1964_2: { @@ -1045,17 +1050,22 @@ issue_1964_2: { input: { function f() { var long_variable_name = /\s/; + console.log(long_variable_name.source); return "a b c".split(long_variable_name)[1]; } console.log(f()); } expect: { function f() { + console.log(/\s/.source); return "a b c".split(/\s/)[1]; } console.log(f()); } - expect_stdout: "b" + expect_stdout: [ + "\\s", + "b", + ] } array_slice_index: { diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index 4e096d9d..c1da2991 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -172,6 +172,7 @@ unsafe_evaluate: { options = { evaluate : true, reduce_vars : true, + side_effects : true, unsafe : true, unused : true } @@ -1898,10 +1899,7 @@ redefine_farg_3: { console.log(f([]), g([]), h([])); } expect: { - console.log(function(a) { - var a; - return typeof a; - }([]), "number", "undefined"); + console.log(typeof [], "number", "undefined"); } expect_stdout: "object number undefined" } @@ -2629,3 +2627,190 @@ for_in_prop: { } expect_stdout: "1" } + +obj_var_1: { + options = { + evaluate: true, + passes: 2, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var C = 1; + var obj = { + bar: function() { + return C + C; + } + }; + console.log(obj.bar()); + } + expect: { + console.log({ + bar: function() { + return 2; + } + }.bar()); + } + expect_stdout: "2" +} + +obj_var_2: { + options = { + evaluate: true, + inline: true, + passes: 2, + reduce_vars: true, + side_effects: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var C = 1; + var obj = { + bar: function() { + return C + C; + } + }; + console.log(obj.bar()); + } + expect: { + console.log(2); + } + expect_stdout: "2" +} + +obj_arg_1: { + options = { + evaluate: true, + inline: true, + passes: 2, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var C = 1; + function f(obj) { + return obj.bar(); + } + console.log(f({ + bar: function() { + return C + C; + } + })); + } + expect: { + console.log({ + bar: function() { + return 2; + } + }.bar()); + } + expect_stdout: "2" +} + +obj_arg_2: { + options = { + evaluate: true, + inline: true, + passes: 2, + reduce_vars: true, + side_effects: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var C = 1; + function f(obj) { + return obj.bar(); + } + console.log(f({ + bar: function() { + return C + C; + } + })); + } + expect: { + console.log(2); + } + expect_stdout: "2" +} + +func_arg_1: { + options = { + evaluate: true, + inline: true, + passes: 2, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = 42; + !function(a) { + console.log(a()); + }(function() { + return a; + }); + } + expect: { + console.log(42); + } + expect_stdout: "42" +} + +func_arg_2: { + options = { + evaluate: true, + inline: true, + passes: 2, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = 42; + !function(a) { + console.log(a()); + }(function(a) { + return a; + }); + } + expect: { + console.log(void 0); + } + expect_stdout: "undefined" +} + +regex_loop: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f(x) { + for (var r, s = "acdabcdeabbb"; r = x().exec(s);) + console.log(r[0]); + } + var a = /ab*/g; + f(function() { + return a; + }); + } + expect: { + var a = /ab*/g; + (function(x) { + for (var r, s = "acdabcdeabbb"; r = x().exec(s);) + console.log(r[0]); + })(function() { + return a; + }); + } + expect_stdout: true +} |