From: Alex Lam S.L Date: Thu, 4 Jun 2020 20:06:43 +0000 (+0100) Subject: fix corner cases in `assignments`, `reduce_vars` & `unused` (#3950) X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=04fd3d90f8600bc8c6c5715abce06617110d88d6;p=UglifyJS.git fix corner cases in `assignments`, `reduce_vars` & `unused` (#3950) fixes #3949 fixes #3951 --- diff --git a/lib/compress.js b/lib/compress.js index f3fc5e0e..17b3ecf7 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -384,10 +384,10 @@ merge(Compressor.prototype, { reset_def(tw, compressor, def); if (def.fixed === null) { def.safe_ids = tw.safe_ids; - mark(tw, def, true); + mark(tw, def); } else if (def.fixed) { tw.loop_ids[def.id] = tw.in_loop; - mark(tw, def, true); + mark(tw, def); } }); scope.may_call_this = function() { @@ -446,8 +446,8 @@ merge(Compressor.prototype, { tw.safe_ids = Object.getPrototypeOf(tw.safe_ids); } - function mark(tw, def, safe) { - tw.safe_ids[def.id] = safe && {}; + function mark(tw, def) { + tw.safe_ids[def.id] = {}; } function push_ref(def, ref) { @@ -459,10 +459,11 @@ merge(Compressor.prototype, { if (def.single_use == "m") return false; var safe = tw.safe_ids[def.id]; if (safe) { - if (!HOP(tw.safe_ids, def.id)) safe.read = safe.read ? true : tw.safe_ids; + if (!HOP(tw.safe_ids, def.id)) safe.read = safe.read && safe.read !== tw.safe_ids ? true : tw.safe_ids; if (def.fixed == null) { if (is_arguments(def)) return false; if (def.global && def.name == "arguments") return false; + tw.loop_ids[def.id] = null; def.fixed = make_node(AST_Undefined, def.orig[0]); return true; } @@ -478,20 +479,23 @@ merge(Compressor.prototype, { delete def.safe_ids; return true; } + if (def.fixed === false) return false; var safe = tw.safe_ids[def.id]; if (!HOP(tw.safe_ids, def.id)) { if (!safe) return false; - safe.assign = safe.assign ? true : tw.safe_ids; + if (safe.read && def.scope !== tw.find_parent(AST_Scope)) return false; + safe.assign = safe.assign && safe.assign !== tw.safe_ids ? true : tw.safe_ids; } - if (!safe_to_read(tw, def)) return false; - if (def.fixed === false) return false; - if (def.fixed != null && safe.read && safe.read !== tw.safe_ids) return false; - return all(def.orig, function(sym) { + if (def.fixed != null && safe.read) { + if (safe.read !== tw.safe_ids) return false; + if (tw.loop_ids[def.id] !== tw.in_loop) return false; + } + return safe_to_read(tw, def) && all(def.orig, function(sym) { return !(sym instanceof AST_SymbolLambda); }); } - function ref_once(tw, compressor, def) { + function ref_once(compressor, def) { return compressor.option("unused") && !def.scope.pinned() && def.references.length - def.recursive_refs == 1; @@ -568,22 +572,17 @@ merge(Compressor.prototype, { } var d = sym.definition(); d.assignments++; + var fixed = d.fixed; var eq = node.operator == "="; var value = eq ? node.right : node; if (is_modified(compressor, tw, node, value, 0)) return; - var safe = (eq || safe_to_read(tw, d)) && safe_to_assign(tw, d); - var fixed = d.fixed; - if (safe) { + node.right.walk(tw); + if ((eq || safe_to_read(tw, d)) && safe_to_assign(tw, d)) { push_ref(d, sym); - mark(tw, d, false); - node.right.walk(tw); - mark(tw, d, true); - if (eq) mark_escaped(tw, d, sym.scope, node, value, 0, 1); - } else { - descend(); - } - if (fixed !== false && d.fixed !== false) { + mark(tw, d); if (eq) { + tw.loop_ids[d.id] = tw.in_loop; + mark_escaped(tw, d, sym.scope, node, value, 0, 1); sym.fixed = d.fixed = function() { return node.right; }; @@ -600,6 +599,9 @@ merge(Compressor.prototype, { } sym.fixed.assigns = eq || !fixed.assigns ? [] : fixed.assigns.slice(); sym.fixed.assigns.push(node); + } else { + sym.walk(tw); + d.fixed = false; } return true; }); @@ -739,7 +741,7 @@ merge(Compressor.prototype, { var d = arg.definition(); if (d.fixed === undefined && (!fn.uses_arguments || tw.has_directive("use strict"))) { tw.loop_ids[d.id] = tw.in_loop; - mark(tw, d, true); + mark(tw, d); var value = iife.args[i]; d.fixed = function() { var j = fn.argnames.indexOf(arg); @@ -800,7 +802,7 @@ merge(Compressor.prototype, { var recursive = recursive_ref(tw, d); if (recursive) { d.recursive_refs++; - } else if (value && ref_once(tw, compressor, d)) { + } else if (value && ref_once(compressor, d)) { d.in_loop = tw.loop_ids[d.id] !== tw.in_loop; d.single_use = value instanceof AST_Lambda && !value.pinned() @@ -864,15 +866,10 @@ merge(Compressor.prototype, { } var d = exp.definition(); d.assignments++; - var safe = safe_to_read(tw, d) && safe_to_assign(tw, d); var fixed = d.fixed; - if (safe) { + if (safe_to_read(tw, d) && safe_to_assign(tw, d)) { push_ref(d, exp); - mark(tw, d, true); - } else { - descend(); - } - if (fixed !== false && d.fixed !== false) { + mark(tw, d); if (fixed == null) fixed = make_node(AST_Undefined, d.orig[0]); d.fixed = function() { var value = fixed instanceof AST_Node ? fixed : fixed(); @@ -901,29 +898,28 @@ merge(Compressor.prototype, { }; exp.fixed.assigns = fixed.assigns; } + } else { + exp.walk(tw); + d.fixed = false; } return true; }); def(AST_VarDef, function(tw, descend) { var node = this; + if (!node.value) return; + descend(); var d = node.name.definition(); - if (node.value) { - if (safe_to_assign(tw, d)) { - tw.loop_ids[d.id] = tw.in_loop; - mark(tw, d, false); - descend(); - mark(tw, d, true); - if (d.fixed !== false) { - d.fixed = function() { - return node.value; - }; - d.fixed.assigns = [ node ]; - } - return true; - } else { - d.fixed = false; - } + if (safe_to_assign(tw, d)) { + mark(tw, d); + tw.loop_ids[d.id] = tw.in_loop; + d.fixed = function() { + return node.value; + }; + d.fixed.assigns = [ node ]; + } else { + d.fixed = false; } + return true; }); def(AST_While, function(tw, descend) { var saved_loop = tw.in_loop; @@ -4710,7 +4706,7 @@ merge(Compressor.prototype, { } } } - track_assigns(node_def, sym); + if (track_assigns(node_def, sym) && is_lhs(sym, node) !== sym) add_assigns(node_def, sym); return true; } if (node instanceof AST_SymbolRef) { @@ -7885,16 +7881,22 @@ merge(Compressor.prototype, { && self.right.left.name == self.left.name && ASSIGN_OPS[self.right.operator]) { // x = x - 2 => x -= 2 - self.operator = self.right.operator + "="; - self.right = self.right.right; + return make_node(AST_Assign, self, { + operator: self.right.operator + "=", + left: self.left, + right: self.right.right, + }).optimize(compressor); } else if (self.right.right instanceof AST_SymbolRef && self.right.right.name == self.left.name && ASSIGN_OPS_COMMUTATIVE[self.right.operator] && !self.right.left.has_side_effects(compressor)) { // x = 2 & x => x &= 2 - self.operator = self.right.operator + "="; - self.right = self.right.left; + return make_node(AST_Assign, self, { + operator: self.right.operator + "=", + left: self.left, + right: self.right.left, + }).optimize(compressor); } } if ((self.operator == "-=" || self.operator == "+=" diff --git a/test/compress/assignment.js b/test/compress/assignments.js similarity index 88% rename from test/compress/assignment.js rename to test/compress/assignments.js index 2230272a..0f87f9a0 100644 --- a/test/compress/assignment.js +++ b/test/compress/assignments.js @@ -407,3 +407,57 @@ issue_3429_2: { } expect_stdout: "undefined" } + +issue_3949_1: { + options = { + assignments: true, + evaluate: true, + reduce_vars: true, + } + input: { + var a = 42; + function f() { + var b = a; + b = b >> 2; + return 100 + b; + } + console.log(f()); + } + expect: { + var a = 42; + function f() { + var b = a; + b >>= 2; + return 100 + b; + } + console.log(f()); + } + expect_stdout: "110" +} + +issue_3949_2: { + options = { + assignments: true, + evaluate: true, + reduce_vars: true, + } + input: { + var a = 42; + function f() { + var b = a; + b = 5 & b; + return 100 + b; + } + console.log(f()); + } + expect: { + var a = 42; + function f() { + var b = a; + b &= 5; + return 100 + b; + } + console.log(f()); + } + expect_stdout: "100" +} diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js index 546e0d3b..cea5ca84 100644 --- a/test/compress/collapse_vars.js +++ b/test/compress/collapse_vars.js @@ -3001,6 +3001,7 @@ issue_2298: { expect: { !function() { (function() { + 0; try { !function(b) { (void 0)[1] = "foo"; diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index b21ca399..f3aeab21 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -2285,7 +2285,7 @@ issue_3598: { try { (function() { a = "PASS"; - var c = (void (c.p = 0))[!1]; + (void ((void 0).p = 0))[!1]; })(); } catch (e) {} console.log(a); @@ -2557,10 +2557,9 @@ issue_3899: { console.log(typeof a); } expect: { - 0; - var a = function() { + function a() { return 2; - }; + } console.log(typeof a); } expect_stdout: "function" @@ -2625,3 +2624,31 @@ assign_if_assign_read: { } expect_stdout: "PASS" } + +issue_3951: { + options = { + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = console.log("PASS"); + console.log(a); + a = "0"; + console.log(a.p = 0); + a && a; + } + expect: { + var a = console.log("PASS"); + console.log(a); + a = "0"; + console.log(a.p = 0); + } + expect_stdout: [ + "PASS", + "undefined", + "0", + ] +} diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js index 4e3bc725..fd8deab5 100644 --- a/test/compress/evaluate.js +++ b/test/compress/evaluate.js @@ -1579,9 +1579,9 @@ issue_2968_1: { expect: { var c = "FAIL"; (function() { - b = -(a = 42), - void ((a <<= 0) && (a[(c = "PASS", 0 >>> (b += 1))] = 0)); - var a, b; + a = 42, + void ((a <<= 0) && (a[(c = "PASS", 0)] = 0)); + var a; })(); console.log(c); } @@ -2341,10 +2341,7 @@ issue_3878_1: { console.log(b ? "PASS" : "FAIL"); } expect: { - var b = function(a) { - return (a = 0) == (a && this > (a += 0)); - }(); - console.log(b ? "PASS" : "FAIL"); + console.log(true ? "PASS" : "FAIL"); } expect_stdout: "PASS" } @@ -2435,12 +2432,11 @@ issue_3903: { console.log(d); } expect: { - var a = "PASS"; function f(b, c) { return console, c; } - var d = f(f(), a = a); - console.log(d); + f(f(), "PASS"); + console.log("PASS"); } expect_stdout: "PASS" } @@ -2649,7 +2645,7 @@ issue_3933: { } expect: { (function(a, b) { - 1, (b ^= 1), console.log("PASS"); + 1, 1, console.log("PASS"); })(); } expect_stdout: "PASS" diff --git a/test/compress/join_vars.js b/test/compress/join_vars.js index 590f9970..9f0b35b7 100644 --- a/test/compress/join_vars.js +++ b/test/compress/join_vars.js @@ -808,9 +808,9 @@ issue_3795: { } expect: { var a = "FAIL", d = function() { - if (a = 42, d) return -1; + if (void 0) return -1; a = "PASS"; - }(); + }(a = 42); console.log(a, d); } expect_stdout: "PASS undefined" diff --git a/test/compress/keep_fargs.js b/test/compress/keep_fargs.js index e1737081..e42b3978 100644 --- a/test/compress/keep_fargs.js +++ b/test/compress/keep_fargs.js @@ -306,6 +306,7 @@ issue_2298: { expect: { !function() { (function() { + 0; try { !function() { (void 0)[1] = "foo"; diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index cb7599a9..3368cc6f 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -1520,8 +1520,7 @@ func_inline: { } expect: { function f() { - console.log(1 + h()); - var h; + console.log(1 + (void 0)()); } } } @@ -2671,8 +2670,8 @@ var_assign_6: { } expect: { !function() { - var a = function(){}(a = 1); - console.log(a); + (function(){}()); + console.log(void 0); }(); } expect_stdout: "undefined" @@ -7111,3 +7110,166 @@ issue_3922: { } expect_stdout: "PASS" } + +issue_3949_1: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + (function f(a) { + var a = void (a = 0, g); + function g() { + console.log(typeof a); + } + g(); + })(); + } + expect: { + (function f(a) { + var a = void (a = 0, g); + function g() { + console.log(typeof a); + } + g(); + })(); + } + expect_stdout: "undefined" +} + +issue_3949_2: { + options = { + reduce_vars: true, + unused: true, + } + input: { + (function f(a) { + var a = void (a = 0, g); + function g() { + console.log(typeof a); + } + g(); + })(); + } + expect: { + (function(a) { + a = void (a = 0, g); + function g() { + console.log(typeof a); + } + g(); + })(); + } + expect_stdout: "undefined" +} + +issue_3949_3: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + function f() {} + for (var a, i = 3; 0 <= --i; ) { + a = f; + console.log(a === b); + var b = a; + } + } + expect: { + function f() {} + for (var a, i = 3; 0 <= --i; ) { + a = f; + console.log(a === b); + var b = a; + } + } + expect_stdout: [ + "false", + "true", + "true", + ] +} + +issue_3949_4: { + options = { + reduce_vars: true, + unused: true, + toplevel: true, + } + input: { + function f() {} + for (var a, i = 3; 0 <= --i; ) { + a = f; + console.log(a === b); + var b = a; + } + } + expect: { + function f() {} + for (var a, i = 3; 0 <= --i; ) { + a = f; + console.log(a === b); + var b = a; + } + } + expect_stdout: [ + "false", + "true", + "true", + ] +} + +local_assignment_lambda: { + options = { + evaluate: true, + reduce_vars: true, + sequences: true, + toplevel: true, + unused: true, + } + input: { + var a = "FAIL"; + function f() { + a = "PASS"; + console.log(a); + } + f(); + f(); + } + expect: { + function f() { + console.log("PASS"); + } + f(), + f(); + } + expect_stdout: [ + "PASS", + "PASS", + ] +} + +local_assignment_loop: { + options = { + evaluate: true, + reduce_vars: true, + sequences: true, + toplevel: true, + unused: true, + } + input: { + var a = "FAIL"; + do { + a = "PASS"; + console.log(a); + } while (!console); + } + expect: { + do { + console.log("PASS"); + } while (!console); + } + expect_stdout: "PASS" +} diff --git a/test/compress/side_effects.js b/test/compress/side_effects.js index 8c263d47..63282660 100644 --- a/test/compress/side_effects.js +++ b/test/compress/side_effects.js @@ -97,9 +97,8 @@ issue_2233_2: { var RegExp; UndeclaredGlobal; function foo() { - var Number; AnotherUndeclaredGlobal; - Number.isNaN; + (void 0).isNaN; } } }