From 91d87ae6633ba66706df32e8b955061c0e9d788f Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 15 Jan 2020 04:05:58 +0800 Subject: [PATCH] fix corner case in `unsafe_math` (#3683) fixes #3682 --- lib/compress.js | 81 +++++++++++++++++++++++++++++++--------- test/compress/numbers.js | 52 +++++++++++++++++++++++++- 2 files changed, 113 insertions(+), 20 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index e645bd4e58..ffa5a64484 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -2485,6 +2485,60 @@ merge(Compressor.prototype, { node.DEFMETHOD("is_truthy", func); }); + // is_negative_zero() + // return true if the node may represent -0 + (function(def) { + def(AST_Node, return_true); + def(AST_Array, return_false); + function binary(op, left, right) { + switch (op) { + case "-": + return left.is_negative_zero() + && (!(right instanceof AST_Constant) || right.value == 0); + case "&&": + case "||": + case "*": + return left.is_negative_zero() || right.is_negative_zero(); + case "/": + case "%": + return left.is_negative_zero(); + default: + return false; + } + } + def(AST_Assign, function() { + var op = this.operator; + if (op == "=") return this.right.is_negative_zero(); + return binary(op.slice(0, -1), this.left, this.right); + }); + def(AST_Binary, function() { + return binary(this.operator, this.left, this.right); + }); + def(AST_Constant, function() { + return this.value == 0 && 1 / this.value < 0; + }); + def(AST_Lambda, return_false); + def(AST_Object, return_false); + def(AST_RegExp, return_false); + def(AST_Sequence, function() { + return this.tail_node().is_negative_zero(); + }); + def(AST_SymbolRef, function() { + var fixed = this.fixed_value(); + if (!fixed) return true; + this.is_negative_zero = return_true; + var result = fixed.is_negative_zero(); + delete this.is_negative_zero; + return result; + }); + def(AST_UnaryPrefix, function() { + return this.operator == "+" && this.expression.is_negative_zero() + || this.operator == "-"; + }); + })(function(node, func) { + node.DEFMETHOD("is_negative_zero", func); + }); + // may_throw_on_access() // returns true if this node may be null, undefined or contain `AST_Accessor` (function(def) { @@ -6111,7 +6165,6 @@ merge(Compressor.prototype, { }); var indexFns = makePredicate("indexOf lastIndexOf"); - var minus_zero_op = makePredicate("- * / %"); var commutativeOperators = makePredicate("== === != !== * & | ^"); function is_object(node) { return node instanceof AST_Array @@ -6533,6 +6586,7 @@ merge(Compressor.prototype, { && (self.operator != "+" || self.right.left.is_boolean(compressor) || self.right.left.is_number(compressor)) + && (self.operator != "-" || !self.left.is_negative_zero()) && (self.right.left.is_constant_expression() || !self.right.right.has_side_effects(compressor))) { self = make_node(AST_Binary, self, { @@ -6567,7 +6621,12 @@ merge(Compressor.prototype, { self = make_binary(self, self.left.operator, lhs, self.left.right); } else if (self.left.right instanceof AST_Constant) { var rhs = make_binary(self.left, align(self.left.operator, self.operator), self.left.right, self.right, self.left.right.start, self.right.end); - self = make_binary(self, self.left.operator, self.left.left, rhs); + if (self.left.operator != "-" + || !self.right.value + || rhs.evaluate(compressor) + || !self.left.left.is_negative_zero()) { + self = make_binary(self, self.left.operator, self.left.left, rhs); + } } } break; @@ -6580,7 +6639,7 @@ merge(Compressor.prototype, { operator: "+", expression: self.right }).optimize(compressor); - if (self.right.is_number(compressor) && !may_be_minus_zero(self.right)) return self.right; + if (self.right.is_number(compressor) && !self.right.is_negative_zero()) return self.right; } break; // 1 * n => n @@ -6601,7 +6660,7 @@ merge(Compressor.prototype, { operator: "+", expression: self.left }).optimize(compressor); - if (self.left.is_number(compressor) && !may_be_minus_zero(self.left)) return self.left; + if (self.left.is_number(compressor) && !self.left.is_negative_zero()) return self.left; } break; // n - 0 => n @@ -6756,20 +6815,6 @@ merge(Compressor.prototype, { && self.left.expression instanceof AST_Number && self.left.expression.value == 1; } } - - function may_be_minus_zero(node) { - var ev = node.evaluate(compressor); - if (ev instanceof AST_Node) { - var op = ev.operator; - if (!op) return true; - if (ev instanceof AST_Assign) { - if (op == "=") return may_be_minus_zero(ev.right); - op = op.slice(0, -1); - } - if (minus_zero_op[op]) return true; - if (ev instanceof AST_UnaryPrefix && op == "+") return true; - } else if (ev == 0 && 1 / ev < 0) return true; - } }); function recursive_ref(compressor, def) { diff --git a/test/compress/numbers.js b/test/compress/numbers.js index c40ca09ddf..fdfb6c2fea 100644 --- a/test/compress/numbers.js +++ b/test/compress/numbers.js @@ -1075,11 +1075,11 @@ issue_3653: { } expect: { console.log(0 - (console && 0)); - console.log(0 - (console && 0) + 0); + console.log(0 - (console && 0)); console.log(0 - (0 - (console && 0))); console.log(0 - (console && 0)); console.log(1 / (0 - (console && 0))); - console.log(0 - (console && 0) + 0); + console.log(0 - (console && 0)); console.log(0 - (console && 0)); console.log(0 - (console && 0)); console.log(0 - (console && 0)); @@ -1167,3 +1167,51 @@ issue_3676_2: { } expect_stdout: "NaN" } + +issue_3682_1: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + var a = -0; + console.log(1 / (a - 1 + 1)); + } + expect: { + var a = -0; + console.log(1 / (a - 1 + 1)); + } + expect_stdout: "Infinity" +} + +issue_3682_2: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + var a = -0, b = 1; + console.log(1 / (a - (b - b))); + } + expect: { + var a = -0, b = 1; + console.log(1 / (a - (b - b))); + } + expect_stdout: "-Infinity" +} + +issue_3682_3: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + var a = -0, b = 1, c = -1; + console.log(1 / (a - (+b + +c))); + } + expect: { + var a = -0, b = 1, c = -1; + console.log(1 / (a - (+b + +c))); + } + expect_stdout: "-Infinity" +}