From aa7e8783f84d119f0226f20d019d6f81f325847f Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Fri, 12 May 2017 04:51:44 +0800 Subject: [PATCH 1/3] fix invalid transform on `const` (#1919) - preserve (re)assignment to `const` for runtime error - suppress `cascade` on `const`, as runtime behaviour is ill-defined --- lib/compress.js | 12 ++++++++- test/compress/collapse_vars.js | 46 ++++++++++++++++++++++++++++++++++ test/compress/drop-unused.js | 25 ++++++++++++++++++ test/compress/sequences.js | 24 ++++++++++++++++++ 4 files changed, 106 insertions(+), 1 deletion(-) diff --git a/lib/compress.js b/lib/compress.js index 8454a432..f9dcd0f3 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -440,6 +440,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 +1917,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 +3075,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/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 +} From f631d6437a7706ed8354105e73867a63d85ca490 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Fri, 12 May 2017 12:34:55 +0800 Subject: [PATCH 2/3] avoid `arguments` and `eval` in `reduce_vars` (#1924) fixes #1922 --- lib/compress.js | 16 ++++++---- test/compress/reduce_vars.js | 57 ++++++++++++++++++++++++++++++++---- 2 files changed, 63 insertions(+), 10 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index f9dcd0f3..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; 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" +} From 9a985139812c07cf31c75b74e038d1f6efc63319 Mon Sep 17 00:00:00 2001 From: olsonpm Date: Thu, 11 May 2017 23:29:55 -0500 Subject: [PATCH 3/3] add documentation for `side_effects` & `[#@]__PURE__` (#1925) --- README.md | 5 +++++ 1 file changed, 5 insertions(+) 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