improve truthy compression (#3009)
This commit is contained in:
@@ -2041,6 +2041,28 @@ merge(Compressor.prototype, {
|
|||||||
&& !node.expression.has_side_effects(compressor);
|
&& !node.expression.has_side_effects(compressor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// is_truthy()
|
||||||
|
// return true if `!!node === true`
|
||||||
|
(function(def) {
|
||||||
|
def(AST_Node, return_false);
|
||||||
|
def(AST_Array, return_true);
|
||||||
|
def(AST_Assign, function() {
|
||||||
|
return this.operator == "=" && this.right.is_truthy();
|
||||||
|
});
|
||||||
|
def(AST_Lambda, return_true);
|
||||||
|
def(AST_Object, return_true);
|
||||||
|
def(AST_RegExp, return_true);
|
||||||
|
def(AST_Sequence, function() {
|
||||||
|
return this.tail_node().is_truthy();
|
||||||
|
});
|
||||||
|
def(AST_SymbolRef, function() {
|
||||||
|
var fixed = this.fixed_value();
|
||||||
|
return fixed && fixed.is_truthy();
|
||||||
|
});
|
||||||
|
})(function(node, func) {
|
||||||
|
node.DEFMETHOD("is_truthy", func);
|
||||||
|
});
|
||||||
|
|
||||||
// may_throw_on_access()
|
// may_throw_on_access()
|
||||||
// returns true if this node may be null, undefined or contain `AST_Accessor`
|
// returns true if this node may be null, undefined or contain `AST_Accessor`
|
||||||
(function(def) {
|
(function(def) {
|
||||||
@@ -3821,7 +3843,7 @@ merge(Compressor.prototype, {
|
|||||||
|
|
||||||
OPT(AST_Do, function(self, compressor){
|
OPT(AST_Do, function(self, compressor){
|
||||||
if (!compressor.option("loops")) return self;
|
if (!compressor.option("loops")) return self;
|
||||||
var cond = self.condition.tail_node().evaluate(compressor);
|
var cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
|
||||||
if (!(cond instanceof AST_Node)) {
|
if (!(cond instanceof AST_Node)) {
|
||||||
if (cond) return make_node(AST_For, self, {
|
if (cond) return make_node(AST_For, self, {
|
||||||
body: make_node(AST_BlockStatement, self.body, {
|
body: make_node(AST_BlockStatement, self.body, {
|
||||||
@@ -3943,9 +3965,11 @@ merge(Compressor.prototype, {
|
|||||||
self.condition = best_of_expression(self.condition.transform(compressor), orig);
|
self.condition = best_of_expression(self.condition.transform(compressor), orig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (compressor.option("dead_code")) {
|
if (cond instanceof AST_Node) {
|
||||||
if (cond instanceof AST_Node) cond = self.condition.tail_node().evaluate(compressor);
|
cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
|
||||||
if (!cond) {
|
}
|
||||||
|
if (!cond) {
|
||||||
|
if (compressor.option("dead_code")) {
|
||||||
var body = [];
|
var body = [];
|
||||||
extract_declarations_from_unreachable_code(compressor, self.body, body);
|
extract_declarations_from_unreachable_code(compressor, self.body, body);
|
||||||
if (self.init instanceof AST_Statement) {
|
if (self.init instanceof AST_Statement) {
|
||||||
@@ -3960,6 +3984,16 @@ merge(Compressor.prototype, {
|
|||||||
}));
|
}));
|
||||||
return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
|
return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
|
||||||
}
|
}
|
||||||
|
} else if (self.condition && !(cond instanceof AST_Node)) {
|
||||||
|
self.body = make_node(AST_BlockStatement, self.body, {
|
||||||
|
body: [
|
||||||
|
make_node(AST_SimpleStatement, self.condition, {
|
||||||
|
body: self.condition
|
||||||
|
}),
|
||||||
|
self.body
|
||||||
|
]
|
||||||
|
});
|
||||||
|
self.condition = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return if_break_in_loop(self, compressor);
|
return if_break_in_loop(self, compressor);
|
||||||
@@ -3980,7 +4014,9 @@ merge(Compressor.prototype, {
|
|||||||
self.condition = best_of_expression(self.condition.transform(compressor), orig);
|
self.condition = best_of_expression(self.condition.transform(compressor), orig);
|
||||||
}
|
}
|
||||||
if (compressor.option("dead_code")) {
|
if (compressor.option("dead_code")) {
|
||||||
if (cond instanceof AST_Node) cond = self.condition.tail_node().evaluate(compressor);
|
if (cond instanceof AST_Node) {
|
||||||
|
cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
|
||||||
|
}
|
||||||
if (!cond) {
|
if (!cond) {
|
||||||
compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
|
compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
|
||||||
var body = [];
|
var body = [];
|
||||||
@@ -4864,8 +4900,10 @@ merge(Compressor.prototype, {
|
|||||||
return make_node(AST_Undefined, self).optimize(compressor);
|
return make_node(AST_Undefined, self).optimize(compressor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (compressor.in_boolean_context()) {
|
if (compressor.option("booleans")) {
|
||||||
switch (self.operator) {
|
if (self.operator == "!" && e.is_truthy()) {
|
||||||
|
return make_sequence(self, [ e, make_node(AST_False, self) ]).optimize(compressor);
|
||||||
|
} else if (compressor.in_boolean_context()) switch (self.operator) {
|
||||||
case "!":
|
case "!":
|
||||||
if (e instanceof AST_UnaryPrefix && e.operator == "!") {
|
if (e instanceof AST_UnaryPrefix && e.operator == "!") {
|
||||||
// !!foo ==> foo, if we're in boolean context
|
// !!foo ==> foo, if we're in boolean context
|
||||||
@@ -5106,7 +5144,7 @@ merge(Compressor.prototype, {
|
|||||||
if (compressor.option("evaluate")) {
|
if (compressor.option("evaluate")) {
|
||||||
switch (self.operator) {
|
switch (self.operator) {
|
||||||
case "&&":
|
case "&&":
|
||||||
var ll = self.left.truthy ? true : self.left.falsy ? false : self.left.evaluate(compressor);
|
var ll = fuzzy_eval(self.left);
|
||||||
if (!ll) {
|
if (!ll) {
|
||||||
compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
|
compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
|
||||||
return maintain_this_binding(compressor.parent(), compressor.self(), self.left).optimize(compressor);
|
return maintain_this_binding(compressor.parent(), compressor.self(), self.left).optimize(compressor);
|
||||||
@@ -5141,7 +5179,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "||":
|
case "||":
|
||||||
var ll = self.left.truthy ? true : self.left.falsy ? false : self.left.evaluate(compressor);
|
var ll = fuzzy_eval(self.left);
|
||||||
if (!ll) {
|
if (!ll) {
|
||||||
compressor.warn("Condition left of || always false [{file}:{line},{col}]", self.start);
|
compressor.warn("Condition left of || always false [{file}:{line},{col}]", self.start);
|
||||||
return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
|
return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
|
||||||
@@ -5379,6 +5417,13 @@ merge(Compressor.prototype, {
|
|||||||
return best_of(compressor, ev, self);
|
return best_of(compressor, ev, self);
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
|
|
||||||
|
function fuzzy_eval(node) {
|
||||||
|
if (node.truthy) return true;
|
||||||
|
if (node.falsy) return false;
|
||||||
|
if (node.is_truthy()) return true;
|
||||||
|
return node.evaluate(compressor);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function recursive_ref(compressor, def) {
|
function recursive_ref(compressor, def) {
|
||||||
@@ -5674,15 +5719,13 @@ merge(Compressor.prototype, {
|
|||||||
expressions.push(self);
|
expressions.push(self);
|
||||||
return make_sequence(self, expressions);
|
return make_sequence(self, expressions);
|
||||||
}
|
}
|
||||||
var cond = self.condition.evaluate(compressor);
|
var cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
|
||||||
if (cond !== self.condition) {
|
if (!cond) {
|
||||||
if (cond) {
|
compressor.warn("Condition always false [{file}:{line},{col}]", self.start);
|
||||||
compressor.warn("Condition always true [{file}:{line},{col}]", self.start);
|
return make_sequence(self, [ self.condition, self.alternative ]).optimize(compressor);
|
||||||
return maintain_this_binding(compressor.parent(), compressor.self(), self.consequent);
|
} else if (!(cond instanceof AST_Node)) {
|
||||||
} else {
|
compressor.warn("Condition always true [{file}:{line},{col}]", self.start);
|
||||||
compressor.warn("Condition always false [{file}:{line},{col}]", self.start);
|
return make_sequence(self, [ self.condition, self.consequent ]).optimize(compressor);
|
||||||
return maintain_this_binding(compressor.parent(), compressor.self(), self.alternative);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
var negated = cond.negate(compressor, first_in_statement(compressor));
|
var negated = cond.negate(compressor, first_in_statement(compressor));
|
||||||
if (best_of(compressor, cond, negated) === negated) {
|
if (best_of(compressor, cond, negated) === negated) {
|
||||||
@@ -6122,19 +6165,6 @@ merge(Compressor.prototype, {
|
|||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
|
|
||||||
function literals_in_boolean_context(self, compressor) {
|
|
||||||
if (compressor.in_boolean_context()) {
|
|
||||||
return best_of(compressor, self, make_sequence(self, [
|
|
||||||
self,
|
|
||||||
make_node(AST_True, self)
|
|
||||||
]).optimize(compressor));
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
};
|
|
||||||
OPT(AST_Array, literals_in_boolean_context);
|
|
||||||
OPT(AST_Object, literals_in_boolean_context);
|
|
||||||
OPT(AST_RegExp, literals_in_boolean_context);
|
|
||||||
|
|
||||||
OPT(AST_Return, function(self, compressor){
|
OPT(AST_Return, function(self, compressor){
|
||||||
if (self.value && is_undefined(self.value, compressor)) {
|
if (self.value && is_undefined(self.value, compressor)) {
|
||||||
self.value = null;
|
self.value = null;
|
||||||
|
|||||||
@@ -703,10 +703,11 @@ ternary_boolean_alternative: {
|
|||||||
|
|
||||||
trivial_boolean_ternary_expressions : {
|
trivial_boolean_ternary_expressions : {
|
||||||
options = {
|
options = {
|
||||||
|
booleans: true,
|
||||||
conditionals: true,
|
conditionals: true,
|
||||||
evaluate : true,
|
evaluate: true,
|
||||||
booleans : true
|
side_effects: true,
|
||||||
};
|
}
|
||||||
input: {
|
input: {
|
||||||
f('foo' in m ? true : false);
|
f('foo' in m ? true : false);
|
||||||
f('foo' in m ? false : true);
|
f('foo' in m ? false : true);
|
||||||
|
|||||||
@@ -745,7 +745,7 @@ in_boolean_context: {
|
|||||||
!b("foo"),
|
!b("foo"),
|
||||||
!b([1, 2]),
|
!b([1, 2]),
|
||||||
!b(/foo/),
|
!b(/foo/),
|
||||||
![1, foo()],
|
(foo(), !1),
|
||||||
(foo(), !1)
|
(foo(), !1)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1566,3 +1566,43 @@ issue_2968: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "PASS"
|
expect_stdout: "PASS"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
truthy_conditionals: {
|
||||||
|
options = {
|
||||||
|
conditionals: true,
|
||||||
|
evaluate: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
if (a = {}) x();
|
||||||
|
(b = /foo/) && y();
|
||||||
|
(c = function() {}) || z();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
a = {}, x();
|
||||||
|
b = /foo/, y();
|
||||||
|
c = function() {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
truthy_loops: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
loops: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
while ([]) x();
|
||||||
|
do {
|
||||||
|
y();
|
||||||
|
} while(a = {});
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (;;) {
|
||||||
|
[];
|
||||||
|
x();
|
||||||
|
}
|
||||||
|
for (;;) {
|
||||||
|
y();
|
||||||
|
a = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -175,8 +175,8 @@ should_warn: {
|
|||||||
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:141,31]",
|
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:141,31]",
|
||||||
"WARN: Condition always true [test/compress/issue-1261.js:141,8]",
|
"WARN: Condition always true [test/compress/issue-1261.js:141,8]",
|
||||||
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:142,23]",
|
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:142,23]",
|
||||||
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:143,24]",
|
|
||||||
"WARN: Condition always true [test/compress/issue-1261.js:143,8]",
|
"WARN: Condition always true [test/compress/issue-1261.js:143,8]",
|
||||||
|
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:143,24]",
|
||||||
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:144,31]",
|
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:144,31]",
|
||||||
"WARN: Condition always false [test/compress/issue-1261.js:144,8]",
|
"WARN: Condition always false [test/compress/issue-1261.js:144,8]",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ negate_iife_3_evaluate: {
|
|||||||
(function(){ return true })() ? console.log(true) : console.log(false);
|
(function(){ return true })() ? console.log(true) : console.log(false);
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
console.log(true);
|
true, console.log(true);
|
||||||
}
|
}
|
||||||
expect_stdout: true
|
expect_stdout: true
|
||||||
}
|
}
|
||||||
@@ -110,7 +110,7 @@ negate_iife_3_off_evaluate: {
|
|||||||
(function(){ return true })() ? console.log(true) : console.log(false);
|
(function(){ return true })() ? console.log(true) : console.log(false);
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
console.log(true);
|
true, console.log(true);
|
||||||
}
|
}
|
||||||
expect_stdout: true
|
expect_stdout: true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ reduce_vars: {
|
|||||||
console.log(a - 5);
|
console.log(a - 5);
|
||||||
eval("console.log(a);");
|
eval("console.log(a);");
|
||||||
})(eval);
|
})(eval);
|
||||||
"yes";
|
true, "yes";
|
||||||
console.log(A + 1);
|
console.log(A + 1);
|
||||||
}
|
}
|
||||||
expect_stdout: true
|
expect_stdout: true
|
||||||
@@ -147,7 +147,7 @@ modified: {
|
|||||||
}
|
}
|
||||||
function f4() {
|
function f4() {
|
||||||
var b = 2, c = 3;
|
var b = 2, c = 3;
|
||||||
b = c;
|
1, b = c;
|
||||||
console.log(1 + b);
|
console.log(1 + b);
|
||||||
console.log(b + c);
|
console.log(b + c);
|
||||||
console.log(1 + c);
|
console.log(1 + c);
|
||||||
@@ -715,10 +715,12 @@ passes: {
|
|||||||
passes: 2,
|
passes: 2,
|
||||||
reduce_funcs: true,
|
reduce_funcs: true,
|
||||||
reduce_vars: true,
|
reduce_vars: true,
|
||||||
|
sequences: true,
|
||||||
|
side_effects: true,
|
||||||
unused: true,
|
unused: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
function f() {
|
(function() {
|
||||||
var a = 1, b = 2, c = 3;
|
var a = 1, b = 2, c = 3;
|
||||||
if (a) {
|
if (a) {
|
||||||
b = c;
|
b = c;
|
||||||
@@ -729,17 +731,22 @@ passes: {
|
|||||||
console.log(b + c);
|
console.log(b + c);
|
||||||
console.log(a + c);
|
console.log(a + c);
|
||||||
console.log(a + b + c);
|
console.log(a + b + c);
|
||||||
}
|
})();
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
function f() {
|
(function() {
|
||||||
3;
|
console.log(4),
|
||||||
console.log(4);
|
console.log(6),
|
||||||
console.log(6);
|
console.log(4),
|
||||||
console.log(4);
|
|
||||||
console.log(7);
|
console.log(7);
|
||||||
}
|
})();
|
||||||
}
|
}
|
||||||
|
expect_stdout: [
|
||||||
|
"4",
|
||||||
|
"6",
|
||||||
|
"4",
|
||||||
|
"7",
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
iife: {
|
iife: {
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ if_else_empty: {
|
|||||||
if ({} ? a : b); else {}
|
if ({} ? a : b); else {}
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
!{} ? b : a;
|
({}), a;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user