improve truthy compression (#3009)

This commit is contained in:
Alex Lam S.L
2018-03-16 06:12:59 +08:00
committed by GitHub
parent b29d435bb5
commit 20ca0f5906
7 changed files with 127 additions and 49 deletions

View File

@@ -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;