Merge branch 'master' into harmony-v2.8.22

This commit is contained in:
alexlamsl
2017-04-09 11:48:47 +08:00
21 changed files with 1331 additions and 245 deletions

View File

@@ -72,7 +72,7 @@ function Compressor(options, false_by_default) {
negate_iife : !false_by_default,
passes : 1,
properties : !false_by_default,
pure_getters : false,
pure_getters : !false_by_default && "strict",
pure_funcs : null,
reduce_vars : !false_by_default,
screw_ie8 : true,
@@ -252,9 +252,7 @@ merge(Compressor.prototype, {
AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan){
var reduce_vars = rescan && compressor.option("reduce_vars");
var toplevel = compressor.option("toplevel");
var ie8 = !compressor.option("screw_ie8");
var safe_ids = [];
push();
var safe_ids = Object.create(null);
var suppressor = new TreeWalker(function(node) {
if (node instanceof AST_Symbol) {
var d = node.definition();
@@ -263,10 +261,8 @@ merge(Compressor.prototype, {
}
});
var tw = new TreeWalker(function(node, descend){
if (!(node instanceof AST_Directive || node instanceof AST_Constant)) {
node._squeezed = false;
node._optimized = false;
}
node._squeezed = false;
node._optimized = false;
if (reduce_vars) {
if (node instanceof AST_Toplevel) node.globals.each(reset_def);
if (node instanceof AST_Scope) node.variables.each(reset_def);
@@ -274,11 +270,11 @@ merge(Compressor.prototype, {
var d = node.definition();
d.references.push(node);
if (d.fixed === undefined || !is_safe(d)
|| is_modified(node, 0, d.fixed instanceof AST_Lambda)) {
|| is_modified(node, 0, node.fixed_value() instanceof AST_Lambda)) {
d.fixed = false;
}
}
if (ie8 && node instanceof AST_SymbolCatch) {
if (node instanceof AST_SymbolCatch) {
node.definition().fixed = false;
}
if (node instanceof AST_VarDef) {
@@ -287,8 +283,17 @@ merge(Compressor.prototype, {
} else {
var d = node.name.definition();
if (d.fixed == null) {
d.fixed = node.value;
mark_as_safe(d);
if (node.value) {
d.fixed = function() {
return node.value;
};
mark(d, false);
descend();
} else {
d.fixed = null;
}
mark(d, true);
return true;
} else if (node.value) {
d.fixed = false;
}
@@ -300,11 +305,10 @@ merge(Compressor.prototype, {
d.fixed = false;
} else {
d.fixed = node;
mark_as_safe(d);
mark(d, true);
}
var save_ids = safe_ids;
safe_ids = [];
push();
safe_ids = Object.create(null);
descend();
safe_ids = save_ids;
return true;
@@ -319,8 +323,10 @@ merge(Compressor.prototype, {
// So existing transformation rules can work on them.
node.argnames.forEach(function(arg, i) {
var d = arg.definition();
d.fixed = iife.args[i] || make_node(AST_Undefined, iife);
mark_as_safe(d);
d.fixed = function() {
return iife.args[i] || make_node(AST_Undefined, iife);
};
mark(d, true);
});
}
if (node instanceof AST_If || node instanceof AST_DWLoop) {
@@ -368,29 +374,27 @@ merge(Compressor.prototype, {
});
this.walk(tw);
function mark_as_safe(def) {
safe_ids[safe_ids.length - 1][def.id] = true;
function mark(def, safe) {
safe_ids[def.id] = safe;
}
function is_safe(def) {
for (var i = safe_ids.length, id = def.id; --i >= 0;) {
if (safe_ids[i][id]) {
if (def.fixed == null) {
var orig = def.orig[0];
if (orig instanceof AST_SymbolFunarg || orig.name == "arguments") return false;
def.fixed = make_node(AST_Undefined, orig);
}
return true;
if (safe_ids[def.id]) {
if (def.fixed == null) {
var orig = def.orig[0];
if (orig instanceof AST_SymbolFunarg || orig.name == "arguments") return false;
def.fixed = make_node(AST_Undefined, orig);
}
return true;
}
}
function push() {
safe_ids.push(Object.create(null));
safe_ids = Object.create(safe_ids);
}
function pop() {
safe_ids.pop();
safe_ids = Object.getPrototypeOf(safe_ids);
}
function reset_def(def) {
@@ -405,7 +409,7 @@ merge(Compressor.prototype, {
function is_modified(node, level, func) {
var parent = tw.parent(level);
if (isLHS(node, parent)
if (is_lhs(node, parent)
|| !func && parent instanceof AST_Call && parent.expression === node) {
return true;
} else if (parent instanceof AST_PropAccess && parent.expression === node) {
@@ -414,6 +418,12 @@ merge(Compressor.prototype, {
}
});
AST_SymbolRef.DEFMETHOD("fixed_value", function() {
var fixed = this.definition().fixed;
if (!fixed || fixed instanceof AST_Node) return fixed;
return fixed();
});
function find_variable(compressor, name) {
var scope, i = 0;
while (scope = compressor.parent(i++)) {
@@ -474,15 +484,15 @@ merge(Compressor.prototype, {
// func(something) because that changes the meaning of
// the func (becomes lexical instead of global).
function maintain_this_binding(parent, orig, val) {
if (parent instanceof AST_Call && parent.expression === orig) {
if (val instanceof AST_PropAccess || val instanceof AST_SymbolRef && val.name === "eval") {
return make_node(AST_Seq, orig, {
car: make_node(AST_Number, orig, {
value: 0
}),
cdr: val
});
}
if (parent instanceof AST_UnaryPrefix && parent.operator == "delete"
|| parent instanceof AST_Call && parent.expression === orig
&& (val instanceof AST_PropAccess || val instanceof AST_SymbolRef && val.name == "eval")) {
return make_node(AST_Seq, orig, {
car: make_node(AST_Number, orig, {
value: 0
}),
cdr: val
});
}
return val;
}
@@ -702,7 +712,7 @@ merge(Compressor.prototype, {
return statements;
function is_lvalue(node, parent) {
return node instanceof AST_SymbolRef && isLHS(node, parent);
return node instanceof AST_SymbolRef && is_lhs(node, parent);
}
function replace_var(node, parent, is_constant) {
if (is_lvalue(node, parent)) return node;
@@ -916,7 +926,7 @@ merge(Compressor.prototype, {
}
var ab = aborts(stat.body);
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null;
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
|| (ab instanceof AST_Continue && self === loop_body(lct))
|| (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
@@ -938,7 +948,7 @@ merge(Compressor.prototype, {
}
var ab = aborts(stat.alternative);
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null;
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
|| (ab instanceof AST_Continue && self === loop_body(lct))
|| (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
@@ -987,7 +997,7 @@ merge(Compressor.prototype, {
extract_declarations_from_unreachable_code(compressor, stat, a);
} else {
if (stat instanceof AST_LoopControl) {
var lct = compressor.loopcontrol_target(stat.label);
var lct = compressor.loopcontrol_target(stat);
if ((stat instanceof AST_Break
&& !(lct instanceof AST_IterationStatement)
&& loop_body(lct) === self) || (stat instanceof AST_Continue
@@ -1173,6 +1183,61 @@ merge(Compressor.prototype, {
&& !node.expression.has_side_effects(compressor);
}
// may_eq_null()
// returns true if this node may evaluate to null or undefined
(function(def) {
AST_Node.DEFMETHOD("may_eq_null", function(compressor) {
var pure_getters = compressor.option("pure_getters");
return !pure_getters || this._eq_null(pure_getters);
});
function is_strict(pure_getters) {
return /strict/.test(pure_getters);
}
def(AST_Node, is_strict);
def(AST_Null, return_true);
def(AST_Undefined, return_true);
def(AST_Constant, return_false);
def(AST_Array, return_false);
def(AST_Object, return_false);
def(AST_Function, return_false);
def(AST_UnaryPostfix, return_false);
def(AST_UnaryPrefix, function() {
return this.operator == "void";
});
def(AST_Binary, function(pure_getters) {
switch (this.operator) {
case "&&":
return this.left._eq_null(pure_getters);
case "||":
return this.left._eq_null(pure_getters)
&& this.right._eq_null(pure_getters);
default:
return false;
}
})
def(AST_Assign, function(pure_getters) {
return this.operator == "="
&& this.right._eq_null(pure_getters);
})
def(AST_Conditional, function(pure_getters) {
return this.consequent._eq_null(pure_getters)
|| this.alternative._eq_null(pure_getters);
})
def(AST_Seq, function(pure_getters) {
return this.cdr._eq_null(pure_getters);
});
def(AST_SymbolRef, function(pure_getters) {
if (this.is_undefined) return true;
if (!is_strict(pure_getters)) return false;
var fixed = this.fixed_value();
return !fixed || fixed._eq_null(pure_getters);
});
})(function(node, func) {
node.DEFMETHOD("_eq_null", func);
});
/* -----[ boolean/negation helpers ]----- */
// methods to determine whether an expression has a boolean result type
@@ -1260,9 +1325,9 @@ merge(Compressor.prototype, {
var unary_side_effects = makePredicate("delete ++ --");
function isLHS(node, parent) {
return parent instanceof AST_Unary && unary_side_effects(parent.operator)
|| parent instanceof AST_Assign && parent.left === node;
function is_lhs(node, parent) {
if (parent instanceof AST_Unary && unary_side_effects(parent.operator)) return parent.expression;
if (parent instanceof AST_Assign && parent.left === node) return node;
}
(function (def){
@@ -1275,7 +1340,7 @@ merge(Compressor.prototype, {
node = parent;
parent = compressor.parent(level++);
} while (parent instanceof AST_PropAccess && parent.expression === node);
if (isLHS(node, parent)) {
if (is_lhs(node, parent)) {
compressor.warn('global_defs ' + this.print_to_string() + ' redefined [{file}:{line},{col}]', this.start);
} else {
return def;
@@ -1305,7 +1370,7 @@ merge(Compressor.prototype, {
}
def(AST_Node, noop);
def(AST_Dot, function(compressor, suffix){
return this.expression._find_defs(compressor, suffix + "." + this.property);
return this.expression._find_defs(compressor, "." + this.property + suffix);
});
def(AST_SymbolRef, function(compressor, suffix){
if (!this.global()) return;
@@ -1525,15 +1590,15 @@ merge(Compressor.prototype, {
if (this._evaluating) throw def;
this._evaluating = true;
try {
var d = this.definition();
if (compressor.option("reduce_vars") && d.fixed) {
var fixed = this.fixed_value();
if (compressor.option("reduce_vars") && fixed) {
if (compressor.option("unsafe")) {
if (!HOP(d.fixed, "_evaluated")) {
d.fixed._evaluated = ev(d.fixed, compressor);
if (!HOP(fixed, "_evaluated")) {
fixed._evaluated = ev(fixed, compressor);
}
return d.fixed._evaluated;
return fixed._evaluated;
}
return ev(d.fixed, compressor);
return ev(fixed, compressor);
}
} finally {
this._evaluating = false;
@@ -1718,7 +1783,7 @@ merge(Compressor.prototype, {
|| this.expression.has_side_effects(compressor);
});
def(AST_SymbolRef, function(compressor){
return this.global() && this.undeclared();
return this.undeclared();
});
def(AST_Object, function(compressor){
return any(this.properties, compressor);
@@ -1733,17 +1798,14 @@ merge(Compressor.prototype, {
return any(this.elements, compressor);
});
def(AST_Dot, function(compressor){
if (!compressor.option("pure_getters")) return true;
return this.expression.has_side_effects(compressor);
return this.expression.may_eq_null(compressor)
|| this.expression.has_side_effects(compressor);
});
def(AST_Sub, function(compressor){
if (!compressor.option("pure_getters")) return true;
return this.expression.has_side_effects(compressor)
return this.expression.may_eq_null(compressor)
|| this.expression.has_side_effects(compressor)
|| this.property.has_side_effects(compressor);
});
def(AST_PropAccess, function(compressor){
return !compressor.option("pure_getters");
});
def(AST_Seq, function(compressor){
return this.car.has_side_effects(compressor)
|| this.cdr.has_side_effects(compressor);
@@ -1790,7 +1852,7 @@ merge(Compressor.prototype, {
OPT(AST_LabeledStatement, function(self, compressor){
if (self.body instanceof AST_Break
&& compressor.loopcontrol_target(self.body.label) === self.body) {
&& compressor.loopcontrol_target(self.body) === self.body) {
return make_node(AST_EmptyStatement, self);
}
return self.label.references.length == 0 ? self.body : self;
@@ -2388,11 +2450,11 @@ merge(Compressor.prototype, {
return values && AST_Seq.from_array(values);
});
def(AST_Dot, function(compressor, first_in_statement){
if (!compressor.option("pure_getters")) return this;
if (this.expression.may_eq_null(compressor)) return this;
return this.expression.drop_side_effect_free(compressor, first_in_statement);
});
def(AST_Sub, function(compressor, first_in_statement){
if (!compressor.option("pure_getters")) return this;
if (this.expression.may_eq_null(compressor)) return this;
var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
if (!expression) return this.property.drop_side_effect_free(compressor, first_in_statement);
var property = this.property.drop_side_effect_free(compressor);
@@ -2438,13 +2500,21 @@ merge(Compressor.prototype, {
return make_node(AST_For, self, {
body: self.body
});
} else if (compressor.option("dead_code") && self instanceof AST_While) {
}
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 });
} else {
cond = make_node_from_constant(cond, self.condition).transform(compressor);
self.condition = best_of_expression(cond, self.condition);
}
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;
});
self.walk(tw);
if (!has_loop_control) return self.body;
}
}
if (self instanceof AST_While) {
@@ -2470,7 +2540,7 @@ merge(Compressor.prototype, {
var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body;
if (first instanceof AST_If) {
if (first.body instanceof AST_Break
&& compressor.loopcontrol_target(first.body.label) === compressor.self()) {
&& compressor.loopcontrol_target(first.body) === compressor.self()) {
if (self.condition) {
self.condition = make_node(AST_Binary, self.condition, {
left: self.condition,
@@ -2483,7 +2553,7 @@ merge(Compressor.prototype, {
drop_it(first.alternative);
}
else if (first.alternative instanceof AST_Break
&& compressor.loopcontrol_target(first.alternative.label) === compressor.self()) {
&& compressor.loopcontrol_target(first.alternative) === compressor.self()) {
if (self.condition) {
self.condition = make_node(AST_Binary, self.condition, {
left: self.condition,
@@ -2714,7 +2784,7 @@ merge(Compressor.prototype, {
self.body = body;
while (branch = body[body.length - 1]) {
var stat = branch.body[branch.body.length - 1];
if (stat instanceof AST_Break && compressor.loopcontrol_target(stat.label) === self)
if (stat instanceof AST_Break && compressor.loopcontrol_target(stat) === self)
branch.body.pop();
if (branch.body.length || branch instanceof AST_Case
&& (default_branch || branch.expression.has_side_effects(compressor))) break;
@@ -2733,7 +2803,7 @@ merge(Compressor.prototype, {
if (has_break
|| node instanceof AST_Lambda
|| node instanceof AST_SimpleStatement) return true;
if (node instanceof AST_Break && tw.loopcontrol_target(node.label) === self)
if (node instanceof AST_Break && tw.loopcontrol_target(node) === self)
has_break = true;
});
self.walk(tw);
@@ -2819,11 +2889,12 @@ merge(Compressor.prototype, {
if (compressor.option("reduce_vars")
&& exp instanceof AST_SymbolRef) {
var def = exp.definition();
if (def.fixed instanceof AST_Defun) {
def.fixed = make_node(AST_Function, def.fixed, def.fixed).clone(true);
var fixed = exp.fixed_value();
if (fixed instanceof AST_Defun) {
def.fixed = fixed = make_node(AST_Function, fixed, fixed).clone(true);
}
if (def.fixed instanceof AST_Function) {
exp = def.fixed;
if (fixed instanceof AST_Function) {
exp = fixed;
if (compressor.option("unused")
&& def.references.length == 1
&& !(def.scope.uses_arguments
@@ -3158,8 +3229,9 @@ merge(Compressor.prototype, {
if (this.expression instanceof AST_Seq) {
var seq = this.expression;
var x = seq.to_array();
this.expression = x.pop();
x.push(this);
var e = this.clone();
e.expression = x.pop();
x.push(e);
seq = AST_Seq.from_array(x).transform(compressor);
return seq;
}
@@ -3172,11 +3244,27 @@ merge(Compressor.prototype, {
});
OPT(AST_UnaryPrefix, function(self, compressor){
var e = self.expression;
if (self.operator == "delete"
&& !(e instanceof AST_SymbolRef
|| e instanceof AST_PropAccess
|| e instanceof AST_NaN
|| e instanceof AST_Infinity
|| e instanceof AST_Undefined)) {
if (e instanceof AST_Seq) {
e = e.to_array();
e.push(make_node(AST_True, self));
return AST_Seq.from_array(e).optimize(compressor);
}
return make_node(AST_Seq, self, {
car: e,
cdr: make_node(AST_True, self)
}).optimize(compressor);
}
var seq = self.lift_sequences(compressor);
if (seq !== self) {
return seq;
}
var e = self.expression;
if (compressor.option("side_effects") && self.operator == "void") {
e = e.drop_side_effect_free(compressor);
if (e) {
@@ -3213,9 +3301,14 @@ merge(Compressor.prototype, {
if (e instanceof AST_Binary
&& (self.operator == "+" || self.operator == "-")
&& (e.operator == "*" || e.operator == "/" || e.operator == "%")) {
self.expression = e.left;
e.left = self;
return e.optimize(compressor);
return make_node(AST_Binary, self, {
operator: e.operator,
left: make_node(AST_UnaryPrefix, e.left, {
operator: self.operator,
expression: e.left
}),
right: e.right
});
}
// avoids infinite recursion of numerals
if (self.operator != "-"
@@ -3234,23 +3327,25 @@ merge(Compressor.prototype, {
if (this.left instanceof AST_Seq) {
var seq = this.left;
var x = seq.to_array();
this.left = x.pop();
x.push(this);
var e = this.clone();
e.left = x.pop();
x.push(e);
return AST_Seq.from_array(x).optimize(compressor);
}
if (this.right instanceof AST_Seq && !this.left.has_side_effects(compressor)) {
var assign = this.operator == "=" && this.left instanceof AST_SymbolRef;
var root = this.right;
var root = this.right.clone();
var cursor, seq = root;
while (assign || !seq.car.has_side_effects(compressor)) {
cursor = seq;
if (seq.cdr instanceof AST_Seq) {
seq = seq.cdr;
seq = seq.cdr = seq.cdr.clone();
} else break;
}
if (cursor) {
this.right = cursor.cdr;
cursor.cdr = this;
var e = this.clone();
e.right = cursor.cdr;
cursor.cdr = e;
return root.optimize(compressor);
}
}
@@ -3628,12 +3723,11 @@ merge(Compressor.prototype, {
OPT(AST_SymbolRef, function(self, compressor){
var def = self.resolve_defines(compressor);
if (def) {
return def;
return def.optimize(compressor);
}
// testing against !self.scope.uses_with first is an optimization
if (compressor.option("screw_ie8")
&& self.undeclared()
&& !isLHS(self, compressor.parent())
&& (!self.scope.uses_with || !compressor.find_parent(AST_With))) {
switch (self.name) {
case "undefined":
@@ -3646,13 +3740,13 @@ merge(Compressor.prototype, {
}
if (compressor.option("evaluate") && compressor.option("reduce_vars")) {
var d = self.definition();
if (d.fixed) {
var fixed = self.fixed_value();
if (fixed) {
if (d.should_replace === undefined) {
var init = d.fixed.evaluate(compressor);
if (init !== d.fixed) {
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 init = fixed.evaluate(compressor);
if (init !== fixed) {
init = make_node_from_constant(init, fixed);
var value = best_of_expression(init.optimize(compressor), fixed).print_to_string().length;
var name = d.name.length;
var freq = d.references.length;
var overhead = d.global || !freq ? 0 : (name + 2 + value) / freq;
@@ -3662,13 +3756,17 @@ merge(Compressor.prototype, {
}
}
if (d.should_replace) {
return d.should_replace.clone(true);
return best_of_expression(d.should_replace.optimize(compressor), fixed).clone(true);
}
}
}
return self;
});
function is_atomic(lhs, self) {
return lhs instanceof AST_SymbolRef || lhs.TYPE === self.TYPE;
}
OPT(AST_Undefined, function(self, compressor){
if (compressor.option("unsafe")) {
var undef = find_variable(compressor, "undefined");
@@ -3682,6 +3780,8 @@ merge(Compressor.prototype, {
return ref;
}
}
var lhs = is_lhs(compressor.self(), compressor.parent());
if (lhs && is_atomic(lhs, self)) return self;
return make_node(AST_UnaryPrefix, self, {
operator: "void",
expression: make_node(AST_Number, self, {
@@ -3691,8 +3791,13 @@ merge(Compressor.prototype, {
});
OPT(AST_Infinity, function(self, compressor){
var retain = compressor.option("keep_infinity") && !find_variable(compressor, "Infinity");
return retain ? self : make_node(AST_Binary, self, {
var lhs = is_lhs(compressor.self(), compressor.parent());
if (lhs && is_atomic(lhs, self)) return self;
if (compressor.option("keep_infinity")
&& !(lhs && !is_atomic(lhs, self))
&& !find_variable(compressor, "Infinity"))
return self;
return make_node(AST_Binary, self, {
operator: "/",
left: make_node(AST_Number, self, {
value: 1
@@ -3704,15 +3809,20 @@ merge(Compressor.prototype, {
});
OPT(AST_NaN, function(self, compressor){
return find_variable(compressor, "NaN") ? make_node(AST_Binary, self, {
operator: "/",
left: make_node(AST_Number, self, {
value: 0
}),
right: make_node(AST_Number, self, {
value: 0
})
}) : self;
var lhs = is_lhs(compressor.self(), compressor.parent());
if (lhs && !is_atomic(lhs, self)
|| find_variable(compressor, "NaN")) {
return make_node(AST_Binary, self, {
operator: "/",
left: make_node(AST_Number, self, {
value: 0
}),
right: make_node(AST_Number, self, {
value: 0
})
});
}
return self;
});
var ASSIGN_OPS = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ];
@@ -3975,7 +4085,7 @@ merge(Compressor.prototype, {
OPT(AST_Dot, function(self, compressor){
var def = self.resolve_defines(compressor);
if (def) {
return def;
return def.optimize(compressor);
}
var prop = self.property;
if (RESERVED_WORDS(prop) && !compressor.option("screw_ie8")) {