Merge branch 'master' into harmony-v3.0.25
This commit is contained in:
161
lib/compress.js
161
lib/compress.js
@@ -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: []
|
||||
|
||||
Reference in New Issue
Block a user