Merge branch 'master' into harmony-v3.0.25

This commit is contained in:
alexlamsl
2017-07-16 11:15:07 +08:00
16 changed files with 411 additions and 144 deletions

View File

@@ -151,10 +151,20 @@ merge(Compressor.prototype, {
node.process_expression(true);
}
var passes = +this.options.passes || 1;
for (var pass = 0; pass < passes && pass < 3; ++pass) {
var last_count = 1 / 0;
for (var pass = 0; pass < passes; pass++) {
if (pass > 0 || this.option("reduce_vars"))
node.reset_opt_flags(this, true);
node = node.transform(this);
if (passes > 1) {
var count = 0;
node.walk(new TreeWalker(function() {
count++;
}));
this.info("pass " + pass + ": last_count: " + last_count + ", count: " + count);
if (count >= last_count) break;
last_count = count;
}
}
if (this.option("expression")) {
node.process_expression(false);
@@ -714,6 +724,16 @@ merge(Compressor.prototype, {
return false;
}
function is_undeclared_ref(node) {
return node instanceof AST_SymbolRef && node.definition().undeclared;
}
var global_names = makePredicate("Array Boolean console Error Function Math Number RegExp Object String");
AST_SymbolRef.DEFMETHOD("is_declared", function(compressor) {
return !this.definition().undeclared
|| compressor.option("unsafe") && global_names(this.name);
});
function tighten_body(statements, compressor) {
var CHANGED, max_iter = 10;
do {
@@ -785,7 +805,7 @@ merge(Compressor.prototype, {
|| node instanceof AST_Debugger
|| node instanceof AST_Destructuring
|| node instanceof AST_IterationStatement && !(node instanceof AST_For)
|| node instanceof AST_SymbolRef && node.undeclared()
|| node instanceof AST_SymbolRef && !node.is_declared(compressor)
|| node instanceof AST_Try
|| node instanceof AST_With
|| parent instanceof AST_For && node !== parent.init) {
@@ -817,6 +837,7 @@ merge(Compressor.prototype, {
right: candidate.value
});
}
candidate.write_only = false;
return candidate;
}
// These node types have child nodes that execute sequentially,
@@ -1389,12 +1410,12 @@ merge(Compressor.prototype, {
// returns true if this node may be null, undefined or contain `AST_Accessor`
(function(def) {
AST_Node.DEFMETHOD("may_throw_on_access", function(compressor) {
var pure_getters = compressor.option("pure_getters");
return !pure_getters || this._throw_on_access(pure_getters);
return !compressor.option("pure_getters")
|| this._dot_throw(compressor);
});
function is_strict(pure_getters) {
return /strict/.test(pure_getters);
function is_strict(compressor) {
return /strict/.test(compressor.option("pure_getters"));
}
def(AST_Node, is_strict);
@@ -1402,8 +1423,8 @@ merge(Compressor.prototype, {
def(AST_Undefined, return_true);
def(AST_Constant, return_false);
def(AST_Array, return_false);
def(AST_Object, function(pure_getters) {
if (!is_strict(pure_getters)) return false;
def(AST_Object, function(compressor) {
if (!is_strict(compressor)) return false;
for (var i = this.properties.length; --i >=0;)
if (this.properties[i].value instanceof AST_Accessor) return true;
return false;
@@ -1414,37 +1435,38 @@ merge(Compressor.prototype, {
def(AST_UnaryPrefix, function() {
return this.operator == "void";
});
def(AST_Binary, function(pure_getters) {
def(AST_Binary, function(compressor) {
switch (this.operator) {
case "&&":
return this.left._throw_on_access(pure_getters);
return this.left._dot_throw(compressor);
case "||":
return this.left._throw_on_access(pure_getters)
&& this.right._throw_on_access(pure_getters);
return this.left._dot_throw(compressor)
&& this.right._dot_throw(compressor);
default:
return false;
}
})
def(AST_Assign, function(pure_getters) {
def(AST_Assign, function(compressor) {
return this.operator == "="
&& this.right._throw_on_access(pure_getters);
&& this.right._dot_throw(compressor);
})
def(AST_Conditional, function(pure_getters) {
return this.consequent._throw_on_access(pure_getters)
|| this.alternative._throw_on_access(pure_getters);
def(AST_Conditional, function(compressor) {
return this.consequent._dot_throw(compressor)
|| this.alternative._dot_throw(compressor);
})
def(AST_Sequence, function(pure_getters) {
return this.expressions[this.expressions.length - 1]._throw_on_access(pure_getters);
def(AST_Sequence, function(compressor) {
return this.expressions[this.expressions.length - 1]._dot_throw(compressor);
});
def(AST_SymbolRef, function(pure_getters) {
def(AST_SymbolRef, function(compressor) {
if (this.is_undefined) return true;
if (!is_strict(pure_getters)) return false;
if (!is_strict(compressor)) return false;
if (is_undeclared_ref(this) && this.is_declared(compressor)) return false;
if (this.is_immutable()) return false;
var fixed = this.fixed_value();
return !fixed || fixed._throw_on_access(pure_getters);
return !fixed || fixed._dot_throw(compressor);
});
})(function(node, func) {
node.DEFMETHOD("_throw_on_access", func);
node.DEFMETHOD("_dot_throw", func);
});
/* -----[ boolean/negation helpers ]----- */
@@ -1814,11 +1836,8 @@ merge(Compressor.prototype, {
});
var global_objs = {
Array: Array,
Boolean: Boolean,
Math: Math,
Number: Number,
RegExp: RegExp,
Object: Object,
String: String,
};
function convert_to_predicate(obj) {
@@ -1855,7 +1874,7 @@ merge(Compressor.prototype, {
}
var exp = this.expression;
var val;
if (exp instanceof AST_SymbolRef && exp.undeclared()) {
if (is_undeclared_ref(exp)) {
if (!(static_values[exp.name] || return_false)(key)) return this;
val = global_objs[exp.name];
} else {
@@ -1932,10 +1951,6 @@ merge(Compressor.prototype, {
"isFinite",
"isNaN",
],
Object: [
"keys",
"getOwnPropertyNames",
],
String: [
"fromCharCode",
],
@@ -1951,7 +1966,7 @@ merge(Compressor.prototype, {
}
var val;
var e = exp.expression;
if (e instanceof AST_SymbolRef && e.undeclared()) {
if (is_undeclared_ref(e)) {
if (!(static_fns[e.name] || return_false)(key)) return this;
val = global_objs[e.name];
} else {
@@ -2139,7 +2154,7 @@ merge(Compressor.prototype, {
|| this.expression.has_side_effects(compressor);
});
def(AST_SymbolRef, function(compressor){
return this.undeclared();
return !this.is_declared(compressor);
});
def(AST_SymbolDeclaration, return_false);
def(AST_Object, function(compressor){
@@ -2271,7 +2286,12 @@ merge(Compressor.prototype, {
if (self.uses_eval || self.uses_with) return;
var drop_funcs = !(self instanceof AST_Toplevel) || compressor.toplevel.funcs;
var drop_vars = !(self instanceof AST_Toplevel) || compressor.toplevel.vars;
var assign_as_unused = !/keep_assign/.test(compressor.option("unused"));
var assign_as_unused = /keep_assign/.test(compressor.option("unused")) ? return_false : function(node) {
if (node instanceof AST_Assign && (node.write_only || node.operator == "=")) {
return node.left;
}
if (node instanceof AST_Unary && node.write_only) return node.expression;
};
var in_use = [];
var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use
if (self instanceof AST_Toplevel && compressor.top_retain) {
@@ -2335,13 +2355,9 @@ merge(Compressor.prototype, {
});
return true;
}
if (assign_as_unused
&& node instanceof AST_Assign
&& node.operator == "="
&& node.left instanceof AST_SymbolRef
&& !is_ref_of(node.left, AST_SymbolBlockDeclaration)
&& scope === self) {
node.right.walk(tw);
if (assign_as_unused(node) instanceof AST_SymbolRef && scope === self
&& !is_ref_of(node.left, AST_SymbolBlockDeclaration)) {
if (node instanceof AST_Assign) node.right.walk(tw);
return true;
}
if (node instanceof AST_SymbolRef) {
@@ -2524,15 +2540,18 @@ merge(Compressor.prototype, {
});
}
}
if (assign_as_unused
&& node instanceof AST_Assign
&& node.operator == "="
&& node.left instanceof AST_SymbolRef) {
var def = node.left.definition();
if (!(def.id in in_use_ids)
if (drop_vars) {
var def = assign_as_unused(node);
if (def instanceof AST_SymbolRef
&& !((def = def.definition()).id in in_use_ids)
&& (drop_vars || !def.global)
&& self.variables.get(def.name) === def) {
return maintain_this_binding(parent, node, node.right.transform(tt));
if (node instanceof AST_Assign) {
return maintain_this_binding(parent, node, node.right.transform(tt));
}
return make_node(AST_Number, node, {
value: 0
});
}
}
// certain combination of unused name + side effect leads to:
@@ -2785,7 +2804,10 @@ merge(Compressor.prototype, {
return make_sequence(this, [ left, right ]);
}
});
def(AST_Assign, return_this);
def(AST_Assign, function(compressor){
this.write_only = !this.left.has_side_effects(compressor);
return this;
});
def(AST_Conditional, function(compressor){
var consequent = this.consequent.drop_side_effect_free(compressor);
var alternative = this.alternative.drop_side_effect_free(compressor);
@@ -2806,7 +2828,10 @@ merge(Compressor.prototype, {
return node;
});
def(AST_Unary, function(compressor, first_in_statement){
if (unary_side_effects(this.operator)) return this;
if (unary_side_effects(this.operator)) {
this.write_only = !this.expression.has_side_effects(compressor);
return this;
}
if (this.operator == "typeof" && this.expression instanceof AST_SymbolRef) return null;
var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
if (first_in_statement
@@ -2820,8 +2845,8 @@ merge(Compressor.prototype, {
}
return expression;
});
def(AST_SymbolRef, function() {
return this.undeclared() ? this : null;
def(AST_SymbolRef, function(compressor) {
return this.is_declared(compressor) ? null : this;
});
def(AST_Object, function(compressor, first_in_statement){
var values = trim(this.properties, compressor, first_in_statement);
@@ -3316,7 +3341,7 @@ merge(Compressor.prototype, {
self.args.length = last;
}
if (compressor.option("unsafe")) {
if (exp instanceof AST_SymbolRef && exp.undeclared()) {
if (is_undeclared_ref(exp)) {
switch (exp.name) {
case "Array":
if (self.args.length != 1) {
@@ -3447,8 +3472,7 @@ merge(Compressor.prototype, {
}
}
if (compressor.option("unsafe_Func")
&& exp instanceof AST_SymbolRef
&& exp.undeclared()
&& is_undeclared_ref(exp)
&& exp.name == "Function") {
// new Function() => function(){}
if (self.args.length == 0) return make_node(AST_Function, self, {
@@ -3581,9 +3605,7 @@ merge(Compressor.prototype, {
while (name.expression) {
name = name.expression;
}
if (name instanceof AST_SymbolRef
&& name.name == "console"
&& name.undeclared()) {
if (is_undeclared_ref(name) && name.name == "console") {
return make_node(AST_Undefined, self).optimize(compressor);
}
}
@@ -3604,7 +3626,7 @@ merge(Compressor.prototype, {
OPT(AST_New, function(self, compressor){
if (compressor.option("unsafe")) {
var exp = self.expression;
if (exp instanceof AST_SymbolRef && exp.undeclared()) {
if (is_undeclared_ref(exp)) {
switch (exp.name) {
case "Object":
case "RegExp":
@@ -3681,6 +3703,8 @@ merge(Compressor.prototype, {
operator: car.operator,
expression: left
});
} else {
car.write_only = false;
}
if (parent) {
parent[field] = car;
@@ -3896,7 +3920,7 @@ merge(Compressor.prototype, {
&& self.right instanceof AST_UnaryPrefix
&& self.right.operator == "typeof") {
var expr = self.right.expression;
if (expr instanceof AST_SymbolRef ? !expr.undeclared()
if (expr instanceof AST_SymbolRef ? expr.is_declared(compressor)
: !(expr instanceof AST_PropAccess && compressor.option("ie8"))) {
self.right = expr;
self.left = make_node(AST_Undefined, self.left).optimize(compressor);
@@ -4226,7 +4250,7 @@ merge(Compressor.prototype, {
}
// testing against !self.scope.uses_with first is an optimization
if (!compressor.option("ie8")
&& self.undeclared()
&& is_undeclared_ref(self)
&& (!self.scope.uses_with || !compressor.find_parent(AST_With))) {
switch (self.name) {
case "undefined":
@@ -4598,7 +4622,7 @@ merge(Compressor.prototype, {
var prop = self.property;
if (prop instanceof AST_String && compressor.option("properties")) {
prop = prop.getValue();
if (RESERVED_WORDS(prop) ? !compressor.option("ie8") : is_identifier_string(prop)) {
if (is_identifier_string(prop)) {
return make_node(AST_Dot, self, {
expression : self.expression,
property : prop
@@ -4635,20 +4659,11 @@ merge(Compressor.prototype, {
if (def) {
return def.optimize(compressor);
}
var prop = self.property;
if (RESERVED_WORDS(prop) && compressor.option("ie8")) {
return make_node(AST_Sub, self, {
expression : self.expression,
property : make_node(AST_String, self, {
value: prop
})
}).optimize(compressor);
}
if (compressor.option("unsafe") && self.expression instanceof AST_Object) {
var values = self.expression.properties;
for (var i = values.length; --i >= 0;) {
var key = values[i].key;
if ((key instanceof AST_SymbolMethod ? key.name : key) === prop) {
if ((key instanceof AST_SymbolMethod ? key.name : key) === self.property) {
var value = values[i].value;
if (key instanceof AST_SymbolMethod) {
if (values[i].is_generator) break;
@@ -4667,7 +4682,7 @@ merge(Compressor.prototype, {
&& self.expression instanceof AST_Dot
&& self.expression.property == "prototype") {
var exp = self.expression.expression;
if (exp instanceof AST_SymbolRef && exp.undeclared()) switch (exp.name) {
if (is_undeclared_ref(exp)) switch (exp.name) {
case "Array":
self.expression = make_node(AST_Array, self.expression, {
elements: []