From 50a578c1f63e61654d71642cbfa35f41da9bd626 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sun, 27 Oct 2019 03:07:07 +0800 Subject: [PATCH] compress arithmetic expressions further (#3529) --- lib/compress.js | 152 +++++++++-------- test/compress/asm.js | 2 +- test/compress/numbers.js | 353 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 432 insertions(+), 75 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 03c1f590..6d8c1f8f 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -2437,9 +2437,10 @@ merge(Compressor.prototype, { || this.operator == "=" && this.right.is_number(compressor); }); def(AST_Binary, function(compressor) { - return binary[this.operator] || this.operator == "+" - && this.left.is_number(compressor) - && this.right.is_number(compressor); + if (binary[this.operator]) return true; + if (this.operator != "+") return false; + return (this.left.is_boolean(compressor) || this.left.is_number(compressor)) + && (this.right.is_boolean(compressor) || this.right.is_number(compressor)); }); var fn = makePredicate([ "charCodeAt", @@ -5777,6 +5778,7 @@ merge(Compressor.prototype, { } } if (compressor.option("evaluate")) { + var associative = true; switch (self.operator) { case "&&": var ll = fuzzy_eval(self.left); @@ -5845,9 +5847,6 @@ merge(Compressor.prototype, { }).optimize(compressor); } break; - } - var associative = true; - switch (self.operator) { case "+": // "foo" + ("bar" + x) => "foobar" + x if (self.left instanceof AST_Constant @@ -5927,13 +5926,38 @@ merge(Compressor.prototype, { }); break; } + case "-": + // a - -b => a + b + if (self.right instanceof AST_UnaryPrefix + && self.right.operator == "-" + && self.left.is_number(compressor)) { + self = make_node(AST_Binary, self, { + operator: "+", + left: self.left, + right: self.right.expression + }); + break; + } case "*": + case "/": associative = compressor.option("unsafe_math"); + // +a - b => a - b + // a - +b => a - b + if (self.operator != "+") { + if (self.left instanceof AST_UnaryPrefix && self.left.operator == "+") { + self.left = self.left.expression; + } + if (self.right instanceof AST_UnaryPrefix && self.right.operator == "+") { + self.right = self.right.expression; + } + } case "&": case "|": case "^": // a + +b => +b + a - if (self.left.is_number(compressor) + if (self.operator != "-" + && self.operator != "/" + && self.left.is_number(compressor) && self.right.is_number(compressor) && reversible() && !(self.left instanceof AST_Binary @@ -5951,77 +5975,56 @@ merge(Compressor.prototype, { self = best_of(compressor, self, reversed); } } - if (associative && self.is_number(compressor)) { - // a + (b + c) => (a + b) + c - if (self.right instanceof AST_Binary - && self.right.operator == self.operator) { - self = make_node(AST_Binary, self, { + if (!associative || !self.is_number(compressor)) break; + // a + (b + c) => (a + b) + c + if (self.right instanceof AST_Binary + && self.right.operator != "%" + && PRECEDENCE[self.right.operator] == PRECEDENCE[self.operator]) { + self = make_node(AST_Binary, self, { + operator: align(self.operator, self.right.operator), + left: make_node(AST_Binary, self.left, { operator: self.operator, + left: self.left, + right: self.right.left, + start: self.left.start, + end: self.right.left.end + }), + right: self.right.right + }); + } + // (2 * n) * 3 => 6 * n + // (n + 2) + 3 => n + 5 + if (self.right instanceof AST_Constant + && self.left instanceof AST_Binary + && self.left.operator != "%" + && PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]) { + if (self.left.left instanceof AST_Constant) { + self = make_node(AST_Binary, self, { + operator: self.left.operator, left: make_node(AST_Binary, self.left, { operator: self.operator, - left: self.left, - right: self.right.left, - start: self.left.start, - end: self.right.left.end + left: self.left.left, + right: self.right, + start: self.left.left.start, + end: self.right.end }), - right: self.right.right + right: self.left.right }); - } - // (n + 2) + 3 => 5 + n - // (2 * n) * 3 => 6 + n - if (self.right instanceof AST_Constant - && self.left instanceof AST_Binary - && self.left.operator == self.operator) { - if (self.left.left instanceof AST_Constant) { - self = make_node(AST_Binary, self, { - operator: self.operator, - left: make_node(AST_Binary, self.left, { - operator: self.operator, - left: self.left.left, - right: self.right, - start: self.left.left.start, - end: self.right.end - }), - right: self.left.right - }); - } else if (self.left.right instanceof AST_Constant) { - self = make_node(AST_Binary, self, { - operator: self.operator, - left: make_node(AST_Binary, self.left, { - operator: self.operator, - left: self.left.right, - right: self.right, - start: self.left.right.start, - end: self.right.end - }), - right: self.left.left - }); - } - } - // (a | 1) | (2 | d) => (3 | a) | b - if (self.left instanceof AST_Binary - && self.left.operator == self.operator - && self.left.right instanceof AST_Constant - && self.right instanceof AST_Binary - && self.right.operator == self.operator - && self.right.left instanceof AST_Constant) { + } else if (self.left.right instanceof AST_Constant) { self = make_node(AST_Binary, self, { - operator: self.operator, - left: make_node(AST_Binary, self.left, { - operator: self.operator, - left: make_node(AST_Binary, self.left.left, { - operator: self.operator, - left: self.left.right, - right: self.right.left, - start: self.left.right.start, - end: self.right.left.end - }), - right: self.left.left - }), - right: self.right.right + operator: self.left.operator, + left: self.left.left, + right: make_node(AST_Binary, self.left, { + operator: align(self.left.operator, self.operator), + left: self.left.right, + right: self.right, + start: self.left.right.start, + end: self.right.end + }) }); } } + break; } } if (compressor.option("unsafe")) { @@ -6086,6 +6089,17 @@ merge(Compressor.prototype, { } return self; + function align(ref, op) { + switch (ref) { + case "-": + return op == "+" ? "-" : "+"; + case "/": + return op == "*" ? "/" : "*"; + default: + return op; + } + } + function fuzzy_eval(node) { if (node.truthy) return true; if (node.falsy) return false; diff --git a/test/compress/asm.js b/test/compress/asm.js index cebe6838..1f00f8f2 100644 --- a/test/compress/asm.js +++ b/test/compress/asm.js @@ -96,7 +96,7 @@ asm_mixed: { return +sum; } function geometricMean(start, end) { - return start |= 0, end |= 0, +exp(+logSum(start, end) / +(end - start | 0)); + return start |= 0, end |= 0, +exp(logSum(start, end) / (end - start | 0)); } var exp = stdlib.Math.exp, log = stdlib.Math.log, values = new stdlib.Float64Array(buffer); return { geometricMean: geometricMean }; diff --git a/test/compress/numbers.js b/test/compress/numbers.js index 000b9ab5..3f579781 100644 --- a/test/compress/numbers.js +++ b/test/compress/numbers.js @@ -51,17 +51,20 @@ comparisons: { comparisons: true, } input: { + var x = "42", y = "0x30"; console.log( ~x === 42, - x % n === 42 + x % y === 42 ); } expect: { + var x = "42", y = "0x30"; console.log( 42 == ~x, - x % n == 42 + x % y == 42 ); } + expect_stdout: "false true" } evaluate_1: { @@ -103,12 +106,94 @@ evaluate_1: { } } +evaluate_1_unsafe_math: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + console.log( + x + 1 + 2, + x * 1 * 2, + +x + 1 + 2, + 1 + x + 2 + 3, + 1 | x | 2 | 3, + 1 + x-- + 2 + 3, + 1 + (x*y + 2) + 3, + 1 + (2 + x + 3), + 1 + (2 + ~x + 3), + -y + (2 + ~x + 3), + 1 & (2 & x & 3), + 1 + (2 + (x |= 0) + 3) + ); + } + expect: { + console.log( + x + 1 + 2, + 2 * x, + +x + 3, + 1 + x + 2 + 3, + 3 | x, + 6 + x--, + x*y + 6, + 1 + (2 + x + 3), + 6 + ~x, + 5 - y + ~x, + 0 & x, + 6 + (x |= 0) + ); + } +} + evaluate_2: { + options = { + evaluate: true, + unsafe_math: false, + } + input: { + var x = "42", y = null; + console.log( + x + 1 + 2, + x * 1 * 2, + +x + 1 + 2, + 1 + x + 2 + 3, + 1 | x | 2 | 3, + 1 + x-- + 2 + 3, + 1 + (x*y + 2) + 3, + 1 + (2 + x + 3), + 1 + (2 + ~x + 3), + -y + (2 + ~x + 3), + 1 & (2 & x & 3), + 1 + (2 + (x |= 0) + 3) + ); + } + expect: { + var x = "42", y = null; + console.log( + x + 1 + 2, + 1 * x * 2, + +x + 1 + 2, + 1 + x + 2 + 3, + 3 | x, + 1 + x-- + 2 + 3, + x*y + 2 + 1 + 3, + 1 + (2 + x + 3), + 2 + ~x + 3 + 1, + 2 + ~x + 3 - y, + 0 & x, + 2 + (x |= 0) + 3 + 1 + ); + } + expect_stdout: "4212 84 45 14223 43 48 6 47 -36 -37 0 47" +} + +evaluate_2_unsafe_math: { options = { evaluate: true, unsafe_math: true, } input: { + var x = "42", y = null; console.log( x + 1 + 2, x * 1 * 2, @@ -118,24 +203,30 @@ evaluate_2: { 1 + x-- + 2 + 3, 1 + (x*y + 2) + 3, 1 + (2 + x + 3), + 1 + (2 + ~x + 3), + -y + (2 + ~x + 3), 1 & (2 & x & 3), 1 + (2 + (x |= 0) + 3) ); } expect: { + var x = "42", y = null; console.log( x + 1 + 2, 2 * x, - 3 + +x, + +x + 3, 1 + x + 2 + 3, 3 | x, 6 + x--, - 6 + x*y, + x*y + 6, 1 + (2 + x + 3), + 6 + ~x, + 5 + ~x - y, 0 & x, 6 + (x |= 0) ); } + expect_stdout: "4212 84 45 14223 43 48 6 47 -36 -37 0 47" } evaluate_3: { @@ -148,7 +239,7 @@ evaluate_3: { console.log(1 + Number(x) + 2); } expect: { - console.log(3 + +x); + console.log(+x + 3); } } @@ -182,6 +273,258 @@ evaluate_4: { } } +evaluate_5: { + options = { + evaluate: true, + unsafe_math: false, + } + input: { + var a = true; + console.log( + +a + 2 + 3, + +a + 2 - 3, + +a - 2 + 3, + +a - 2 - 3, + 2 + +a + 3, + 2 + +a - 3, + 2 - +a + 3, + 2 - +a - 3, + 2 + 3 + +a, + 2 + 3 - +a, + 2 - 3 + +a, + 2 - 3 - +a + ); + } + expect: { + var a = true; + console.log( + +a + 2 + 3, + +a + 2 - 3, + a - 2 + 3, + a - 2 - 3, + +a + 2 + 3, + +a + 2 - 3, + 2 - a + 3, + 2 - a - 3, + +a + 5, + 5 - a, + +a - 1, + -1 - a + ); + } + expect_stdout: "6 0 2 -4 6 0 4 -2 6 4 0 -2" +} + +evaluate_5_unsafe_math: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + var a = true; + console.log( + +a + 2 + 3, + +a + 2 - 3, + +a - 2 + 3, + +a - 2 - 3, + 2 + +a + 3, + 2 + +a - 3, + 2 - +a + 3, + 2 - +a - 3, + 2 + 3 + +a, + 2 + 3 - +a, + 2 - 3 + +a, + 2 - 3 - +a + ); + } + expect: { + var a = true; + console.log( + +a + 5, + +a + -1, + a - -1, + a - 5, + +a + 5, + +a + -1, + 5 - a, + -1 - a, + +a + 5, + 5 - a, + +a - 1, + -1 - a + ); + } + expect_stdout: "6 0 2 -4 6 0 4 -2 6 4 0 -2" +} + +evaluate_6: { + options = { + evaluate: true, + unsafe_math: false, + } + input: { + var a = true; + console.log( + -a + 2 + 3, + -a + 2 - 3, + -a - 2 + 3, + -a - 2 - 3, + 2 + -a + 3, + 2 + -a - 3, + 2 - -a + 3, + 2 - -a - 3, + 2 + 3 + -a, + 2 + 3 - -a, + 2 - 3 + -a, + 2 - 3 - -a + ); + } + expect: { + var a = true; + console.log( + 2 - a + 3, + 2 - a - 3, + -a - 2 + 3, + -a - 2 - 3, + 2 - a + 3, + 2 - a - 3, + 2 + a + 3, + 2 + a - 3, + 5 - a, + 5 + a, + -1 - a, + -1 + a + ); + } + expect_stdout: "4 -2 0 -6 4 -2 6 0 4 6 -2 0" +} + +evaluate_6_unsafe_math: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + var a = true; + console.log( + -a + 2 + 3, + -a + 2 - 3, + -a - 2 + 3, + -a - 2 - 3, + 2 + -a + 3, + 2 + -a - 3, + 2 - -a + 3, + 2 - -a - 3, + 2 + 3 + -a, + 2 + 3 - -a, + 2 - 3 + -a, + 2 - 3 - -a + ); + } + expect: { + var a = true; + console.log( + 5 - a, + -1 - a, + -a - -1, + -a - 5, + 5 - a, + -1 - a, + 2 + a + 3, + -1 + a, + 5 - a, + 5 + a, + -1 - a, + -1 + a + ); + } + expect_stdout: "4 -2 0 -6 4 -2 6 0 4 6 -2 0" +} + +evaluate_7: { + options = { + evaluate: true, + unsafe_math: false, + } + input: { + var x = "42", y; + console.log( + +x + 2 + (3 + !y), + +x + 2 + (3 - !y), + +x + 2 - (3 + !y), + +x + 2 - (3 - !y), + +x - 2 + (3 + !y), + +x - 2 + (3 - !y), + +x - 2 - (3 + !y), + +x - 2 - (3 - !y) + ); + } + expect: { + var x = "42", y; + console.log( + +x + 2 + (3 + !y), + +x + 2 + (3 - !y), + +x + 2 - (3 + !y), + +x + 2 - (3 - !y), + x - 2 + (3 + !y), + x - 2 + (3 - !y), + x - 2 - (3 + !y), + x - 2 - (3 - !y) + ); + } + expect_stdout: "48 46 40 42 44 42 36 38" +} + +evaluate_7_unsafe_math: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + var x = "42", y; + console.log( + +x + 2 + (3 + !y), + +x + 2 + (3 - !y), + +x + 2 - (3 + !y), + +x + 2 - (3 - !y), + +x - 2 + (3 + !y), + +x - 2 + (3 - !y), + +x - 2 - (3 + !y), + +x - 2 - (3 - !y) + ); + } + expect: { + var x = "42", y; + console.log( + +x + 5 + !y, + +x + 5 - !y, + +x + -1 - !y, + +x + -1 + !y, + x - -1 + !y, + x - -1 - !y, + x - 5 - !y, + x - 5 + !y + ); + } + expect_stdout: "48 46 40 42 44 42 36 38" +} + +NaN_redefined: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + var NaN; + console.log(1 / (0 / 0)); + } + expect: { + var NaN; + console.log(0 / 0); + } + expect_stdout: "NaN" +} + issue_1710: { options = { evaluate: true, -- 2.34.1