compress arithmetic expressions further (#3529)

This commit is contained in:
Alex Lam S.L
2019-10-27 03:07:07 +08:00
committed by GitHub
parent 85237b08d4
commit 50a578c1f6
3 changed files with 433 additions and 76 deletions

View File

@@ -2437,9 +2437,10 @@ merge(Compressor.prototype, {
|| this.operator == "=" && this.right.is_number(compressor); || this.operator == "=" && this.right.is_number(compressor);
}); });
def(AST_Binary, function(compressor) { def(AST_Binary, function(compressor) {
return binary[this.operator] || this.operator == "+" if (binary[this.operator]) return true;
&& this.left.is_number(compressor) if (this.operator != "+") return false;
&& this.right.is_number(compressor); return (this.left.is_boolean(compressor) || this.left.is_number(compressor))
&& (this.right.is_boolean(compressor) || this.right.is_number(compressor));
}); });
var fn = makePredicate([ var fn = makePredicate([
"charCodeAt", "charCodeAt",
@@ -5777,6 +5778,7 @@ merge(Compressor.prototype, {
} }
} }
if (compressor.option("evaluate")) { if (compressor.option("evaluate")) {
var associative = true;
switch (self.operator) { switch (self.operator) {
case "&&": case "&&":
var ll = fuzzy_eval(self.left); var ll = fuzzy_eval(self.left);
@@ -5845,9 +5847,6 @@ merge(Compressor.prototype, {
}).optimize(compressor); }).optimize(compressor);
} }
break; break;
}
var associative = true;
switch (self.operator) {
case "+": case "+":
// "foo" + ("bar" + x) => "foobar" + x // "foo" + ("bar" + x) => "foobar" + x
if (self.left instanceof AST_Constant if (self.left instanceof AST_Constant
@@ -5927,13 +5926,38 @@ merge(Compressor.prototype, {
}); });
break; 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 "*":
case "/":
associative = compressor.option("unsafe_math"); 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 "|": case "|":
case "^": case "^":
// a + +b => +b + a // 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) && self.right.is_number(compressor)
&& reversible() && reversible()
&& !(self.left instanceof AST_Binary && !(self.left instanceof AST_Binary
@@ -5951,12 +5975,13 @@ merge(Compressor.prototype, {
self = best_of(compressor, self, reversed); self = best_of(compressor, self, reversed);
} }
} }
if (associative && self.is_number(compressor)) { if (!associative || !self.is_number(compressor)) break;
// a + (b + c) => (a + b) + c // a + (b + c) => (a + b) + c
if (self.right instanceof AST_Binary if (self.right instanceof AST_Binary
&& self.right.operator == self.operator) { && self.right.operator != "%"
&& PRECEDENCE[self.right.operator] == PRECEDENCE[self.operator]) {
self = make_node(AST_Binary, self, { self = make_node(AST_Binary, self, {
operator: self.operator, operator: align(self.operator, self.right.operator),
left: make_node(AST_Binary, self.left, { left: make_node(AST_Binary, self.left, {
operator: self.operator, operator: self.operator,
left: self.left, left: self.left,
@@ -5967,14 +5992,15 @@ merge(Compressor.prototype, {
right: self.right.right right: self.right.right
}); });
} }
// (n + 2) + 3 => 5 + n // (2 * n) * 3 => 6 * n
// (2 * n) * 3 => 6 + n // (n + 2) + 3 => n + 5
if (self.right instanceof AST_Constant if (self.right instanceof AST_Constant
&& self.left instanceof AST_Binary && self.left instanceof AST_Binary
&& self.left.operator == self.operator) { && self.left.operator != "%"
&& PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]) {
if (self.left.left instanceof AST_Constant) { if (self.left.left instanceof AST_Constant) {
self = make_node(AST_Binary, self, { self = make_node(AST_Binary, self, {
operator: self.operator, operator: self.left.operator,
left: make_node(AST_Binary, self.left, { left: make_node(AST_Binary, self.left, {
operator: self.operator, operator: self.operator,
left: self.left.left, left: self.left.left,
@@ -5986,42 +6012,19 @@ merge(Compressor.prototype, {
}); });
} else if (self.left.right instanceof AST_Constant) { } else if (self.left.right instanceof AST_Constant) {
self = make_node(AST_Binary, self, { self = make_node(AST_Binary, self, {
operator: self.operator, operator: self.left.operator,
left: make_node(AST_Binary, self.left, { left: self.left.left,
operator: self.operator, right: make_node(AST_Binary, self.left, {
operator: align(self.left.operator, self.operator),
left: self.left.right, left: self.left.right,
right: self.right, right: self.right,
start: self.left.right.start, start: self.left.right.start,
end: self.right.end 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) {
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
}); });
} }
} }
break;
} }
} }
if (compressor.option("unsafe")) { if (compressor.option("unsafe")) {
@@ -6086,6 +6089,17 @@ merge(Compressor.prototype, {
} }
return self; return self;
function align(ref, op) {
switch (ref) {
case "-":
return op == "+" ? "-" : "+";
case "/":
return op == "*" ? "/" : "*";
default:
return op;
}
}
function fuzzy_eval(node) { function fuzzy_eval(node) {
if (node.truthy) return true; if (node.truthy) return true;
if (node.falsy) return false; if (node.falsy) return false;

View File

@@ -96,7 +96,7 @@ asm_mixed: {
return +sum; return +sum;
} }
function geometricMean(start, end) { 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); var exp = stdlib.Math.exp, log = stdlib.Math.log, values = new stdlib.Float64Array(buffer);
return { geometricMean: geometricMean }; return { geometricMean: geometricMean };

View File

@@ -51,17 +51,20 @@ comparisons: {
comparisons: true, comparisons: true,
} }
input: { input: {
var x = "42", y = "0x30";
console.log( console.log(
~x === 42, ~x === 42,
x % n === 42 x % y === 42
); );
} }
expect: { expect: {
var x = "42", y = "0x30";
console.log( console.log(
42 == ~x, 42 == ~x,
x % n == 42 x % y == 42
); );
} }
expect_stdout: "false true"
} }
evaluate_1: { evaluate_1: {
@@ -103,7 +106,7 @@ evaluate_1: {
} }
} }
evaluate_2: { evaluate_1_unsafe_math: {
options = { options = {
evaluate: true, evaluate: true,
unsafe_math: true, unsafe_math: true,
@@ -118,6 +121,8 @@ evaluate_2: {
1 + x-- + 2 + 3, 1 + x-- + 2 + 3,
1 + (x*y + 2) + 3, 1 + (x*y + 2) + 3,
1 + (2 + x + 3), 1 + (2 + x + 3),
1 + (2 + ~x + 3),
-y + (2 + ~x + 3),
1 & (2 & x & 3), 1 & (2 & x & 3),
1 + (2 + (x |= 0) + 3) 1 + (2 + (x |= 0) + 3)
); );
@@ -126,18 +131,104 @@ evaluate_2: {
console.log( console.log(
x + 1 + 2, x + 1 + 2,
2 * x, 2 * x,
3 + +x, +x + 3,
1 + x + 2 + 3, 1 + x + 2 + 3,
3 | x, 3 | x,
6 + x--, 6 + x--,
6 + x*y, x*y + 6,
1 + (2 + x + 3), 1 + (2 + x + 3),
6 + ~x,
5 - y + ~x,
0 & x, 0 & x,
6 + (x |= 0) 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,
+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,
2 * x,
+x + 3,
1 + x + 2 + 3,
3 | x,
6 + x--,
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: { evaluate_3: {
options = { options = {
evaluate: true, evaluate: true,
@@ -148,7 +239,7 @@ evaluate_3: {
console.log(1 + Number(x) + 2); console.log(1 + Number(x) + 2);
} }
expect: { 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: { issue_1710: {
options = { options = {
evaluate: true, evaluate: true,