&& !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) {
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, {
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) {
}));
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);
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 = [];
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
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);
}
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);
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) {
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) {
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;