fix stack issues with AST_Node.evaluate() (#1603)

As patched in #1597, `make_node_from_constant()` makes inconsistent and sometimes incorrect calls to `optimize()` and `transform()`.

Fix those issues properly by changing the semantics of `evaluate()` and `make_node_from_constant()`, with the side effect that `evaluate()` no longer eagerly converts constant to `AST_Node`.
This commit is contained in:
Alex Lam S.L
2017-03-16 01:02:59 +08:00
committed by GitHub
parent 8223b2e0db
commit cf4bf4ceb1

View File

@@ -98,10 +98,10 @@ function Compressor(options, false_by_default) {
this.top_retain = function(def) { this.top_retain = function(def) {
return top_retain.test(def.name); return top_retain.test(def.name);
}; };
} else if (typeof top_retain === "function") { } else if (typeof top_retain == "function") {
this.top_retain = top_retain; this.top_retain = top_retain;
} else if (top_retain) { } else if (top_retain) {
if (typeof top_retain === "string") { if (typeof top_retain == "string") {
top_retain = top_retain.split(/,/); top_retain = top_retain.split(/,/);
} }
this.top_retain = function(def) { this.top_retain = function(def) {
@@ -151,7 +151,17 @@ merge(Compressor.prototype, {
node = node.hoist_declarations(this); node = node.hoist_declarations(this);
was_scope = true; was_scope = true;
} }
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this); descend(node, this);
// Existing code relies on how AST_Node.optimize() worked, and omitting the
// following replacement call would result in degraded efficiency of both
// output and performance.
descend(node, this); descend(node, this);
var opt = node.optimize(this); var opt = node.optimize(this);
if (was_scope && opt instanceof AST_Scope) { if (was_scope && opt instanceof AST_Scope) {
@@ -384,7 +394,7 @@ merge(Compressor.prototype, {
return new ctor(props); return new ctor(props);
}; };
function make_node_from_constant(compressor, val, orig) { function make_node_from_constant(val, orig) {
switch (typeof val) { switch (typeof val) {
case "string": case "string":
return make_node(AST_String, orig, { return make_node(AST_String, orig, {
@@ -404,9 +414,9 @@ merge(Compressor.prototype, {
return make_node(AST_Number, orig, { value: val }); return make_node(AST_Number, orig, { value: val });
case "boolean": case "boolean":
return make_node(val ? AST_True : AST_False, orig).optimize(compressor); return make_node(val ? AST_True : AST_False, orig);
case "undefined": case "undefined":
return make_node(AST_Undefined, orig).transform(compressor); return make_node(AST_Undefined, orig);
default: default:
if (val === null) { if (val === null) {
return make_node(AST_Null, orig, { value: null }); return make_node(AST_Null, orig, { value: null });
@@ -1198,11 +1208,11 @@ merge(Compressor.prototype, {
} }
} }
}); });
function to_node(compressor, value, orig) { function to_node(value, orig) {
if (value instanceof AST_Node) return make_node(value.CTOR, orig, value); if (value instanceof AST_Node) return make_node(value.CTOR, orig, value);
if (Array.isArray(value)) return make_node(AST_Array, orig, { if (Array.isArray(value)) return make_node(AST_Array, orig, {
elements: value.map(function(value) { elements: value.map(function(value) {
return to_node(compressor, value, orig); return to_node(value, orig);
}) })
}); });
if (value && typeof value == "object") { if (value && typeof value == "object") {
@@ -1210,14 +1220,14 @@ merge(Compressor.prototype, {
for (var key in value) { for (var key in value) {
props.push(make_node(AST_ObjectKeyVal, orig, { props.push(make_node(AST_ObjectKeyVal, orig, {
key: key, key: key,
value: to_node(compressor, value[key], orig) value: to_node(value[key], orig)
})); }));
} }
return make_node(AST_Object, orig, { return make_node(AST_Object, orig, {
properties: props properties: props
}); });
} }
return make_node_from_constant(compressor, value, orig); return make_node_from_constant(value, orig);
} }
def(AST_Node, noop); def(AST_Node, noop);
def(AST_Dot, function(compressor, suffix){ def(AST_Dot, function(compressor, suffix){
@@ -1228,7 +1238,7 @@ merge(Compressor.prototype, {
var name; var name;
var defines = compressor.option("global_defs"); var defines = compressor.option("global_defs");
if (defines && HOP(defines, (name = this.name + suffix))) { if (defines && HOP(defines, (name = this.name + suffix))) {
var node = to_node(compressor, defines[name], this); var node = to_node(defines[name], this);
var top = compressor.find_parent(AST_Toplevel); var top = compressor.find_parent(AST_Toplevel);
node.walk(new TreeWalker(function(node) { node.walk(new TreeWalker(function(node) {
if (node instanceof AST_SymbolRef) { if (node instanceof AST_SymbolRef) {
@@ -1243,45 +1253,40 @@ merge(Compressor.prototype, {
node.DEFMETHOD("_find_defs", func); node.DEFMETHOD("_find_defs", func);
}); });
function best_of(ast1, ast2) { function best_of_expression(ast1, ast2) {
return ast1.print_to_string().length > return ast1.print_to_string().length >
ast2.print_to_string().length ast2.print_to_string().length
? ast2 : ast1; ? ast2 : ast1;
} }
function best_of_statement(ast1, ast2) { function best_of_statement(ast1, ast2) {
return best_of(make_node(AST_SimpleStatement, ast1, { return best_of_expression(make_node(AST_SimpleStatement, ast1, {
body: ast1 body: ast1
}), make_node(AST_SimpleStatement, ast2, { }), make_node(AST_SimpleStatement, ast2, {
body: ast2 body: ast2
})).body; })).body;
} }
function best_of(compressor, ast1, ast2) {
return (first_in_statement(compressor) ? best_of_statement : best_of_expression)(ast1, ast2);
}
// methods to evaluate a constant expression // methods to evaluate a constant expression
(function (def){ (function (def){
// The evaluate method returns an array with one or two // If the node has been successfully reduced to a constant,
// elements. If the node has been successfully reduced to a // then its value is returned; otherwise the element itself
// constant, then the second element tells us the value; // is returned.
// otherwise the second element is missing. The first element // They can be distinguished as constant value is never a
// of the array is always an AST_Node descendant; if // descendant of AST_Node.
// evaluation was successful it's a node that represents the
// constant; otherwise it's the original or a replacement node.
AST_Node.DEFMETHOD("evaluate", function(compressor){ AST_Node.DEFMETHOD("evaluate", function(compressor){
if (!compressor.option("evaluate")) return [ this ]; if (!compressor.option("evaluate")) return this;
var val;
try { try {
val = this._eval(compressor); var val = this._eval(compressor);
return !val || val instanceof RegExp || typeof val != "object" ? val : this;
} catch(ex) { } catch(ex) {
if (ex !== def) throw ex; if (ex !== def) throw ex;
return [ this ]; return this;
} }
var node;
try {
node = make_node_from_constant(compressor, val, this);
} catch(ex) {
return [ this ];
}
return [ best_of(node, this), val ];
}); });
var unaryPrefix = makePredicate("! ~ - +"); var unaryPrefix = makePredicate("! ~ - +");
AST_Node.DEFMETHOD("is_constant", function(){ AST_Node.DEFMETHOD("is_constant", function(){
@@ -1319,8 +1324,8 @@ merge(Compressor.prototype, {
})); }));
} }
var result = this.evaluate(compressor); var result = this.evaluate(compressor);
if (result.length > 1) { if (result !== this) {
return result[1]; return result;
} }
throw new Error(string_template("Cannot evaluate constant [{file}:{line},{col}]", this.start)); throw new Error(string_template("Cannot evaluate constant [{file}:{line},{col}]", this.start));
}); });
@@ -1480,9 +1485,9 @@ merge(Compressor.prototype, {
var stat = make_node(AST_SimpleStatement, alt, { var stat = make_node(AST_SimpleStatement, alt, {
body: alt body: alt
}); });
return best_of(negated, stat) === stat ? alt : negated; return best_of_expression(negated, stat) === stat ? alt : negated;
} }
return best_of(negated, alt); return best_of_expression(negated, alt);
} }
def(AST_Node, function(){ def(AST_Node, function(){
return basic_negation(this); return basic_negation(this);
@@ -2233,27 +2238,24 @@ merge(Compressor.prototype, {
}); });
OPT(AST_DWLoop, function(self, compressor){ OPT(AST_DWLoop, function(self, compressor){
var cond = self.condition.evaluate(compressor);
self.condition = cond[0];
if (!compressor.option("loops")) return self; if (!compressor.option("loops")) return self;
if (cond.length > 1) { var cond = self.condition.evaluate(compressor);
if (cond[1]) { if (cond !== self.condition) {
if (cond) {
return make_node(AST_For, self, { return make_node(AST_For, self, {
body: self.body body: self.body
}); });
} else if (self instanceof AST_While) { } else if (compressor.option("dead_code") && self instanceof AST_While) {
if (compressor.option("dead_code")) { var a = [];
var a = []; extract_declarations_from_unreachable_code(compressor, self.body, a);
extract_declarations_from_unreachable_code(compressor, self.body, a); return make_node(AST_BlockStatement, self, { body: a });
return make_node(AST_BlockStatement, self, { body: a });
}
} else { } else {
// self instanceof AST_Do cond = make_node_from_constant(cond, self.condition).transform(compressor);
return self; self.condition = best_of_expression(cond, self.condition);
} }
} }
if (self instanceof AST_While) { if (self instanceof AST_While) {
return make_node(AST_For, self, self).transform(compressor); return make_node(AST_For, self, self).optimize(compressor);
} }
return self; return self;
}); });
@@ -2275,7 +2277,7 @@ merge(Compressor.prototype, {
var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body; var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body;
if (first instanceof AST_If) { if (first instanceof AST_If) {
if (first.body instanceof AST_Break if (first.body instanceof AST_Break
&& compressor.loopcontrol_target(first.body.label) === self) { && compressor.loopcontrol_target(first.body.label) === compressor.self()) {
if (self.condition) { if (self.condition) {
self.condition = make_node(AST_Binary, self.condition, { self.condition = make_node(AST_Binary, self.condition, {
left: self.condition, left: self.condition,
@@ -2288,7 +2290,7 @@ merge(Compressor.prototype, {
drop_it(first.alternative); drop_it(first.alternative);
} }
else if (first.alternative instanceof AST_Break else if (first.alternative instanceof AST_Break
&& compressor.loopcontrol_target(first.alternative.label) === self) { && compressor.loopcontrol_target(first.alternative.label) === compressor.self()) {
if (self.condition) { if (self.condition) {
self.condition = make_node(AST_Binary, self.condition, { self.condition = make_node(AST_Binary, self.condition, {
left: self.condition, left: self.condition,
@@ -2304,27 +2306,25 @@ merge(Compressor.prototype, {
}; };
OPT(AST_For, function(self, compressor){ OPT(AST_For, function(self, compressor){
var cond = self.condition;
if (cond) {
cond = cond.evaluate(compressor);
self.condition = cond[0];
}
if (!compressor.option("loops")) return self; if (!compressor.option("loops")) return self;
if (cond) { if (self.condition) {
if (cond.length > 1 && !cond[1]) { var cond = self.condition.evaluate(compressor);
if (compressor.option("dead_code")) { if (compressor.option("dead_code") && !cond) {
var a = []; var a = [];
if (self.init instanceof AST_Statement) { if (self.init instanceof AST_Statement) {
a.push(self.init); a.push(self.init);
}
else if (self.init) {
a.push(make_node(AST_SimpleStatement, self.init, {
body: self.init
}));
}
extract_declarations_from_unreachable_code(compressor, self.body, a);
return make_node(AST_BlockStatement, self, { body: a });
} }
else if (self.init) {
a.push(make_node(AST_SimpleStatement, self.init, {
body: self.init
}));
}
extract_declarations_from_unreachable_code(compressor, self.body, a);
return make_node(AST_BlockStatement, self, { body: a });
}
if (cond !== self.condition) {
cond = make_node_from_constant(cond, self.condition).transform(compressor);
self.condition = best_of_expression(cond, self.condition);
} }
} }
if_break_in_loop(self, compressor); if_break_in_loop(self, compressor);
@@ -2340,9 +2340,8 @@ merge(Compressor.prototype, {
// “has no side effects”; also it doesn't work for cases like // “has no side effects”; also it doesn't work for cases like
// `x && true`, though it probably should. // `x && true`, though it probably should.
var cond = self.condition.evaluate(compressor); var cond = self.condition.evaluate(compressor);
self.condition = cond[0]; if (cond !== self.condition) {
if (cond.length > 1) { if (cond) {
if (cond[1]) {
compressor.warn("Condition always true [{file}:{line},{col}]", self.condition.start); compressor.warn("Condition always true [{file}:{line},{col}]", self.condition.start);
if (compressor.option("dead_code")) { if (compressor.option("dead_code")) {
var a = []; var a = [];
@@ -2361,6 +2360,8 @@ merge(Compressor.prototype, {
return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor); return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor);
} }
} }
cond = make_node_from_constant(cond, self.condition).transform(compressor);
self.condition = best_of_expression(cond, self.condition);
} }
var negated = self.condition.negate(compressor); var negated = self.condition.negate(compressor);
var self_condition_length = self.condition.print_to_string().length; var self_condition_length = self.condition.print_to_string().length;
@@ -2488,12 +2489,12 @@ merge(Compressor.prototype, {
} }
break; break;
} }
var exp = self.expression.evaluate(compressor); var value = self.expression.evaluate(compressor);
out: if (exp.length == 2) try { out: if (value !== self.expression) try {
// constant expression // constant expression
self.expression = exp[0]; var expression = make_node_from_constant(value, self.expression);
self.expression = best_of_expression(expression, self.expression);
if (!compressor.option("dead_code")) break out; if (!compressor.option("dead_code")) break out;
var value = exp[1];
var in_if = false; var in_if = false;
var in_block = false; var in_block = false;
var started = false; var started = false;
@@ -2540,11 +2541,11 @@ merge(Compressor.prototype, {
if (stopped) return MAP.skip; if (stopped) return MAP.skip;
if (node instanceof AST_Case) { if (node instanceof AST_Case) {
var exp = node.expression.evaluate(compressor); var exp = node.expression.evaluate(compressor);
if (exp.length < 2) { if (exp === node.expression) {
// got a case with non-constant expression, baling out // got a case with non-constant expression, baling out
throw self; throw self;
} }
if (exp[1] === value || started) { if (exp === value || started) {
started = true; started = true;
if (aborts(node)) stopped = true; if (aborts(node)) stopped = true;
descend(node, this); descend(node, this);
@@ -2758,15 +2759,14 @@ merge(Compressor.prototype, {
var separator; var separator;
if (self.args.length > 0) { if (self.args.length > 0) {
separator = self.args[0].evaluate(compressor); separator = self.args[0].evaluate(compressor);
if (separator.length < 2) break EXIT; // not a constant if (separator === self.args[0]) break EXIT; // not a constant
separator = separator[1];
} }
var elements = []; var elements = [];
var consts = []; var consts = [];
exp.expression.elements.forEach(function(el) { exp.expression.elements.forEach(function(el) {
el = el.evaluate(compressor); var value = el.evaluate(compressor);
if (el.length > 1) { if (value !== el) {
consts.push(el[1]); consts.push(value);
} else { } else {
if (consts.length > 0) { if (consts.length > 0) {
elements.push(make_node(AST_String, self, { elements.push(make_node(AST_String, self, {
@@ -2774,7 +2774,7 @@ merge(Compressor.prototype, {
})); }));
consts.length = 0; consts.length = 0;
} }
elements.push(el[0]); elements.push(el);
} }
}); });
if (consts.length > 0) { if (consts.length > 0) {
@@ -2815,7 +2815,7 @@ merge(Compressor.prototype, {
node.expression = node.expression.clone(); node.expression = node.expression.clone();
node.expression.expression = node.expression.expression.clone(); node.expression.expression = node.expression.expression.clone();
node.expression.expression.elements = elements; node.expression.expression.elements = elements;
return best_of(self, node); return best_of(compressor, self, node);
} }
} }
if (exp instanceof AST_Function) { if (exp instanceof AST_Function) {
@@ -2961,8 +2961,7 @@ merge(Compressor.prototype, {
return e.expression; return e.expression;
} }
if (e instanceof AST_Binary) { if (e instanceof AST_Binary) {
var statement = first_in_statement(compressor); self = best_of(compressor, self, e.negate(compressor, first_in_statement(compressor)));
self = (statement ? best_of_statement : best_of)(self, e.negate(compressor, statement));
} }
break; break;
case "typeof": case "typeof":
@@ -2975,7 +2974,15 @@ merge(Compressor.prototype, {
}).optimize(compressor); }).optimize(compressor);
} }
} }
return self.evaluate(compressor)[0]; // avoids infinite recursion of numerals
if (self.operator != "-" || !(self.expression instanceof AST_Number)) {
var ev = self.evaluate(compressor);
if (ev !== self) {
ev = make_node_from_constant(ev, self).optimize(compressor);
return best_of(compressor, ev, self);
}
}
return self;
}); });
function has_side_effects_or_prop_access(node, compressor) { function has_side_effects_or_prop_access(node, compressor) {
@@ -3097,48 +3104,48 @@ merge(Compressor.prototype, {
case "&&": case "&&":
var ll = self.left.evaluate(compressor); var ll = self.left.evaluate(compressor);
var rr = self.right.evaluate(compressor); var rr = self.right.evaluate(compressor);
if ((ll.length > 1 && !ll[1]) || (rr.length > 1 && !rr[1])) { if (!ll || !rr) {
compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start); compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start);
return make_node(AST_Seq, self, { return make_node(AST_Seq, self, {
car: self.left, car: self.left,
cdr: make_node(AST_False, self) cdr: make_node(AST_False, self)
}).optimize(compressor); }).optimize(compressor);
} }
if (ll.length > 1 && ll[1]) { if (ll !== self.left && ll) {
return rr[0]; return self.right.optimize(compressor);
} }
if (rr.length > 1 && rr[1]) { if (rr !== self.right && rr) {
return ll[0]; return self.left.optimize(compressor);
} }
break; break;
case "||": case "||":
var ll = self.left.evaluate(compressor); var ll = self.left.evaluate(compressor);
var rr = self.right.evaluate(compressor); var rr = self.right.evaluate(compressor);
if ((ll.length > 1 && ll[1]) || (rr.length > 1 && rr[1])) { if (ll !== self.left && ll || rr !== self.right && rr) {
compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start); compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start);
return make_node(AST_Seq, self, { return make_node(AST_Seq, self, {
car: self.left, car: self.left,
cdr: make_node(AST_True, self) cdr: make_node(AST_True, self)
}).optimize(compressor); }).optimize(compressor);
} }
if (ll.length > 1 && !ll[1]) { if (!ll) {
return rr[0]; return self.right.optimize(compressor);
} }
if (rr.length > 1 && !rr[1]) { if (!rr) {
return ll[0]; return self.left.optimize(compressor);
} }
break; break;
case "+": case "+":
var ll = self.left.evaluate(compressor); var ll = self.left.evaluate(compressor);
var rr = self.right.evaluate(compressor); var rr = self.right.evaluate(compressor);
if (ll.length > 1 && ll[0] instanceof AST_String && ll[1]) { if (ll && typeof ll == "string") {
compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start); compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
return make_node(AST_Seq, self, { return make_node(AST_Seq, self, {
car: self.right, car: self.right,
cdr: make_node(AST_True, self) cdr: make_node(AST_True, self)
}).optimize(compressor); }).optimize(compressor);
} }
if (rr.length > 1 && rr[0] instanceof AST_String && rr[1]) { if (rr && typeof rr == "string") {
compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start); compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
return make_node(AST_Seq, self, { return make_node(AST_Seq, self, {
car: self.left, car: self.left,
@@ -3150,12 +3157,11 @@ merge(Compressor.prototype, {
if (compressor.option("comparisons") && self.is_boolean()) { if (compressor.option("comparisons") && self.is_boolean()) {
if (!(compressor.parent() instanceof AST_Binary) if (!(compressor.parent() instanceof AST_Binary)
|| compressor.parent() instanceof AST_Assign) { || compressor.parent() instanceof AST_Assign) {
var statement = first_in_statement(compressor);
var negated = make_node(AST_UnaryPrefix, self, { var negated = make_node(AST_UnaryPrefix, self, {
operator: "!", operator: "!",
expression: self.negate(compressor, statement) expression: self.negate(compressor, first_in_statement(compressor))
}); });
self = (statement ? best_of_statement : best_of)(self, negated); self = best_of(compressor, self, negated);
} }
if (compressor.option("unsafe_comps")) { if (compressor.option("unsafe_comps")) {
switch (self.operator) { switch (self.operator) {
@@ -3307,9 +3313,9 @@ merge(Compressor.prototype, {
}); });
if (self.right instanceof AST_Constant if (self.right instanceof AST_Constant
&& !(self.left instanceof AST_Constant)) { && !(self.left instanceof AST_Constant)) {
self = best_of(reversed, self); self = best_of(compressor, reversed, self);
} else { } else {
self = best_of(self, reversed); self = best_of(compressor, self, reversed);
} }
} }
if (associative && self.is_number(compressor)) { if (associative && self.is_number(compressor)) {
@@ -3406,7 +3412,12 @@ merge(Compressor.prototype, {
self.right = self.right.right; self.right = self.right.right;
return self.transform(compressor); return self.transform(compressor);
} }
return self.evaluate(compressor)[0]; var ev = self.evaluate(compressor);
if (ev !== self) {
ev = make_node_from_constant(ev, self).optimize(compressor);
return best_of(compressor, ev, self);
}
return self;
}); });
OPT(AST_SymbolRef, function(self, compressor){ OPT(AST_SymbolRef, function(self, compressor){
@@ -3421,11 +3432,11 @@ merge(Compressor.prototype, {
&& (!self.scope.uses_with || !compressor.find_parent(AST_With))) { && (!self.scope.uses_with || !compressor.find_parent(AST_With))) {
switch (self.name) { switch (self.name) {
case "undefined": case "undefined":
return make_node(AST_Undefined, self).transform(compressor); return make_node(AST_Undefined, self).optimize(compressor);
case "NaN": case "NaN":
return make_node(AST_NaN, self).transform(compressor); return make_node(AST_NaN, self).optimize(compressor);
case "Infinity": case "Infinity":
return make_node(AST_Infinity, self).transform(compressor); return make_node(AST_Infinity, self).optimize(compressor);
} }
} }
if (compressor.option("evaluate") && compressor.option("reduce_vars")) { if (compressor.option("evaluate") && compressor.option("reduce_vars")) {
@@ -3433,12 +3444,14 @@ merge(Compressor.prototype, {
if (d.fixed) { if (d.fixed) {
if (d.should_replace === undefined) { if (d.should_replace === undefined) {
var init = d.fixed.evaluate(compressor); var init = d.fixed.evaluate(compressor);
if (init.length > 1) { if (init !== d.fixed) {
var value = init[0].print_to_string().length; init = make_node_from_constant(init, d.fixed).optimize(compressor);
init = best_of_expression(init, d.fixed);
var value = init.print_to_string().length;
var name = d.name.length; var name = d.name.length;
var freq = d.references.length; var freq = d.references.length;
var overhead = d.global || !freq ? 0 : (name + 2 + value) / freq; var overhead = d.global || !freq ? 0 : (name + 2 + value) / freq;
d.should_replace = value <= name + overhead ? init[0] : false; d.should_replace = value <= name + overhead ? init : false;
} else { } else {
d.should_replace = false; d.should_replace = false;
} }
@@ -3509,8 +3522,8 @@ merge(Compressor.prototype, {
return AST_Seq.cons(car, self); return AST_Seq.cons(car, self);
} }
var cond = self.condition.evaluate(compressor); var cond = self.condition.evaluate(compressor);
if (cond.length > 1) { if (cond !== self.condition) {
if (cond[1]) { if (cond) {
compressor.warn("Condition always true [{file}:{line},{col}]", self.start); compressor.warn("Condition always true [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor.parent(), self, self.consequent); return maintain_this_binding(compressor.parent(), self, self.consequent);
} else { } else {
@@ -3518,9 +3531,8 @@ merge(Compressor.prototype, {
return maintain_this_binding(compressor.parent(), self, self.alternative); return maintain_this_binding(compressor.parent(), self, self.alternative);
} }
} }
var statement = first_in_statement(compressor); var negated = cond.negate(compressor, first_in_statement(compressor));
var negated = cond[0].negate(compressor, statement); if (best_of(compressor, cond, negated) === negated) {
if ((statement ? best_of_statement : best_of)(cond[0], negated) === negated) {
self = make_node(AST_Conditional, self, { self = make_node(AST_Conditional, self, {
condition: negated, condition: negated,
consequent: self.alternative, consequent: self.alternative,
@@ -3700,7 +3712,12 @@ merge(Compressor.prototype, {
}); });
} }
} }
return self.evaluate(compressor)[0]; var ev = self.evaluate(compressor);
if (ev !== self) {
ev = make_node_from_constant(ev, self).optimize(compressor);
return best_of(compressor, ev, self);
}
return self;
}); });
OPT(AST_Dot, function(self, compressor){ OPT(AST_Dot, function(self, compressor){
@@ -3739,13 +3756,17 @@ merge(Compressor.prototype, {
break; break;
} }
} }
return self.evaluate(compressor)[0]; var ev = self.evaluate(compressor);
if (ev !== self) {
ev = make_node_from_constant(ev, self).optimize(compressor);
return best_of(compressor, ev, self);
}
return self;
}); });
function literals_in_boolean_context(self, compressor) { function literals_in_boolean_context(self, compressor) {
if (compressor.option("booleans") && compressor.in_boolean_context()) { if (compressor.option("booleans") && compressor.in_boolean_context()) {
var best = first_in_statement(compressor) ? best_of_statement : best_of; return best_of(compressor, self, make_node(AST_Seq, self, {
return best(self, make_node(AST_Seq, self, {
car: self, car: self,
cdr: make_node(AST_True, self) cdr: make_node(AST_True, self)
}).optimize(compressor)); }).optimize(compressor));