fix & enhance `unsafe_math` (#3537)
authorAlex Lam S.L <alexlamsl@gmail.com>
Mon, 28 Oct 2019 05:37:08 +0000 (13:37 +0800)
committerGitHub <noreply@github.com>
Mon, 28 Oct 2019 05:37:08 +0000 (13:37 +0800)
closes #3535
fixes #3536

lib/compress.js
test/compress/numbers.js
test/ufuzz/index.js

index 8e116c6..442df59 100644 (file)
@@ -2899,7 +2899,19 @@ merge(Compressor.prototype, {
               case ">=" : result = left >=  right; break;
               default   : return this;
             }
-            return isNaN(result) && compressor.find_parent(AST_With) ? this : result;
+            if (isNaN(result)) return compressor.find_parent(AST_With) ? this : result;
+            if (compressor.option("unsafe_math")
+                && typeof result == "number"
+                && (this.operator == "+" || this.operator == "-")) {
+                var digits = Math.max(0, decimals(left), decimals(right));
+                if (digits < 21) return +result.toFixed(digits);
+            }
+            return result;
+
+            function decimals(operand) {
+                var match = /(\.[0-9]*)?(e.+)?$/.exec(+operand);
+                return (match[1] || ".").length - 1 - (match[2] || "").slice(1);
+            }
         });
         def(AST_Conditional, function(compressor, cached, depth) {
             var condition = this.condition._eval(compressor, cached, depth);
@@ -5980,8 +5992,11 @@ merge(Compressor.prototype, {
                 // a + (b + c) => (a + b) + c
                 if (self.right instanceof AST_Binary
                     && self.right.operator != "%"
+                    && PRECEDENCE[self.right.operator] == PRECEDENCE[self.operator]
                     && self.right.is_number(compressor)
-                    && PRECEDENCE[self.right.operator] == PRECEDENCE[self.operator]) {
+                    && (self.operator != "+"
+                        || self.right.left.is_boolean(compressor)
+                        || self.right.left.is_number(compressor))) {
                     self = make_node(AST_Binary, self, {
                         operator: align(self.operator, self.right.operator),
                         left: make_node(AST_Binary, self.left, {
index 7946ba8..63a100b 100644 (file)
@@ -713,7 +713,7 @@ issue_3531_1: {
     }
     expect: {
         var a = "1";
-        console.log(typeof (a + 1 - (.2 + .1)));
+        console.log(typeof (a + 1 - .3));
     }
     expect_stdout: "number"
 }
@@ -747,3 +747,21 @@ issue_3531_3: {
     }
     expect_stdout: "-22"
 }
+
+issue_3536: {
+    options = {
+        evaluate: true,
+        unsafe_math: true,
+    }
+    input: {
+        var a = 100, b = 10;
+        var c = --a + ("23" - (b++, 1));
+        console.log(typeof c, a, b, c);
+    }
+    expect: {
+        var a = 100, b = 10;
+        var c = --a + ("23" - (b++, 1));
+        console.log(typeof c, a, b, c);
+    }
+    expect_stdout: "number 99 11 121"
+}
index c71dbb3..e6ee3fd 100644 (file)
@@ -1050,16 +1050,20 @@ function log_rename(options) {
     }
 }
 
+function orig_code(unsafe_math) {
+    return unsafe_math ? original_code.replace(/( - 0\.1){3}/g, " - 0.3") : original_code;
+}
+
 function log(options) {
+    options = JSON.parse(options);
     if (!ok) errorln("\n\n\n\n\n\n!!!!!!!!!!\n\n\n");
     errorln("//=============================================================");
     if (!ok) errorln("// !!!!!! Failed... round " + round);
     errorln("// original code");
-    try_beautify(original_code, false, original_result, errorln);
+    try_beautify(orig_code(options.compress.unsafe_math), options.toplevel, original_result, errorln);
     errorln();
     errorln();
     errorln("//-------------------------------------------------------------");
-    options = JSON.parse(options);
     if (typeof uglify_code == "string") {
         errorln("// uglified code");
         try_beautify(uglify_code, options.toplevel, uglify_result, errorln);
@@ -1103,7 +1107,7 @@ for (var round = 1; round <= num_iterations; round++) {
     var orig_result = [ sandbox.run_code(original_code) ];
     errored = typeof orig_result[0] != "string";
     if (!errored) {
-        orig_result.push(sandbox.run_code(original_code, true), sandbox.run_code(original_code.replace(/( - 0\.1){3}/g, " - 0.3")));
+        orig_result.push(sandbox.run_code(original_code, true), sandbox.run_code(orig_code(true)));
     }
     (errored ? fallback_options : minify_options).forEach(function(options) {
         var o = JSON.parse(options);