diff --git a/README.md b/README.md index 1d64fcb2..8ec101f6 100644 --- a/README.md +++ b/README.md @@ -443,6 +443,11 @@ to set `true`; it's effectively a shortcut for `foo=true`). - `keep_infinity` -- default `false`. Pass `true` to prevent `Infinity` from being compressed into `1/0`, which may cause performance issues on Chrome. +- `side_effects` -- default `false`. Pass `true` to potentially drop functions +marked as "pure". (A function is marked as "pure" via the comment annotation +`/* @__PURE__ */` or `/* #__PURE__ */`) + + ### The `unsafe` option It enables some transformations that *might* break code logic in certain diff --git a/lib/compress.js b/lib/compress.js index 8454a432..d0f9668c 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -326,10 +326,14 @@ merge(Compressor.prototype, { // So existing transformation rules can work on them. node.argnames.forEach(function(arg, i) { var d = arg.definition(); - d.fixed = function() { - return iife.args[i] || make_node(AST_Undefined, iife); - }; - mark(d, true); + if (!node.uses_arguments && d.fixed === undefined) { + d.fixed = function() { + return iife.args[i] || make_node(AST_Undefined, iife); + }; + mark(d, true); + } else { + d.fixed = false; + } }); } if (node instanceof AST_If || node instanceof AST_DWLoop) { @@ -414,7 +418,9 @@ merge(Compressor.prototype, { function reset_def(def) { def.escaped = false; - if (toplevel || !def.global || def.orig[0] instanceof AST_SymbolConst) { + if (def.scope.uses_eval) { + def.fixed = false; + } else if (toplevel || !def.global || def.orig[0] instanceof AST_SymbolConst) { def.fixed = undefined; } else { def.fixed = false; @@ -440,6 +446,14 @@ merge(Compressor.prototype, { return fixed(); }); + function is_reference_const(ref) { + if (!(ref instanceof AST_SymbolRef)) return false; + var orig = ref.definition().orig; + for (var i = orig.length; --i >= 0;) { + if (orig[i] instanceof AST_SymbolConst) return true; + } + } + function find_variable(compressor, name) { var scope, i = 0; while (scope = compressor.parent(i++)) { @@ -1909,6 +1923,7 @@ merge(Compressor.prototype, { && node instanceof AST_Assign && node.operator == "=" && node.left instanceof AST_SymbolRef + && !is_reference_const(node.left) && scope === self) { node.right.walk(tw); return true; @@ -3066,7 +3081,8 @@ merge(Compressor.prototype, { } if (left && !(left instanceof AST_SymbolRef - && left.definition().orig[0] instanceof AST_SymbolLambda)) { + && (left.definition().orig[0] instanceof AST_SymbolLambda + || is_reference_const(left)))) { var parent, field; var cdr = self.cdr; while (true) { diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js index 2264783d..53ae8f4e 100644 --- a/test/compress/collapse_vars.js +++ b/test/compress/collapse_vars.js @@ -1592,3 +1592,49 @@ var_side_effects_3: { } expect_stdout: true } + +reassign_const_1: { + options = { + collapse_vars: true, + } + input: { + function f() { + const a = 1; + a = 2; + return a; + } + console.log(f()); + } + expect: { + function f() { + const a = 1; + a = 2; + return a; + } + console.log(f()); + } + expect_stdout: true +} + +reassign_const_2: { + options = { + collapse_vars: true, + } + input: { + function f() { + const a = 1; + ++a; + return a; + } + console.log(f()); + } + expect: { + function f() { + const a = 1; + ++a; + return a; + } + console.log(f()); + } + expect_stdout: true +} diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index 9d37dc7e..7a2f86a9 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -1064,3 +1064,28 @@ issue_1830_2: { } expect_stdout: "1" } + +reassign_const: { + options = { + cascade: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f() { + const a = 1; + a = 2; + return a; + } + console.log(f()); + } + expect: { + function f() { + const a = 1; + return a = 2, a; + } + console.log(f()); + } + expect_stdout: true +} diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index 6726bab5..767311dd 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -41,22 +41,22 @@ reduce_vars: { var A = 1; (function() { console.log(-3); - console.log(-4); + console.log(A - 5); })(); (function f1() { var a = 2; - console.log(-3); + console.log(a - 5); eval("console.log(a);"); })(); (function f2(eval) { var a = 2; - console.log(-3); + console.log(a - 5); eval("console.log(a);"); })(eval); (function() { return "yes"; })(); - console.log(2); + console.log(A + 1); } expect_stdout: true } @@ -1732,7 +1732,10 @@ redefine_arguments_3: { console.log(function() { var arguments; return typeof arguments; - }(), "number", "undefined"); + }(), "number", function(x) { + var arguments = x; + return typeof arguments; + }()); } expect_stdout: "object number undefined" } @@ -2122,3 +2125,47 @@ issue_1865: { } expect_stdout: true } + +issue_1922_1: { + options = { + evaluate: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function(a) { + arguments[0] = 2; + return a; + }(1)); + } + expect: { + console.log(function(a) { + arguments[0] = 2; + return a; + }(1)); + } + expect_stdout: "2" +} + +issue_1922_2: { + options = { + evaluate: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function() { + var a; + eval("a = 1"); + return a; + }(1)); + } + expect: { + console.log(function() { + var a; + eval("a = 1"); + return a; + }(1)); + } + expect_stdout: "1" +} diff --git a/test/compress/sequences.js b/test/compress/sequences.js index 699341c0..3bd0ca35 100644 --- a/test/compress/sequences.js +++ b/test/compress/sequences.js @@ -610,3 +610,27 @@ delete_seq_6: { } expect_stdout: true } + +reassign_const: { + options = { + cascade: true, + sequences: true, + side_effects: true, + } + input: { + function f() { + const a = 1; + a++; + return a; + } + console.log(f()); + } + expect: { + function f() { + const a = 1; + return a++, a; + } + console.log(f()); + } + expect_stdout: true +}