improve truthy compression (#3009)
This commit is contained in:
@@ -2041,6 +2041,28 @@ merge(Compressor.prototype, {
|
||||
&& !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()
|
||||
// returns true if this node may be null, undefined or contain `AST_Accessor`
|
||||
(function(def) {
|
||||
@@ -3821,7 +3843,7 @@ merge(Compressor.prototype, {
|
||||
|
||||
OPT(AST_Do, function(self, compressor){
|
||||
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) return make_node(AST_For, self, {
|
||||
body: make_node(AST_BlockStatement, self.body, {
|
||||
@@ -3943,9 +3965,11 @@ merge(Compressor.prototype, {
|
||||
self.condition = best_of_expression(self.condition.transform(compressor), orig);
|
||||
}
|
||||
}
|
||||
if (compressor.option("dead_code")) {
|
||||
if (cond instanceof AST_Node) cond = self.condition.tail_node().evaluate(compressor);
|
||||
if (!cond) {
|
||||
if (cond instanceof AST_Node) {
|
||||
cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
|
||||
}
|
||||
if (!cond) {
|
||||
if (compressor.option("dead_code")) {
|
||||
var body = [];
|
||||
extract_declarations_from_unreachable_code(compressor, self.body, body);
|
||||
if (self.init instanceof AST_Statement) {
|
||||
@@ -3960,6 +3984,16 @@ merge(Compressor.prototype, {
|
||||
}));
|
||||
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);
|
||||
@@ -3980,7 +4014,9 @@ merge(Compressor.prototype, {
|
||||
self.condition = best_of_expression(self.condition.transform(compressor), orig);
|
||||
}
|
||||
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) {
|
||||
compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
|
||||
var body = [];
|
||||
@@ -4864,8 +4900,10 @@ merge(Compressor.prototype, {
|
||||
return make_node(AST_Undefined, self).optimize(compressor);
|
||||
}
|
||||
}
|
||||
if (compressor.in_boolean_context()) {
|
||||
switch (self.operator) {
|
||||
if (compressor.option("booleans")) {
|
||||
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 "!":
|
||||
if (e instanceof AST_UnaryPrefix && e.operator == "!") {
|
||||
// !!foo ==> foo, if we're in boolean context
|
||||
@@ -5106,7 +5144,7 @@ merge(Compressor.prototype, {
|
||||
if (compressor.option("evaluate")) {
|
||||
switch (self.operator) {
|
||||
case "&&":
|
||||
var ll = self.left.truthy ? true : self.left.falsy ? false : self.left.evaluate(compressor);
|
||||
var ll = fuzzy_eval(self.left);
|
||||
if (!ll) {
|
||||
compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
|
||||
return maintain_this_binding(compressor.parent(), compressor.self(), self.left).optimize(compressor);
|
||||
@@ -5141,7 +5179,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
break;
|
||||
case "||":
|
||||
var ll = self.left.truthy ? true : self.left.falsy ? false : self.left.evaluate(compressor);
|
||||
var ll = fuzzy_eval(self.left);
|
||||
if (!ll) {
|
||||
compressor.warn("Condition left of || always false [{file}:{line},{col}]", self.start);
|
||||
return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
|
||||
@@ -5379,6 +5417,13 @@ merge(Compressor.prototype, {
|
||||
return best_of(compressor, ev, 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) {
|
||||
@@ -5674,15 +5719,13 @@ merge(Compressor.prototype, {
|
||||
expressions.push(self);
|
||||
return make_sequence(self, expressions);
|
||||
}
|
||||
var cond = self.condition.evaluate(compressor);
|
||||
if (cond !== self.condition) {
|
||||
if (cond) {
|
||||
compressor.warn("Condition always true [{file}:{line},{col}]", self.start);
|
||||
return maintain_this_binding(compressor.parent(), compressor.self(), self.consequent);
|
||||
} else {
|
||||
compressor.warn("Condition always false [{file}:{line},{col}]", self.start);
|
||||
return maintain_this_binding(compressor.parent(), compressor.self(), self.alternative);
|
||||
}
|
||||
var cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
|
||||
if (!cond) {
|
||||
compressor.warn("Condition always false [{file}:{line},{col}]", self.start);
|
||||
return make_sequence(self, [ self.condition, self.alternative ]).optimize(compressor);
|
||||
} else if (!(cond instanceof AST_Node)) {
|
||||
compressor.warn("Condition always true [{file}:{line},{col}]", self.start);
|
||||
return make_sequence(self, [ self.condition, self.consequent ]).optimize(compressor);
|
||||
}
|
||||
var negated = cond.negate(compressor, first_in_statement(compressor));
|
||||
if (best_of(compressor, cond, negated) === negated) {
|
||||
@@ -6122,19 +6165,6 @@ merge(Compressor.prototype, {
|
||||
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){
|
||||
if (self.value && is_undefined(self.value, compressor)) {
|
||||
self.value = null;
|
||||
|
||||
Reference in New Issue
Block a user