From 05e7d34ed480429cc26c8eedd675263cd0d94a3e Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Tue, 26 Dec 2017 18:29:28 +0800 Subject: [PATCH 01/10] improve `unused` over duplicate variable names (#2656) --- lib/compress.js | 17 +++++++---------- test/compress/drop-unused.js | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 8cfb56be..8df6bbd5 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -2645,14 +2645,14 @@ merge(Compressor.prototype, { var tw = new TreeWalker(function(node, descend){ if (node === self) return; if (node instanceof AST_Defun) { + var node_def = node.name.definition(); if (!drop_funcs && scope === self) { - var node_def = node.name.definition(); if (!(node_def.id in in_use_ids)) { in_use_ids[node_def.id] = true; in_use.push(node_def); } } - initializations.add(node.name.name, node); + initializations.add(node_def.id, node); return true; // don't go in nested scopes } if (node instanceof AST_SymbolFunarg && scope === self) { @@ -2671,7 +2671,7 @@ merge(Compressor.prototype, { } } if (def.value) { - initializations.add(def.name.name, def.value); + initializations.add(node_def.id, def.value); if (def.value.has_side_effects(compressor)) { def.value.walk(tw); } @@ -2686,13 +2686,10 @@ merge(Compressor.prototype, { // initialization code to figure out if it uses other // symbols (that may not be in_use). tw = new TreeWalker(scan_ref_scoped); - for (var i = 0; i < in_use.length; ++i) { - in_use[i].orig.forEach(function(decl){ - // undeclared globals will be instanceof AST_SymbolRef - var init = initializations.get(decl.name); - if (init) init.forEach(function(init){ - init.walk(tw); - }); + for (var i = 0; i < in_use.length; i++) { + var init = initializations.get(in_use[i].id); + if (init) init.forEach(function(init) { + init.walk(tw); }); } // pass 3: we should drop declarations not in_use diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index 275e0f76..90206ec6 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -1413,3 +1413,24 @@ issue_2516_2: { Baz(2); } } + +defun_lambda_same_name: { + options = { + toplevel: true, + unused: true, + } + input: { + function f(n) { + return n ? n * f(n - 1) : 1; + } + console.log(function f(n) { + return n ? n * f(n - 1) : 1; + }(5)); + } + expect: { + console.log(function f(n) { + return n ? n * f(n - 1) : 1; + }(5)); + } + expect_stdout: "120" +} From 7f342cb3e3abd3e39b18e62a2e9d6b8020d82773 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Tue, 26 Dec 2017 18:56:59 +0800 Subject: [PATCH 02/10] suppress `inline` within substituted `AST_Scope` (#2658) fixes #2657 --- lib/compress.js | 2 +- test/compress/functions.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/lib/compress.js b/lib/compress.js index 8df6bbd5..bb355423 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -3982,7 +3982,7 @@ merge(Compressor.prototype, { do { scope = compressor.parent(++level); if (scope instanceof AST_SymbolRef) { - scope = scope.fixed_value(); + if (scope.fixed_value() instanceof AST_Scope) return false; } else if (scope instanceof AST_Catch) { catches[scope.argname.name] = true; } diff --git a/test/compress/functions.js b/test/compress/functions.js index 02b4ab39..7f35de76 100644 --- a/test/compress/functions.js +++ b/test/compress/functions.js @@ -1447,3 +1447,33 @@ recursive_inline: { } expect: {} } + +issue_2657: { + options = { + inline: true, + reduce_vars: true, + sequences: true, + unused: true, + } + input: { + "use strict"; + console.log(function f() { + return h; + function g(b) { + return b || b(); + } + function h(a) { + g(a); + return a; + } + }()(42)); + } + expect: { + "use strict"; + console.log(function(a) { + return b = a, b || b(), a; + var b; + }(42)); + } + expect_stdout: "42" +} From 4832bc5d88e37ca35f1dd5f5d8ddd95cd8bbdd7d Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Tue, 26 Dec 2017 21:25:35 +0800 Subject: [PATCH 03/10] replace single-use recursive functions (#2659) fixes #2628 --- lib/compress.js | 39 +++++++++++++++++++++++++--------- lib/scope.js | 4 +++- test/compress/collapse_vars.js | 7 +++--- test/compress/functions.js | 27 +++++++++++++++++++---- test/compress/reduce_vars.js | 12 +++++------ 5 files changed, 63 insertions(+), 26 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index bb355423..d890caf7 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -319,6 +319,7 @@ merge(Compressor.prototype, { } else { def.fixed = false; } + def.recursive_refs = 0; def.references = []; def.should_replace = undefined; def.single_use = undefined; @@ -369,7 +370,7 @@ merge(Compressor.prototype, { return compressor.option("unused") && !def.scope.uses_eval && !def.scope.uses_with - && def.references.length == 1 + && def.references.length - def.recursive_refs == 1 && tw.loop_ids[def.id] === tw.in_loop; } @@ -621,7 +622,9 @@ merge(Compressor.prototype, { d.fixed = false; } else if (d.fixed) { value = this.fixed_value(); - if (value && ref_once(tw, compressor, d)) { + if (value instanceof AST_Lambda && recursive_ref(tw, d)) { + d.recursive_refs++; + } else if (value && ref_once(tw, compressor, d)) { d.single_use = value instanceof AST_Lambda || d.scope === this.scope && value.is_constant_expression(); } else { @@ -4670,17 +4673,18 @@ merge(Compressor.prototype, { if (fixed instanceof AST_Defun) { d.fixed = fixed = make_node(AST_Function, fixed, fixed); } - if (d.single_use && fixed instanceof AST_Function) { + var single_use = d.single_use; + if (single_use && fixed instanceof AST_Function) { if (d.scope !== self.scope && (!compressor.option("reduce_funcs") || d.escaped == 1 || fixed.inlined)) { - d.single_use = false; + single_use = false; } else if (recursive_ref(compressor, d)) { - d.single_use = false; + single_use = false; } else if (d.scope !== self.scope || d.orig[0] instanceof AST_SymbolFunarg) { - d.single_use = fixed.is_constant_expression(self.scope); - if (d.single_use == "f") { + single_use = fixed.is_constant_expression(self.scope); + if (single_use == "f") { var scope = self.scope; do { if (scope instanceof AST_Defun || scope instanceof AST_Function) { @@ -4690,9 +4694,24 @@ merge(Compressor.prototype, { } } } - if (d.single_use && fixed) { - var value = fixed.optimize(compressor); - return value === fixed ? fixed.clone(true) : value; + if (single_use && fixed) { + var value; + if (d.recursive_refs > 0 && fixed.name instanceof AST_SymbolDefun) { + value = fixed.clone(true); + var defun_def = value.name.definition(); + value.name = make_node(AST_SymbolLambda, value.name, value.name); + value.name.scope = value; + var lambda_def = value.def_function(value.name); + value.walk(new TreeWalker(function(node) { + if (node instanceof AST_SymbolRef && node.definition() === defun_def) { + node.thedef = lambda_def; + } + })); + } else { + value = fixed.optimize(compressor); + if (value === fixed) value = fixed.clone(true); + } + return value; } if (fixed && d.should_replace === undefined) { var init; diff --git a/lib/scope.js b/lib/scope.js index bbfa037c..bceec289 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -307,7 +307,9 @@ AST_Scope.DEFMETHOD("find_variable", function(name){ }); AST_Scope.DEFMETHOD("def_function", function(symbol){ - this.functions.set(symbol.name, this.def_variable(symbol)); + var def = this.def_variable(symbol); + this.functions.set(symbol.name, def); + return def; }); AST_Scope.DEFMETHOD("def_variable", function(symbol){ diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js index 9a20c559..0bad06a4 100644 --- a/test/compress/collapse_vars.js +++ b/test/compress/collapse_vars.js @@ -3878,10 +3878,9 @@ recursive_function_replacement: { console.log(f(c)); } expect: { - function f(n) { - return x(y(f(n))); - } - console.log(f(c)); + console.log(function n(o) { + return x(y(n(o))); + }(c)); } } diff --git a/test/compress/functions.js b/test/compress/functions.js index 7f35de76..83a27a06 100644 --- a/test/compress/functions.js +++ b/test/compress/functions.js @@ -1345,11 +1345,10 @@ issue_2630_3: { expect: { var x = 2, a = 1; (function() { - function f1(a) { + (function f1(a) { f2(); --x >= 0 && f1({}); - } - f1(a++); + })(a++); function f2() { a++; } @@ -1424,7 +1423,7 @@ issue_2630_5: { expect_stdout: "155" } -recursive_inline: { +recursive_inline_1: { options = { inline: true, reduce_funcs: true, @@ -1448,6 +1447,26 @@ recursive_inline: { expect: {} } +recursive_inline_2: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f(n) { + return n ? n * f(n - 1) : 1; + } + console.log(f(5)); + } + expect: { + console.log(function f(n) { + return n ? n * f(n - 1) : 1; + }(5)); + } + expect_stdout: "120" +} + issue_2657: { options = { inline: true, diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index 2b2c77da..6f302ecd 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -1355,10 +1355,9 @@ defun_inline_1: { function f() { return function(b) { return b; - }(2) + h(); - function h() { + }(2) + function h() { return h(); - } + }(); } } } @@ -1382,12 +1381,11 @@ defun_inline_2: { } expect: { function f() { - function h() { - return h(); - } return function(b) { return b; - }(2) + h(); + }(2) + function h() { + return h(); + }(); } } } From 3ff625de7e3d381de43b4703faae402381844c9f Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 27 Dec 2017 05:31:37 +0800 Subject: [PATCH 04/10] fix bugs on substituted `AST_Defun` (#2661) fixes #2660 --- lib/compress.js | 18 ++++++------ test/compress/drop-unused.js | 54 ++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 8 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index d890caf7..4a2a436b 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -3685,8 +3685,9 @@ merge(Compressor.prototype, { if (compressor.option("reduce_vars") && fn instanceof AST_SymbolRef) { fn = fn.fixed_value(); } + var is_func = fn instanceof AST_Lambda; if (compressor.option("unused") - && fn instanceof AST_Function + && is_func && !fn.uses_arguments && !fn.uses_eval) { var pos = 0, last = 0; @@ -3855,7 +3856,7 @@ merge(Compressor.prototype, { if (func instanceof AST_SymbolRef) { func = func.fixed_value(); } - if (func instanceof AST_Function && !func.contains_this()) { + if (func instanceof AST_Lambda && !func.contains_this()) { return make_sequence(this, [ self.args[0], make_node(AST_Call, self, { @@ -3925,7 +3926,7 @@ merge(Compressor.prototype, { } } } - var stat = fn instanceof AST_Function && fn.body[0]; + var stat = is_func && fn.body[0]; if (compressor.option("inline") && stat instanceof AST_Return) { var value = stat.value; if (!value || value.is_constant_expression()) { @@ -3933,7 +3934,7 @@ merge(Compressor.prototype, { return make_sequence(self, args).optimize(compressor); } } - if (fn instanceof AST_Function) { + if (is_func) { var def, value, scope, level = -1; if (compressor.option("inline") && !fn.uses_arguments @@ -4670,11 +4671,8 @@ merge(Compressor.prototype, { && is_lhs(self, compressor.parent()) !== self) { var d = self.definition(); var fixed = self.fixed_value(); - if (fixed instanceof AST_Defun) { - d.fixed = fixed = make_node(AST_Function, fixed, fixed); - } var single_use = d.single_use; - if (single_use && fixed instanceof AST_Function) { + if (single_use && fixed instanceof AST_Lambda) { if (d.scope !== self.scope && (!compressor.option("reduce_funcs") || d.escaped == 1 @@ -4695,6 +4693,9 @@ merge(Compressor.prototype, { } } if (single_use && fixed) { + if (fixed instanceof AST_Defun) { + fixed = make_node(AST_Function, fixed, fixed); + } var value; if (d.recursive_refs > 0 && fixed.name instanceof AST_SymbolDefun) { value = fixed.clone(true); @@ -4705,6 +4706,7 @@ merge(Compressor.prototype, { value.walk(new TreeWalker(function(node) { if (node instanceof AST_SymbolRef && node.definition() === defun_def) { node.thedef = lambda_def; + lambda_def.references.push(node); } })); } else { diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index 90206ec6..714e1d16 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -1434,3 +1434,57 @@ defun_lambda_same_name: { } expect_stdout: "120" } + +issue_2660_1: { + options = { + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = 2; + function f(b) { + return b && f() || a--; + } + f(1); + console.log(a); + } + expect: { + var a = 2; + (function f(b) { + return b && f() || a--; + })(1); + console.log(a); + } + expect_stdout: "1" +} + +issue_2660_2: { + options = { + collapse_vars: true, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = 1; + function f(b) { + b && f(); + --a, a.toString(); + } + f(); + console.log(a); + } + expect: { + var a = 1; + (function f(b) { + b && f(), + (--a).toString(); + })(), + console.log(a); + } + expect_stdout: "0" +} From 5205dbcbf4f522f8b4dc9f9f9727b621982c9f28 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 27 Dec 2017 07:00:12 +0800 Subject: [PATCH 05/10] retain recursive function names (#2667) fixes #2665 --- lib/compress.js | 11 ++++++++--- test/compress/drop-unused.js | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 4a2a436b..c8cddc37 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -4700,9 +4700,14 @@ merge(Compressor.prototype, { if (d.recursive_refs > 0 && fixed.name instanceof AST_SymbolDefun) { value = fixed.clone(true); var defun_def = value.name.definition(); - value.name = make_node(AST_SymbolLambda, value.name, value.name); - value.name.scope = value; - var lambda_def = value.def_function(value.name); + var lambda_def = value.variables.get(value.name.name); + var name = lambda_def && lambda_def.orig[0]; + if (!(name instanceof AST_SymbolLambda)) { + name = make_node(AST_SymbolLambda, value.name, value.name); + name.scope = value; + value.name = name; + lambda_def = value.def_function(name); + } value.walk(new TreeWalker(function(node) { if (node instanceof AST_SymbolRef && node.definition() === defun_def) { node.thedef = lambda_def; diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index 714e1d16..21d4b7ce 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -1488,3 +1488,37 @@ issue_2660_2: { } expect_stdout: "0" } + +issue_2665: { + options = { + evaluate: true, + inline: true, + keep_fargs: false, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + typeofs: true, + unused: true, + } + input: { + var a = 1; + function g() { + a-- && g(); + } + typeof h == "function" && h(); + function h() { + typeof g == "function" && g(); + } + console.log(a); + } + expect: { + var a = 1; + !function g() { + a-- && g(); + }(); + console.log(a); + } + expect_stdout: "-1" +} From f30790b11bb9e162a19d7769ab54d8bb3f61cc27 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 27 Dec 2017 07:40:34 +0800 Subject: [PATCH 06/10] fix `dead_code` on `return` assignments (#2668) fixes #2666 --- lib/compress.js | 22 +++++++++++++++++++++- test/compress/dead-code.js | 27 +++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/lib/compress.js b/lib/compress.js index c8cddc37..bd61b87f 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -4841,15 +4841,17 @@ merge(Compressor.prototype, { var ASSIGN_OPS = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ]; var ASSIGN_OPS_COMMUTATIVE = [ '*', '|', '^', '&' ]; OPT(AST_Assign, function(self, compressor){ + var def; if (compressor.option("dead_code") && self.left instanceof AST_SymbolRef - && self.left.definition().scope === compressor.find_parent(AST_Lambda)) { + && (def = self.left.definition()).scope === compressor.find_parent(AST_Lambda)) { var level = 0, node, parent = self; do { node = parent; parent = compressor.parent(level++); if (parent instanceof AST_Exit) { if (in_try(level, parent instanceof AST_Throw)) break; + if (is_reachable(def)) break; if (self.operator == "=") return self.right; return make_node(AST_Binary, self, { operator: self.operator.slice(0, -1), @@ -4891,6 +4893,24 @@ merge(Compressor.prototype, { } } } + + function is_reachable(def) { + var reachable = false; + var find_ref = new TreeWalker(function(node) { + if (reachable) return true; + if (node instanceof AST_SymbolRef && node.definition() === def) { + return reachable = true; + } + }); + self.right.walk(new TreeWalker(function(node) { + if (reachable) return true; + if (node instanceof AST_Scope) { + node.walk(find_ref); + return true; + } + })); + return reachable; + } }); OPT(AST_Conditional, function(self, compressor){ diff --git a/test/compress/dead-code.js b/test/compress/dead-code.js index 591dd3a9..7ea380d2 100644 --- a/test/compress/dead-code.js +++ b/test/compress/dead-code.js @@ -828,3 +828,30 @@ issue_2597: { } expect_stdout: "PASS" } + +issue_2666: { + options = { + dead_code: true, + } + input: { + function f(a) { + return a = { + p: function() { + return a; + } + }; + } + console.log(typeof f().p()); + } + expect: { + function f(a) { + return a = { + p: function() { + return a; + } + }; + } + console.log(typeof f().p()); + } + expect_stdout: "object" +} From cb62bd98d3397d9eb3d738cc0c7f53886d3a213b Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Thu, 28 Dec 2017 02:53:14 +0800 Subject: [PATCH 07/10] fix function inlining within loops (#2675) fixes #2663 --- lib/compress.js | 71 ++++++++++-------- test/compress/functions.js | 150 +++++++++++++++++++++++++++++++++++++ 2 files changed, 191 insertions(+), 30 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index bd61b87f..ac5cd235 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -3982,23 +3982,30 @@ merge(Compressor.prototype, { return self; function can_flatten_args(fn) { - var catches = Object.create(null); + var catches = Object.create(null), defs; do { scope = compressor.parent(++level); - if (scope instanceof AST_SymbolRef) { - if (scope.fixed_value() instanceof AST_Scope) return false; - } else if (scope instanceof AST_Catch) { + if (scope instanceof AST_Catch) { catches[scope.argname.name] = true; + } else if (scope instanceof AST_IterationStatement) { + defs = []; + } else if (scope instanceof AST_SymbolRef) { + if (scope.fixed_value() instanceof AST_Scope) return false; } } while (!(scope instanceof AST_Scope)); var safe_to_inject = compressor.toplevel.vars || !(scope instanceof AST_Toplevel); - return all(fn.argnames, function(arg) { - return arg.__unused - || safe_to_inject - && !catches[arg.name] - && !identifier_atom(arg.name) - && !scope.var_names()[arg.name]; - }); + for (var i = 0, len = fn.argnames.length; i < len; i++) { + var arg = fn.argnames[i]; + if (arg.__unused) continue; + if (!safe_to_inject + || catches[arg.name] + || identifier_atom(arg.name) + || scope.var_names()[arg.name]) { + return false; + } + if (defs) defs.push(arg.definition()); + } + return !defs || defs.length == 0 || !is_reachable(fn.body[0], defs); } function flatten_args(fn) { @@ -4838,6 +4845,28 @@ merge(Compressor.prototype, { return self; }); + function is_reachable(node, defs) { + var reachable = false; + var find_ref = new TreeWalker(function(node) { + if (reachable) return true; + if (node instanceof AST_SymbolRef && member(node.definition(), defs)) { + return reachable = true; + } + }); + var scan_scope = new TreeWalker(function(node) { + if (reachable) return true; + if (node instanceof AST_Scope) { + var parent = scan_scope.parent(); + if (!(parent instanceof AST_Call && parent.expression === node)) { + node.walk(find_ref); + } + return true; + } + }); + node.walk(scan_scope); + return reachable; + } + var ASSIGN_OPS = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ]; var ASSIGN_OPS_COMMUTATIVE = [ '*', '|', '^', '&' ]; OPT(AST_Assign, function(self, compressor){ @@ -4851,7 +4880,7 @@ merge(Compressor.prototype, { parent = compressor.parent(level++); if (parent instanceof AST_Exit) { if (in_try(level, parent instanceof AST_Throw)) break; - if (is_reachable(def)) break; + if (is_reachable(self, [ def ])) break; if (self.operator == "=") return self.right; return make_node(AST_Binary, self, { operator: self.operator.slice(0, -1), @@ -4893,24 +4922,6 @@ merge(Compressor.prototype, { } } } - - function is_reachable(def) { - var reachable = false; - var find_ref = new TreeWalker(function(node) { - if (reachable) return true; - if (node instanceof AST_SymbolRef && node.definition() === def) { - return reachable = true; - } - }); - self.right.walk(new TreeWalker(function(node) { - if (reachable) return true; - if (node instanceof AST_Scope) { - node.walk(find_ref); - return true; - } - })); - return reachable; - } }); OPT(AST_Conditional, function(self, compressor){ diff --git a/test/compress/functions.js b/test/compress/functions.js index 83a27a06..888c6e3c 100644 --- a/test/compress/functions.js +++ b/test/compress/functions.js @@ -1496,3 +1496,153 @@ issue_2657: { } expect_stdout: "42" } + +issue_2663_1: { + options = { + inline: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + var i, o = {}; + function createFn(j) { + return function() { + console.log(j); + }; + } + for (i in { a: 1, b: 2, c: 3 }) + o[i] = createFn(i); + for (i in o) + o[i](); + })(); + } + expect: { + (function() { + var i, o = {}; + function createFn(j) { + return function() { + console.log(j); + }; + } + for (i in { a: 1, b: 2, c: 3 }) + o[i] = createFn(i); + for (i in o) + o[i](); + })(); + } + expect_stdout: [ + "a", + "b", + "c", + ] +} + +issue_2663_2: { + options = { + inline: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + (function() { + var i; + function fn(j) { + return function() { + console.log(j); + }(); + } + for (i in { a: 1, b: 2, c: 3 }) + fn(i); + })(); + } + expect: { + (function() { + var i; + for (i in { a: 1, b: 2, c: 3 }) + j = i, console.log(j); + var j; + })(); + } + expect_stdout: [ + "a", + "b", + "c", + ] +} + +issue_2663_3: { + options = { + inline: true, + reduce_vars: true, + unused: true, + } + input: { + (function () { + var outputs = [ + { type: 0, target: null, eventName: "ngSubmit", propName: null }, + { type: 0, target: null, eventName: "submit", propName: null }, + { type: 0, target: null, eventName: "reset", propName: null }, + ]; + function listenToElementOutputs(outputs) { + var handlers = []; + for (var i = 0; i < outputs.length; i++) { + var output = outputs[i]; + var handleEventClosure = renderEventHandlerClosure(output.eventName); + handlers.push(handleEventClosure) + } + var target, name; + return handlers; + } + function renderEventHandlerClosure(eventName) { + return function () { + return console.log(eventName); + }; + } + listenToElementOutputs(outputs).forEach(function (handler) { + return handler() + }); + })(); + } + expect: { + (function() { + function renderEventHandlerClosure(eventName) { + return function() { + return console.log(eventName); + }; + } + (function(outputs) { + var handlers = []; + for (var i = 0; i < outputs.length; i++) { + var output = outputs[i]; + var handleEventClosure = renderEventHandlerClosure(output.eventName); + handlers.push(handleEventClosure); + } + return handlers; + })([ { + type: 0, + target: null, + eventName: "ngSubmit", + propName: null + }, { + type: 0, + target: null, + eventName: "submit", + propName: null + }, { + type: 0, + target: null, + eventName: "reset", + propName: null + } ]).forEach(function(handler) { + return handler(); + }); + })(); + } + expect_stdout: [ + "ngSubmit", + "submit", + "reset", + ] +} From e40a0ee9c6c4dfbd9506dfcbb76ccd1565bc4ad9 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Thu, 28 Dec 2017 15:36:55 +0800 Subject: [PATCH 08/10] improve assignment variations (#2671) --- test/ufuzz.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/ufuzz.js b/test/ufuzz.js index 14b8f114..1589d5f1 100644 --- a/test/ufuzz.js +++ b/test/ufuzz.js @@ -329,7 +329,8 @@ function createTopLevelCode() { rng(2) == 0 ? createStatements(3, MAX_GENERATION_RECURSION_DEPTH, CANNOT_THROW, CANNOT_BREAK, CANNOT_CONTINUE, CANNOT_RETURN, 0) : createFunctions(rng(MAX_GENERATED_TOPLEVELS_PER_RUN) + 1, MAX_GENERATION_RECURSION_DEPTH, DEFUN_OK, CANNOT_THROW, 0), - 'console.log(null, a, b, c);' // preceding `null` makes for a cleaner output (empty string still shows up etc) + // preceding `null` makes for a cleaner output (empty string still shows up etc) + 'console.log(null, a, b, c, Infinity, NaN, undefined);' ].join('\n'); } @@ -635,6 +636,8 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) { case p++: case p++: return getVarName(); + case p++: + return getVarName() + createAssignment() + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow); case p++: return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow); case p++: From b95e3338d9704927046a030fe814302f55737e0d Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Thu, 28 Dec 2017 17:01:01 +0800 Subject: [PATCH 09/10] fix `pure_getters` on `AST_Binary` (#2681) fixes #2678 --- lib/compress.js | 11 ++--------- test/compress/pure_getters.js | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index ac5cd235..4e3a8f82 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -1693,15 +1693,8 @@ merge(Compressor.prototype, { return this.operator == "void"; }); def(AST_Binary, function(compressor) { - switch (this.operator) { - case "&&": - return this.left._dot_throw(compressor); - case "||": - return this.left._dot_throw(compressor) - && this.right._dot_throw(compressor); - default: - return false; - } + return (this.operator == "&&" || this.operator == "||") + && (this.left._dot_throw(compressor) || this.right._dot_throw(compressor)); }) def(AST_Assign, function(compressor) { return this.operator == "=" diff --git a/test/compress/pure_getters.js b/test/compress/pure_getters.js index 4174bc1b..7185e0c6 100644 --- a/test/compress/pure_getters.js +++ b/test/compress/pure_getters.js @@ -611,3 +611,35 @@ issue_2313_6: { x(); } } + +issue_2678: { + options = { + pure_getters: "strict", + side_effects: true, + } + input: { + var a = 1, c = "FAIL"; + (function f() { + (a-- && f()).p; + return { + get p() { + c = "PASS"; + } + }; + })(); + console.log(c); + } + expect: { + var a = 1, c = "FAIL"; + (function f() { + (a-- && f()).p; + return { + get p() { + c = "PASS"; + } + }; + })(); + console.log(c); + } + expect_stdout: "PASS" +} From 8ca49155a8a3b0030b621fd4a5ba63147ea1c72f Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Fri, 29 Dec 2017 03:07:39 +0800 Subject: [PATCH 10/10] v3.3.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d6b7b411..57cf640b 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "homepage": "http://lisperator.net/uglifyjs", "author": "Mihai Bazon (http://lisperator.net/)", "license": "BSD-2-Clause", - "version": "3.3.2", + "version": "3.3.3", "engines": { "node": ">=0.8.0" },