function drop_returns(compressor, exp) {
var arrow = is_arrow(exp);
var async = is_async(exp);
+ var changed = false;
var drop_body = false;
if (arrow && compressor.option("arrows")) {
if (!exp.value) {
drop_body = true;
} else if (!async || is_primitive(compressor, exp.value)) {
- exp.value = exp.value.drop_side_effect_free(compressor);
+ var dropped = exp.value.drop_side_effect_free(compressor);
+ if (dropped !== exp.value) {
+ changed = true;
+ exp.value = dropped;
+ }
}
} else if (exp instanceof AST_AsyncFunction || exp instanceof AST_Function) {
if (exp.name) {
if (async && !is_primitive(compressor, value)) return node;
value = value.drop_side_effect_free(compressor, true);
}
+ changed = true;
if (!value) return make_node(AST_EmptyStatement, node);
return make_node(AST_SimpleStatement, node, { body: value });
});
var value = node.value;
if (value) {
if (async && !is_primitive(compressor, value)) return;
- node.value = value.drop_side_effect_free(compressor);
+ var dropped = value.drop_side_effect_free(compressor);
+ if (dropped !== value) {
+ changed = true;
+ node.value = dropped;
+ }
}
});
}
var body = node.body;
if (body instanceof AST_Await) {
if (is_primitive(compressor, body.expression)) {
+ changed = true;
body = body.expression.drop_side_effect_free(compressor, true);
if (!body) return make_node(AST_EmptyStatement, node);
node.body = body;
for (var i = exprs.length; --i >= 0;) {
var tail = exprs[i];
if (!(tail instanceof AST_Await)) break;
- if (!is_primitive(compressor, tail.expression)) break;
- if (exprs[i] = tail.expression.drop_side_effect_free(compressor)) break;
+ var value = tail.expression;
+ if (!is_primitive(compressor, value)) break;
+ changed = true;
+ if (exprs[i] = value.drop_side_effect_free(compressor)) break;
}
switch (i) {
case -1:
return make_node(ctor, exp, exp);
}
}
- return drop_body && exp.clone();
+ return changed && exp.clone();
}
def(AST_Call, function(compressor, first_in_statement) {
var self = this;
OPT(AST_UnaryPrefix, function(self, compressor) {
var op = self.operator;
var exp = self.expression;
- if (compressor.option("evaluate") && op == "delete" && !may_not_delete(exp)) {
- return make_sequence(self, [ exp, make_node(AST_True, self) ]).optimize(compressor);
- }
if (compressor.option("sequences") && can_lift()) {
var seq = lift_sequence_in_expression(self, compressor);
if (seq !== self) return seq.optimize(compressor);
}
- if (compressor.option("side_effects") && op == "void") {
- exp = exp.drop_side_effect_free(compressor);
- if (!exp) return make_node(AST_Undefined, self).optimize(compressor);
- self.expression = exp;
- return self;
- }
- if (compressor.option("booleans")) {
- if (op == "!" && exp.is_truthy()) {
- return make_sequence(self, [ exp, make_node(AST_False, self) ]).optimize(compressor);
- } else if (compressor.in_boolean_context()) switch (op) {
- case "!":
- if (exp instanceof AST_UnaryPrefix && exp.operator == "!") {
- // !!foo ---> foo, if we're in boolean context
- return exp.expression;
- }
+ switch (op) {
+ case "-":
+ if (exp instanceof AST_Infinity) exp = exp.transform(compressor);
+ // avoids infinite recursion of numerals
+ if (exp instanceof AST_Number || exp instanceof AST_Infinity) return self;
+ break;
+ case "!":
+ if (!compressor.option("booleans")) break;
+ if (exp.is_truthy()) return make_sequence(self, [ exp, make_node(AST_False, self) ]).optimize(compressor);
+ if (compressor.in_boolean_context()) {
+ // !!foo ---> foo, if we're in boolean context
+ if (exp instanceof AST_UnaryPrefix && exp.operator == "!") return exp.expression;
if (exp instanceof AST_Binary) {
self = best_of(compressor, self, exp.negate(compressor, first_in_statement(compressor)));
}
- break;
- case "typeof":
- // typeof always returns a non-empty string, thus it's
- // always true in booleans
- AST_Node.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
- var exprs = [ make_node(AST_True, self) ];
- if (!(exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor))) exprs.unshift(exp);
- return make_sequence(self, exprs).optimize(compressor);
}
+ break;
+ case "delete":
+ if (!compressor.option("evaluate")) break;
+ if (may_not_delete(exp)) break;
+ return make_sequence(self, [ exp, make_node(AST_True, self) ]).optimize(compressor);
+ case "typeof":
+ if (!compressor.option("booleans")) break;
+ if (!compressor.in_boolean_context()) break;
+ // typeof always returns a non-empty string, thus always truthy
+ AST_Node.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
+ var exprs = [ make_node(AST_True, self) ];
+ if (!(exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor))) exprs.unshift(exp);
+ return make_sequence(self, exprs).optimize(compressor);
+ case "void":
+ if (!compressor.option("side_effects")) break;
+ exp = exp.drop_side_effect_free(compressor);
+ if (!exp) return make_node(AST_Undefined, self).optimize(compressor);
+ self.expression = exp;
+ return self;
}
- if (op == "-" && exp instanceof AST_Infinity) exp = exp.transform(compressor);
if (compressor.option("evaluate")
&& exp instanceof AST_Binary
&& SIGN_OPS[op]
operator: exp.operator,
left: make_node(AST_UnaryPrefix, exp.left, {
operator: op,
- expression: exp.left
+ expression: exp.left,
}),
- right: exp.right
+ right: exp.right,
});
}
- // avoids infinite recursion of numerals
- return op == "-" && (exp instanceof AST_Number || exp instanceof AST_Infinity)
- ? self : try_evaluate(compressor, self);
+ return try_evaluate(compressor, self);
function may_not_delete(node) {
return node instanceof AST_Infinity
}
OPT(AST_Binary, function(self, compressor) {
- function reversible() {
- return self.left.is_constant()
- || self.right.is_constant()
- || !self.left.has_side_effects(compressor)
- && !self.right.has_side_effects(compressor);
- }
- function reverse(op) {
- if (reversible()) {
- if (op) self.operator = op;
- var tmp = self.left;
- self.left = self.right;
- self.right = tmp;
- }
- }
- function swap_chain() {
- var rhs = self.right;
- self.left = make_node(AST_Binary, self, {
- operator: self.operator,
- left: self.left,
- right: rhs.left,
- start: self.left.start,
- end: rhs.left.end
- });
- self.right = rhs.right;
- self.left = self.left.transform(compressor);
- }
if (commutativeOperators[self.operator]
&& self.right.is_constant()
&& !self.left.is_constant()
&& self.left.expression instanceof AST_Number && self.left.expression.value == 1;
}
}
+
+ function reversible() {
+ return self.left.is_constant()
+ || self.right.is_constant()
+ || !self.left.has_side_effects(compressor)
+ && !self.right.has_side_effects(compressor);
+ }
+
+ function reverse(op) {
+ if (reversible()) {
+ if (op) self.operator = op;
+ var tmp = self.left;
+ self.left = self.right;
+ self.right = tmp;
+ }
+ }
+
+ function swap_chain() {
+ var rhs = self.right;
+ self.left = make_node(AST_Binary, self, {
+ operator: self.operator,
+ left: self.left,
+ right: rhs.left,
+ start: self.left.start,
+ end: rhs.left.end
+ });
+ self.right = rhs.right;
+ self.left = self.left.transform(compressor);
+ }
});
OPT(AST_SymbolExport, function(self) {
if (lhs && is_atomic(lhs, self)) return self;
return make_node(AST_UnaryPrefix, self, {
operator: "void",
- expression: make_node(AST_Number, self, {
- value: 0
- })
+ expression: make_node(AST_Number, self, { value: 0 }),
});
});
}
return make_node(AST_Binary, self, {
operator: "/",
- left: make_node(AST_Number, self, {
- value: 1
- }),
- right: make_node(AST_Number, self, {
- value: 0
- })
+ left: make_node(AST_Number, self, { value: 1 }),
+ right: make_node(AST_Number, self, { value: 0 }),
});
});
if (!lhs && !find_scope(compressor).find_variable("NaN")) return self;
return make_node(AST_Binary, self, {
operator: "/",
- left: make_node(AST_Number, self, {
- value: 0
- }),
- right: make_node(AST_Number, self, {
- value: 0
- })
+ left: make_node(AST_Number, self, { value: 0 }),
+ right: make_node(AST_Number, self, { value: 0 }),
});
});
OPT(AST_Boolean, function(self, compressor) {
if (!compressor.option("booleans")) return self;
- if (compressor.in_boolean_context()) return make_node(AST_Number, self, {
- value: +self.value
- });
+ if (compressor.in_boolean_context()) return make_node(AST_Number, self, { value: +self.value });
var p = compressor.parent();
if (p instanceof AST_Binary && (p.operator == "==" || p.operator == "!=")) {
AST_Node.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
line : p.start.line,
col : p.start.col,
});
- return make_node(AST_Number, self, {
- value: +self.value
- });
+ return make_node(AST_Number, self, { value: +self.value });
}
return make_node(AST_UnaryPrefix, self, {
operator: "!",
- expression: make_node(AST_Number, self, {
- value: 1 - self.value
- })
+ expression: make_node(AST_Number, self, { value: 1 - self.value }),
});
});