From 60c0bc1e6b1db1a7288c7924231b28a0e9f35cff Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Fri, 29 May 2020 10:48:26 +0100 Subject: [PATCH] fix corner case in `evaluate` (#3934) fixes #3933 --- lib/compress.js | 95 ++++++++++++++++----------- test/compress/collapse_vars.js | 6 +- test/compress/dead-code.js | 2 +- test/compress/evaluate.js | 114 ++++++++++++++++++++++++++++++++- 4 files changed, 172 insertions(+), 45 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index daf60ee8..b82ac811 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -3288,15 +3288,18 @@ merge(Compressor.prototype, { var lhs = this.left; if (!ignore_side_effects) { if (!(lhs instanceof AST_SymbolRef)) return this; - if (!HOP(lhs, "_eval") && !lhs.fixed) return this; - var def = lhs.definition(); - if (def.undeclared) return this; - if (def.last_ref !== lhs) return this; - if (def.single_use == "m") return this; + if (!HOP(lhs, "_eval")) { + if (!lhs.fixed) return this; + var def = lhs.definition(); + if (!def.fixed) return this; + if (def.undeclared) return this; + if (def.last_ref !== lhs) return this; + if (def.single_use == "m") return this; + } } var op = this.operator; var node; - if (HOP(lhs, "_eval") || !lhs.fixed) { + if (HOP(lhs, "_eval") || !(lhs instanceof AST_SymbolRef) || !lhs.fixed_value()) { node = op == "=" ? this.right : make_node(AST_Binary, this, { operator: op.slice(0, -1), left: lhs, @@ -3356,7 +3359,7 @@ merge(Compressor.prototype, { key = key._eval(compressor, ignore_side_effects, cached, depth); if (key === prop.key) return this; } - if (typeof Object.prototype[key] === 'function') { + if (typeof Object.prototype[key] === "function") { return this; } if (prop.value instanceof AST_Function) continue; @@ -3372,7 +3375,7 @@ merge(Compressor.prototype, { var e = this.expression; var op = this.operator; // Function would be evaluated to an array and so typeof would - // incorrectly return 'object'. Hence making is a special case. + // incorrectly return "object". Hence making is a special case. if (compressor.option("typeofs") && op == "typeof" && (e instanceof AST_Lambda @@ -3400,7 +3403,11 @@ merge(Compressor.prototype, { case "++": case "--": if (!(e instanceof AST_SymbolRef)) return this; - if (!ignore_side_effects && e.definition().last_ref !== e) return this; + if (!ignore_side_effects) { + var def = e.definition(); + if (def.undeclared) return this; + if (def.last_ref !== e) return this; + } if (HOP(e, "_eval")) v = +(op[0] + 1) + +v; modified(e); return v; @@ -3409,8 +3416,17 @@ merge(Compressor.prototype, { }); def(AST_UnaryPostfix, function(compressor, ignore_side_effects, cached, depth) { var e = this.expression; - if (!e.fixed) return this; - if (!ignore_side_effects && e.definition().last_ref !== e) return this; + if (!(e instanceof AST_SymbolRef)) { + if (!ignore_side_effects) return this; + } else if (!HOP(e, "_eval")) { + if (!e.fixed) return this; + if (!ignore_side_effects) { + var def = e.definition(); + if (!def.fixed) return this; + if (def.undeclared) return this; + if (def.last_ref !== e) return this; + } + } var v = e._eval(compressor, ignore_side_effects, cached, depth + 1); if (v === e) return this; modified(e); @@ -7835,34 +7851,35 @@ merge(Compressor.prototype, { var seq = self.lift_sequences(compressor); if (seq !== self) return seq.optimize(compressor); } - if (!compressor.option("assignments")) return self; - if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary) { - // x = expr1 OP expr2 - if (self.right.left instanceof AST_SymbolRef - && 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; - } - 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; - } - } - if ((self.operator == "-=" || self.operator == "+=" - && (self.left.is_boolean(compressor) || self.left.is_number(compressor))) - && self.right instanceof AST_Number - && self.right.value == 1) { - var op = self.operator.slice(0, -1); - return make_node(AST_UnaryPrefix, self, { - operator: op + op, - expression: self.left - }); + if (compressor.option("assignments")) { + if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary) { + // x = expr1 OP expr2 + if (self.right.left instanceof AST_SymbolRef + && 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; + } + 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; + } + } + if ((self.operator == "-=" || self.operator == "+=" + && (self.left.is_boolean(compressor) || self.left.is_number(compressor))) + && self.right instanceof AST_Number + && self.right.value == 1) { + var op = self.operator.slice(0, -1); + return make_node(AST_UnaryPrefix, self, { + operator: op + op, + expression: self.left + }); + } } return try_evaluate(compressor, self); diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js index 82fb057e..6244e03f 100644 --- a/test/compress/collapse_vars.js +++ b/test/compress/collapse_vars.js @@ -917,7 +917,7 @@ collapse_vars_lvalues_drop_assign: { } } -collapse_vars_misc1: { +collapse_vars_misc: { options = { booleans: true, collapse_vars: true, @@ -971,8 +971,8 @@ collapse_vars_misc1: { function f7() { var b = window.a * window.z; return b + b } function f8() { var b = window.a * window.z; return b + (5 + b) } function f9() { var b = window.a * window.z; return bar() || b } - function f10(x) { var a = 5; return a += 3; } - function f11(x) { var a = 5; return a += 2; } + function f10(x) { return 8; } + function f11(x) { return 7; } } } diff --git a/test/compress/dead-code.js b/test/compress/dead-code.js index 795d1f7c..22eb0d91 100644 --- a/test/compress/dead-code.js +++ b/test/compress/dead-code.js @@ -236,7 +236,7 @@ collapse_vars_lvalues_drop_assign: { } } -collapse_vars_misc1: { +collapse_vars_misc: { options = { collapse_vars: true, dead_code: true, diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js index 868ad154..a7b5a4a5 100644 --- a/test/compress/evaluate.js +++ b/test/compress/evaluate.js @@ -2503,7 +2503,7 @@ inlined_increment_prefix: { expect: { var a = 0; void ++a; - console.log(a += 0); + console.log(1); } expect_stdout: "1" } @@ -2525,7 +2525,7 @@ inlined_increment_postfix: { expect: { var a = 0; void a++; - console.log(a += 0); + console.log(1); } expect_stdout: "1" } @@ -2544,3 +2544,113 @@ compound_assignment_to_property: { } expect_stdout: "PASS" } + +issue_2208_assign: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + a = 42; + console.log({ + p: function() { + return function() { + return this.a; + }(); + } + }.p()); + } + expect: { + a = 42; + console.log({ + p: function() { + return function() { + return this.a; + }(); + } + }.p()); + } + expect_stdout: "42" +} + +issue_2208_postfix: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + a = 41; + a++; + console.log({ + p: function() { + return function() { + return this.a; + }(); + } + }.p()); + } + expect: { + a = 41; + a++; + console.log({ + p: function() { + return function() { + return this.a; + }(); + } + }.p()); + } + expect_stdout: "42" +} + +issue_2208_prefix: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + a = 43; + --a; + console.log({ + p: function() { + return function() { + return this.a; + }(); + } + }.p()); + } + expect: { + a = 43; + --a; + console.log({ + p: function() { + return function() { + return this.a; + }(); + } + }.p()); + } + expect_stdout: "42" +} + +issue_3933: { + options = { + evaluate: true, + reduce_vars: true, + unused: true, + } + input: { + (function(a, b) { + a && (b ^= 1) && console.log("PASS"); + })(1); + } + expect: { + (function(a, b) { + 1, (b ^= 1), console.log("PASS"); + })(); + } + expect_stdout: "PASS" +} -- 2.34.1