Merge branch 'master' into harmony-v3.2.1

This commit is contained in:
alexlamsl
2017-12-03 11:59:41 +08:00
26 changed files with 910 additions and 256 deletions

View File

@@ -1203,24 +1203,6 @@ TreeWalker.prototype = {
}
}
},
in_boolean_context: function() {
var stack = this.stack;
var i = stack.length, self = stack[--i];
while (i > 0) {
var p = stack[--i];
if ((p instanceof AST_If && p.condition === self) ||
(p instanceof AST_Conditional && p.condition === self) ||
(p instanceof AST_DWLoop && p.condition === self) ||
(p instanceof AST_For && p.condition === self) ||
(p instanceof AST_UnaryPrefix && p.operator == "!" && p.expression === self))
{
return true;
}
if (!(p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||")))
return false;
self = p;
}
},
loopcontrol_target: function(node) {
var stack = this.stack;
if (node.label) for (var i = stack.length; --i >= 0;) {

View File

@@ -152,6 +152,27 @@ merge(Compressor.prototype, {
return true;
return false;
},
in_boolean_context: function() {
if (!this.option("booleans")) return false;
var self = this.self();
for (var i = 0, p; p = this.parent(i); i++) {
if (p instanceof AST_SimpleStatement
|| p instanceof AST_Conditional && p.condition === self
|| p instanceof AST_DWLoop && p.condition === self
|| p instanceof AST_For && p.condition === self
|| p instanceof AST_If && p.condition === self
|| p instanceof AST_UnaryPrefix && p.operator == "!" && p.expression === self) {
return true;
}
if (p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||")
|| p instanceof AST_Conditional
|| p.tail_node() === self) {
self = p;
} else {
return false;
}
}
},
compress: function(node) {
if (this.option("expression")) {
node.process_expression(true);
@@ -741,7 +762,7 @@ merge(Compressor.prototype, {
function make_sequence(orig, expressions) {
if (expressions.length == 1) return expressions[0];
return make_node(AST_Sequence, orig, {
expressions: expressions
expressions: expressions.reduce(merge_sequence, [])
});
}
@@ -798,6 +819,7 @@ merge(Compressor.prototype, {
} else {
array.push(node);
}
return array;
}
function as_statement_array(thing) {
@@ -851,14 +873,6 @@ merge(Compressor.prototype, {
|| compressor.option("unsafe") && global_names(this.name);
});
function drop_decl(def) {
def.eliminated++;
if (def.orig.length == def.eliminated) {
def.scope.functions.del(def.name);
def.scope.variables.del(def.name);
}
}
function is_identifier_atom(node) {
return node instanceof AST_Infinity
|| node instanceof AST_NaN
@@ -1169,6 +1183,7 @@ merge(Compressor.prototype, {
function get_lhs(expr) {
if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
var def = expr.name.definition();
if (!member(expr.name, def.orig)) return;
var declared = def.orig.length - def.eliminated;
var referenced = def.references.length - def.replaced;
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
@@ -1224,7 +1239,6 @@ merge(Compressor.prototype, {
if (node === expr || node.body === expr) {
found = true;
if (node instanceof AST_VarDef) {
drop_decl(node.name.definition());
node.value = null;
return node;
}
@@ -1496,13 +1510,7 @@ merge(Compressor.prototype, {
function cons_seq(right) {
n--;
var left = prev.body;
if (!(left instanceof AST_Sequence)) {
left = make_node(AST_Sequence, left, {
expressions: [ left ]
});
}
merge_sequence(left.expressions, right);
return left.transform(compressor);
return make_sequence(left, [ left, right ]).transform(compressor);
};
var n = 0, prev;
for (var i = 0, len = statements.length; i < len; i++) {
@@ -1520,7 +1528,7 @@ merge(Compressor.prototype, {
if (!abort) {
if (stat.init) stat.init = cons_seq(stat.init);
else {
stat.init = prev.body.drop_side_effect_free(compressor);
stat.init = prev.body;
n--;
}
}
@@ -1664,7 +1672,7 @@ merge(Compressor.prototype, {
|| this.alternative._dot_throw(compressor);
})
def(AST_Sequence, function(compressor) {
return this.expressions[this.expressions.length - 1]._dot_throw(compressor);
return this.tail_node()._dot_throw(compressor);
});
def(AST_SymbolRef, function(compressor) {
if (this.is_undefined) return true;
@@ -1701,7 +1709,7 @@ merge(Compressor.prototype, {
return this.operator == "=" && this.right.is_boolean();
});
def(AST_Sequence, function(){
return this.expressions[this.expressions.length - 1].is_boolean();
return this.tail_node().is_boolean();
});
def(AST_True, return_true);
def(AST_False, return_true);
@@ -1728,7 +1736,7 @@ merge(Compressor.prototype, {
|| this.operator == "=" && this.right.is_number(compressor);
});
def(AST_Sequence, function(compressor){
return this.expressions[this.expressions.length - 1].is_number(compressor);
return this.tail_node().is_number(compressor);
});
def(AST_Conditional, function(compressor){
return this.consequent.is_number(compressor) && this.alternative.is_number(compressor);
@@ -1755,7 +1763,7 @@ merge(Compressor.prototype, {
return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
});
def(AST_Sequence, function(compressor){
return this.expressions[this.expressions.length - 1].is_string(compressor);
return this.tail_node().is_string(compressor);
});
def(AST_Conditional, function(compressor){
return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
@@ -1933,12 +1941,17 @@ merge(Compressor.prototype, {
return this;
});
def(AST_UnaryPrefix, function(compressor){
var e = this.expression;
// Function would be evaluated to an array and so typeof would
// incorrectly return 'object'. Hence making is a special case.
if (this.operator == "typeof" && is_func_expr(this.expression)) {
if (compressor.option("typeofs")
&& this.operator == "typeof"
&& (e instanceof AST_Lambda
|| e instanceof AST_SymbolRef
&& e.fixed_value() instanceof AST_Lambda)) {
return typeof function(){};
}
var e = ev(this.expression, compressor);
e = ev(e, compressor);
if (e === this.expression) return this;
switch (this.operator) {
case "!": return !e;
@@ -2000,7 +2013,6 @@ merge(Compressor.prototype, {
return value === node ? this : value;
});
def(AST_SymbolRef, function(compressor){
if (!compressor.option("reduce_vars")) return this;
var fixed = this.fixed_value();
if (!fixed) return this;
this._eval = return_this;
@@ -2402,6 +2414,10 @@ merge(Compressor.prototype, {
self.walk(new TreeWalker(function(node) {
if (!result) return true;
if (node instanceof AST_SymbolRef) {
if (self.inlined) {
result = false;
return true;
}
var def = node.definition();
if (member(def, self.enclosed)
&& !self.variables.has(def.name)) {
@@ -2589,51 +2605,21 @@ merge(Compressor.prototype, {
});
return true;
}
var sym;
if (scope === self
&& (sym = assign_as_unused(node)) instanceof AST_SymbolRef
&& !is_ref_of(node.left, AST_SymbolBlockDeclaration)
&& self.variables.get(sym.name) === sym.definition()) {
if (node instanceof AST_Assign) node.right.walk(tw);
return true;
}
if (node instanceof AST_SymbolRef) {
var node_def = node.definition();
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
}
return true;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend();
scope = save_scope;
return true;
}
if (node.destructuring && destructuring_value) {
initializations.add(node.name, destructuring_value);
}
return scan_ref_scoped(node, descend);
});
self.walk(tw);
// pass 2: for every used symbol we need to walk its
// initialization code to figure out if it uses other
// symbols (that may not be in_use).
tw = new TreeWalker(scan_ref_scoped);
for (var i = 0; i < in_use.length; ++i) {
in_use[i].orig.forEach(function(decl){
// undeclared globals will be instanceof AST_SymbolRef
var init = initializations.get(decl.name);
if (init) init.forEach(function(init){
var tw = new TreeWalker(function(node){
if (node instanceof AST_SymbolRef) {
var node_def = node.definition();
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
}
}
});
init.walk(tw);
});
});
@@ -2683,7 +2669,7 @@ merge(Compressor.prototype, {
var keep = (def.id in in_use_ids) || !drop_funcs && def.global;
if (!keep) {
compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
drop_decl(def);
def.eliminated++;
return make_node(AST_EmptyStatement, node);
}
return node;
@@ -2705,17 +2691,24 @@ merge(Compressor.prototype, {
if (!(drop_vars || drop_block) || sym.id in in_use_ids) {
if (def.name instanceof AST_SymbolVar) {
var var_defs = var_defs_by_id.get(sym.id);
if (var_defs.length > 1 && !def.value) {
if (var_defs.length > 1 && (!def.value || sym.orig.indexOf(def.name) > sym.eliminated)) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
if (def.value) {
side_effects.push(make_node(AST_Assign, def, {
operator: "=",
left: make_node(AST_SymbolRef, def.name, def.name),
right: def.value
}));
}
remove(var_defs, def);
drop_decl(sym);
sym.eliminated++;
return;
}
}
if (def.value) {
if (side_effects.length > 0) {
if (tail.length > 0) {
merge_sequence(side_effects, def.value);
side_effects.push(def.value);
def.value = make_sequence(def.value, side_effects);
} else {
body.push(make_node(AST_SimpleStatement, node, {
@@ -2730,36 +2723,20 @@ merge(Compressor.prototype, {
}
} else if (sym.orig[0] instanceof AST_SymbolCatch) {
var value = def.value && def.value.drop_side_effect_free(compressor);
if (value) merge_sequence(side_effects, value);
if (value) side_effects.push(value);
def.value = null;
head.push(def);
} else {
var value = def.value && def.value.drop_side_effect_free(compressor);
if (value) {
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
merge_sequence(side_effects, value);
side_effects.push(value);
} else {
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
}
drop_decl(sym);
sym.eliminated++;
}
});
if (head.length == 0 && tail.length == 1 && tail[0].name instanceof AST_SymbolVar) {
var var_defs = var_defs_by_id.get(tail[0].name.definition().id);
if (var_defs.length > 1) {
var def = tail.pop();
compressor.warn("Converting duplicated definition of variable {name} to assignment [{file}:{line},{col}]", template(def.name));
remove(var_defs, def);
side_effects.unshift(make_node(AST_Assign, def, {
operator: "=",
left: make_node(AST_SymbolRef, def.name, def.name),
right: def.value
}));
def = def.name.definition();
drop_decl(def);
def.replaced--;
}
}
if (head.length > 0 || tail.length > 0) {
node.definitions = head.concat(tail);
body.push(node);
@@ -2844,6 +2821,32 @@ merge(Compressor.prototype, {
}
);
self.transform(tt);
function scan_ref_scoped(node, descend) {
var sym;
if (scope === self
&& (sym = assign_as_unused(node)) instanceof AST_SymbolRef
&& !is_ref_of(node.left, AST_SymbolBlockDeclaration)
&& self.variables.get(sym.name) === sym.definition()) {
if (node instanceof AST_Assign) node.right.walk(tw);
return true;
}
if (node instanceof AST_SymbolRef) {
var node_def = node.definition();
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
}
return true;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend();
scope = save_scope;
return true;
}
}
});
AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){
@@ -3005,7 +3008,7 @@ merge(Compressor.prototype, {
if (!compressor.option("hoist_props") || compressor.has_directive("use asm")) return self;
var top_retain = self instanceof AST_Toplevel && compressor.top_retain || return_false;
var defs_by_id = Object.create(null);
var tt = new TreeTransformer(function(node) {
var tt = new TreeTransformer(function(node, descend) {
if (node instanceof AST_Definitions && tt.parent() instanceof AST_Export) return node;
if (node instanceof AST_VarDef) {
var sym = node.name, def, value;
@@ -3016,6 +3019,7 @@ merge(Compressor.prototype, {
&& !top_retain(def)
&& (value = sym.fixed_value()) === node.value
&& value instanceof AST_Object) {
descend(node, this);
var defs = new Dictionary();
var assignments = [];
value.properties.forEach(function(prop) {
@@ -3073,7 +3077,7 @@ merge(Compressor.prototype, {
var node = nodes[i].drop_side_effect_free(compressor, first_in_statement);
changed |= node !== nodes[i];
if (node) {
merge_sequence(ret, node);
ret.push(node);
first_in_statement = false;
}
}
@@ -3186,11 +3190,11 @@ merge(Compressor.prototype, {
return make_sequence(this, [ expression, property ]);
});
def(AST_Sequence, function(compressor){
var last = this.expressions[this.expressions.length - 1];
var last = this.tail_node();
var expr = last.drop_side_effect_free(compressor);
if (expr === last) return this;
var expressions = this.expressions.slice(0, -1);
if (expr) merge_sequence(expressions, expr);
if (expr) expressions.push(expr);
return make_sequence(this, expressions);
});
def(AST_Expansion, function(compressor, first_in_statement){
@@ -3220,34 +3224,40 @@ merge(Compressor.prototype, {
return self;
});
OPT(AST_DWLoop, function(self, compressor){
OPT(AST_While, function(self, compressor){
return compressor.option("loops") ? make_node(AST_For, self, self).optimize(compressor) : self;
});
OPT(AST_Do, function(self, compressor){
if (!compressor.option("loops")) return self;
var cond = self.condition.evaluate(compressor);
if (cond !== self.condition) {
if (cond) {
return make_node(AST_For, self, {
body: self.body
});
}
if (compressor.option("dead_code") && self instanceof AST_While) {
var a = [];
extract_declarations_from_unreachable_code(compressor, self.body, a);
return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor);
}
if (self instanceof AST_Do) {
var has_loop_control = false;
var tw = new TreeWalker(function(node) {
if (node instanceof AST_Scope || has_loop_control) return true;
if (node instanceof AST_LoopControl && tw.loopcontrol_target(node) === self)
return has_loop_control = true;
});
var parent = compressor.parent();
(parent instanceof AST_LabeledStatement ? parent : self).walk(tw);
if (!has_loop_control) return self.body;
}
}
if (self instanceof AST_While) {
return make_node(AST_For, self, self).optimize(compressor);
var cond = 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, {
body: [
self.body,
make_node(AST_SimpleStatement, self.condition, {
body: self.condition
})
]
})
}).optimize(compressor);
var has_loop_control = false;
var tw = new TreeWalker(function(node) {
if (node instanceof AST_Scope || has_loop_control) return true;
if (node instanceof AST_LoopControl && tw.loopcontrol_target(node) === self)
return has_loop_control = true;
});
var parent = compressor.parent();
(parent instanceof AST_LabeledStatement ? parent : self).walk(tw);
if (!has_loop_control) return make_node(AST_BlockStatement, self.body, {
body: [
self.body,
make_node(AST_SimpleStatement, self.condition, {
body: self.condition
})
]
}).optimize(compressor);
}
return self;
});
@@ -3299,24 +3309,36 @@ merge(Compressor.prototype, {
OPT(AST_For, function(self, compressor){
if (!compressor.option("loops")) return self;
if (compressor.option("side_effects") && self.init) {
self.init = self.init.drop_side_effect_free(compressor);
}
if (self.condition) {
var cond = self.condition.evaluate(compressor);
if (compressor.option("dead_code") && !cond) {
var a = [];
if (self.init instanceof AST_Statement) {
a.push(self.init);
if (!(cond instanceof AST_Node)) {
if (cond) self.condition = null;
else if (!compressor.option("dead_code")) {
var orig = self.condition;
self.condition = make_node_from_constant(cond, self.condition);
self.condition = best_of_expression(self.condition.transform(compressor), orig);
}
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 }).optimize(compressor);
}
if (cond !== self.condition) {
cond = make_node_from_constant(cond, self.condition).transform(compressor);
self.condition = best_of_expression(cond, self.condition);
if (compressor.option("dead_code")) {
if (cond instanceof AST_Node) cond = self.condition.tail_node().evaluate(compressor);
if (!cond) {
var body = [];
extract_declarations_from_unreachable_code(compressor, self.body, body);
if (self.init instanceof AST_Statement) {
body.push(self.init);
} else if (self.init) {
body.push(make_node(AST_SimpleStatement, self.init, {
body: self.init
}));
}
body.push(make_node(AST_SimpleStatement, self.condition, {
body: self.condition
}));
return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
}
}
}
if_break_in_loop(self, compressor);
@@ -3332,28 +3354,34 @@ merge(Compressor.prototype, {
// “has no side effects”; also it doesn't work for cases like
// `x && true`, though it probably should.
var cond = self.condition.evaluate(compressor);
if (cond !== self.condition) {
if (cond) {
compressor.warn("Condition always true [{file}:{line},{col}]", self.condition.start);
if (compressor.option("dead_code")) {
var a = [];
if (self.alternative) {
extract_declarations_from_unreachable_code(compressor, self.alternative, a);
}
a.push(self.body);
return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor);
}
} else {
if (!compressor.option("dead_code") && !(cond instanceof AST_Node)) {
var orig = self.condition;
self.condition = make_node_from_constant(cond, orig);
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) {
compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
if (compressor.option("dead_code")) {
var a = [];
extract_declarations_from_unreachable_code(compressor, self.body, a);
if (self.alternative) a.push(self.alternative);
return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor);
var body = [];
extract_declarations_from_unreachable_code(compressor, self.body, body);
body.push(make_node(AST_SimpleStatement, self.condition, {
body: self.condition
}));
if (self.alternative) body.push(self.alternative);
return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
} else if (!(cond instanceof AST_Node)) {
compressor.warn("Condition always true [{file}:{line},{col}]", self.condition.start);
var body = [];
if (self.alternative) {
extract_declarations_from_unreachable_code(compressor, self.alternative, body);
}
body.push(make_node(AST_SimpleStatement, self.condition, {
body: self.condition
}));
body.push(self.body);
return make_node(AST_BlockStatement, self, { body: body }).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 self_condition_length = self.condition.print_to_string().length;
@@ -3465,11 +3493,15 @@ merge(Compressor.prototype, {
if (!compressor.option("switches")) return self;
var branch;
var value = self.expression.evaluate(compressor);
if (value !== self.expression) {
var expression = make_node_from_constant(value, self.expression).transform(compressor);
self.expression = best_of_expression(expression, self.expression);
if (!(value instanceof AST_Node)) {
var orig = self.expression;
self.expression = make_node_from_constant(value, orig);
self.expression = best_of_expression(self.expression.transform(compressor), orig);
}
if (!compressor.option("dead_code")) return self;
if (value instanceof AST_Node) {
value = self.expression.tail_node().evaluate(compressor);
}
var decl = [];
var body = [];
var default_branch;
@@ -3482,8 +3514,13 @@ merge(Compressor.prototype, {
} else {
eliminate_branch(branch, body[body.length - 1]);
}
} else if (value !== self.expression) {
} else if (!(value instanceof AST_Node)) {
var exp = branch.expression.evaluate(compressor);
if (!(exp instanceof AST_Node) && exp !== value) {
eliminate_branch(branch, body[body.length - 1]);
continue;
}
if (exp instanceof AST_Node) exp = branch.expression.tail_node().evaluate(compressor);
if (exp === value) {
exact_match = branch;
if (default_branch) {
@@ -3492,9 +3529,6 @@ merge(Compressor.prototype, {
eliminate_branch(default_branch, body[default_index - 1]);
default_branch = null;
}
} else if (exp !== branch.expression) {
eliminate_branch(branch, body[body.length - 1]);
continue;
}
}
if (aborts(branch)) {
@@ -3537,12 +3571,16 @@ merge(Compressor.prototype, {
});
self.walk(tw);
if (!has_break) {
body = body[0].body.slice();
body.unshift(make_node(AST_SimpleStatement, self.expression, {
body: self.expression
var statements = body[0].body.slice();
var exp = body[0].expression;
if (exp) statements.unshift(make_node(AST_SimpleStatement, exp, {
body: exp
}));
statements.unshift(make_node(AST_SimpleStatement, self.expression, {
body:self.expression
}));
return make_node(AST_BlockStatement, self, {
body: body
body: statements
}).optimize(compressor);
}
}
@@ -3611,7 +3649,9 @@ merge(Compressor.prototype, {
});
a.push(var_);
}
drop_decl(def.name.definition());
def = def.name.definition();
def.eliminated++;
def.replaced--;
return a;
}, []);
if (assignments.length == 0) return null;
@@ -3971,7 +4011,7 @@ merge(Compressor.prototype, {
trim_right_for_undefined();
if (end > 0 && compressor.option("cascade")) trim_left_for_assignment();
if (end == 0) {
self = maintain_this_binding(compressor.parent(), self, expressions[0]);
self = maintain_this_binding(compressor.parent(), compressor.self(), expressions[0]);
if (!(self instanceof AST_Sequence)) self = self.optimize(compressor);
return self;
}
@@ -4113,7 +4153,7 @@ merge(Compressor.prototype, {
return make_node(AST_Undefined, self).optimize(compressor);
}
}
if (compressor.option("booleans") && compressor.in_boolean_context()) {
if (compressor.in_boolean_context()) {
switch (self.operator) {
case "!":
if (e instanceof AST_UnaryPrefix && e.operator == "!") {
@@ -4267,7 +4307,7 @@ merge(Compressor.prototype, {
}
break;
}
if (compressor.option("booleans") && self.operator == "+" && compressor.in_boolean_context()) {
if (self.operator == "+" && compressor.in_boolean_context()) {
var ll = self.left.evaluate(compressor);
var rr = self.right.evaluate(compressor);
if (ll && typeof ll == "string") {
@@ -4324,49 +4364,72 @@ merge(Compressor.prototype, {
if (compressor.option("evaluate")) {
switch (self.operator) {
case "&&":
var ll = self.left.evaluate(compressor);
var ll = self.left.truthy ? true : self.left.falsy ? false : self.left.evaluate(compressor);
if (!ll) {
compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor.parent(), self, self.left).optimize(compressor);
} else if (ll !== self.left) {
return maintain_this_binding(compressor.parent(), compressor.self(), self.left).optimize(compressor);
} else if (!(ll instanceof AST_Node)) {
compressor.warn("Condition left of && always true [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor.parent(), self, self.right).optimize(compressor);
return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
}
if (compressor.option("booleans") && compressor.in_boolean_context()) {
var rr = self.right.evaluate(compressor);
if (!rr) {
var rr = self.right.evaluate(compressor);
if (!rr) {
if (compressor.in_boolean_context()) {
compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start);
return make_sequence(self, [
self.left,
make_node(AST_False, self)
]).optimize(compressor);
} else if (rr !== self.right) {
compressor.warn("Dropping side-effect-free && in boolean context [{file}:{line},{col}]", self.start);
} else self.falsy = true;
} else if (!(rr instanceof AST_Node)) {
var parent = compressor.parent();
if (parent.operator == "&&" && parent.left === compressor.self() || compressor.in_boolean_context()) {
compressor.warn("Dropping side-effect-free && [{file}:{line},{col}]", self.start);
return self.left.optimize(compressor);
}
}
// x || false && y ---> x ? y : false
if (self.left.operator == "||") {
var lr = self.left.right.evaluate(compressor);
if (!lr) return make_node(AST_Conditional, self, {
condition: self.left.left,
consequent: self.right,
alternative: self.left.right
}).optimize(compressor);
}
break;
case "||":
var ll = self.left.evaluate(compressor);
var ll = self.left.truthy ? true : self.left.falsy ? false : self.left.evaluate(compressor);
if (!ll) {
compressor.warn("Condition left of || always false [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor.parent(), self, self.right).optimize(compressor);
} else if (ll !== self.left) {
return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
} else if (!(ll instanceof AST_Node)) {
compressor.warn("Condition left of || always true [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor.parent(), self, self.left).optimize(compressor);
return maintain_this_binding(compressor.parent(), compressor.self(), self.left).optimize(compressor);
}
if (compressor.option("booleans") && compressor.in_boolean_context()) {
var rr = self.right.evaluate(compressor);
if (!rr) {
compressor.warn("Dropping side-effect-free || in boolean context [{file}:{line},{col}]", self.start);
var rr = self.right.evaluate(compressor);
if (!rr) {
var parent = compressor.parent();
if (parent.operator == "||" && parent.left === compressor.self() || compressor.in_boolean_context()) {
compressor.warn("Dropping side-effect-free || [{file}:{line},{col}]", self.start);
return self.left.optimize(compressor);
} else if (rr !== self.right) {
}
} else if (!(rr instanceof AST_Node)) {
if (compressor.in_boolean_context()) {
compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start);
return make_sequence(self, [
self.left,
make_node(AST_True, self)
]).optimize(compressor);
}
} else self.truthy = true;
}
if (self.left.operator == "&&") {
var lr = self.left.right.evaluate(compressor);
if (lr && !(lr instanceof AST_Node)) return make_node(AST_Conditional, self, {
condition: self.left.left,
consequent: self.left.right,
alternative: self.right
}).optimize(compressor);
}
break;
}
@@ -4818,10 +4881,10 @@ merge(Compressor.prototype, {
if (cond !== self.condition) {
if (cond) {
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(), compressor.self(), self.consequent);
} else {
compressor.warn("Condition always false [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor.parent(), self, self.alternative);
return maintain_this_binding(compressor.parent(), compressor.self(), self.alternative);
}
}
var negated = cond.negate(compressor, first_in_statement(compressor));
@@ -4902,7 +4965,7 @@ merge(Compressor.prototype, {
consequent
]).optimize(compressor);
}
var in_bool = compressor.in_boolean_context();
if (is_true(self.consequent)) {
if (is_false(self.alternative)) {
// c ? true : false ---> !!c
@@ -4958,22 +5021,31 @@ merge(Compressor.prototype, {
// AST_True or !0
function is_true(node) {
return node instanceof AST_True
|| in_bool
&& node instanceof AST_Constant
&& node.getValue()
|| (node instanceof AST_UnaryPrefix
&& node.operator == "!"
&& node.expression instanceof AST_Constant
&& !node.expression.value);
&& !node.expression.getValue());
}
// AST_False or !1
function is_false(node) {
return node instanceof AST_False
|| in_bool
&& node instanceof AST_Constant
&& !node.getValue()
|| (node instanceof AST_UnaryPrefix
&& node.operator == "!"
&& node.expression instanceof AST_Constant
&& !!node.expression.value);
&& node.expression.getValue());
}
});
OPT(AST_Boolean, function(self, compressor){
if (compressor.in_boolean_context()) return make_node(AST_Number, self, {
value: +self.value
});
if (compressor.option("booleans")) {
var p = compressor.parent();
if (p instanceof AST_Binary && (p.operator == "=="
@@ -5168,7 +5240,7 @@ merge(Compressor.prototype, {
});
function literals_in_boolean_context(self, compressor) {
if (compressor.option("booleans") && compressor.in_boolean_context()) {
if (compressor.in_boolean_context()) {
return best_of(compressor, self, make_sequence(self, [
self,
make_node(AST_True, self)

View File

@@ -104,7 +104,7 @@ function reserve_quoted_keys(ast, reserved) {
function addStrings(node, add) {
node.walk(new TreeWalker(function(node) {
if (node instanceof AST_Sequence) {
addStrings(node.expressions[node.expressions.length - 1], add);
addStrings(node.tail_node(), add);
} else if (node instanceof AST_String) {
add(node.value);
} else if (node instanceof AST_Conditional) {

View File

@@ -615,6 +615,11 @@ AST_Toplevel.DEFMETHOD("expand_names", function(options) {
}
});
AST_Node.DEFMETHOD("tail_node", return_this);
AST_Sequence.DEFMETHOD("tail_node", function() {
return this.expressions[this.expressions.length - 1];
});
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
options = this._default_mangler_options(options);
try {
@@ -643,7 +648,7 @@ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
skip_string(node.consequent);
skip_string(node.alternative);
} else if (node instanceof AST_Sequence) {
skip_string(node.expressions[node.expressions.length - 1]);
skip_string(node.tail_node());
}
}
});