From 2f3b46021239c88e4eb38a8d26ae105aaf78d2d1 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Mon, 28 Oct 2019 13:37:08 +0800 Subject: [PATCH] fix & enhance `unsafe_math` (#3537) closes #3535 fixes #3536 --- lib/compress.js | 19 +++++++++++++++++-- test/compress/numbers.js | 20 +++++++++++++++++++- test/ufuzz/index.js | 10 +++++++--- 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 8e116c6c..442df59d 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -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, { diff --git a/test/compress/numbers.js b/test/compress/numbers.js index 7946ba8a..63a100b7 100644 --- a/test/compress/numbers.js +++ b/test/compress/numbers.js @@ -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" +} diff --git a/test/ufuzz/index.js b/test/ufuzz/index.js index c71dbb39..e6ee3fde 100644 --- a/test/ufuzz/index.js +++ b/test/ufuzz/index.js @@ -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); -- 2.34.1