fix corner case in `unsafe_math` (#3683)
authorAlex Lam S.L <alexlamsl@gmail.com>
Tue, 14 Jan 2020 20:05:58 +0000 (04:05 +0800)
committerGitHub <noreply@github.com>
Tue, 14 Jan 2020 20:05:58 +0000 (04:05 +0800)
fixes #3682

lib/compress.js
test/compress/numbers.js

index e645bd4..ffa5a64 100644 (file)
@@ -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) {
index c40ca09..fdfb6c2 100644 (file)
@@ -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"
+}