Merge branch 'master' into harmony-v3.1.7
This commit is contained in:
190
lib/compress.js
190
lib/compress.js
@@ -291,7 +291,7 @@ merge(Compressor.prototype, {
|
||||
self.transform(tt);
|
||||
});
|
||||
|
||||
AST_Node.DEFMETHOD("reset_opt_flags", function(compressor) {
|
||||
AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) {
|
||||
var reduce_vars = compressor.option("reduce_vars");
|
||||
var unused = compressor.option("unused");
|
||||
// Stack of look-up tables to keep track of whether a `SymbolDef` has been
|
||||
@@ -322,12 +322,18 @@ merge(Compressor.prototype, {
|
||||
d.fixed = false;
|
||||
} else if (d.fixed) {
|
||||
var value = node.fixed_value();
|
||||
if (unused && !compressor.exposed(d)) {
|
||||
d.single_use = value
|
||||
&& d.references.length == 1
|
||||
&& loop_ids[d.id] === in_loop
|
||||
&& d.scope === node.scope
|
||||
&& value.is_constant_expression();
|
||||
if (unused && !compressor.exposed(d) && value && d.references.length == 1) {
|
||||
if (value instanceof AST_Lambda) {
|
||||
d.single_use = d.scope === node.scope
|
||||
&& !(d.orig[0] instanceof AST_SymbolFunarg)
|
||||
|| value.is_constant_expression(node.scope);
|
||||
} else {
|
||||
d.single_use = d.scope === node.scope
|
||||
&& loop_ids[d.id] === in_loop
|
||||
&& value.is_constant_expression();
|
||||
}
|
||||
} else {
|
||||
d.single_use = false;
|
||||
}
|
||||
if (is_modified(node, value, 0, is_immutable(value))) {
|
||||
if (d.single_use) {
|
||||
@@ -390,6 +396,10 @@ merge(Compressor.prototype, {
|
||||
} else {
|
||||
d.fixed = node;
|
||||
mark(d, true);
|
||||
if (unused && d.references.length == 1) {
|
||||
d.single_use = d.scope === d.references[0].scope
|
||||
|| node.is_constant_expression(d.references[0].scope);
|
||||
}
|
||||
}
|
||||
var save_ids = safe_ids;
|
||||
safe_ids = Object.create(null);
|
||||
@@ -542,6 +552,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return def.fixed instanceof AST_Defun;
|
||||
}
|
||||
|
||||
function safe_to_assign(def, value) {
|
||||
@@ -580,7 +591,10 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
|
||||
function is_immutable(value) {
|
||||
return value && (value.is_constant() || value instanceof AST_Lambda);
|
||||
if (!value) return false;
|
||||
return value.is_constant()
|
||||
|| value instanceof AST_Lambda
|
||||
|| value instanceof AST_This;
|
||||
}
|
||||
|
||||
function read_property(obj, key) {
|
||||
@@ -608,7 +622,7 @@ merge(Compressor.prototype, {
|
||||
|| !immutable
|
||||
&& parent instanceof AST_Call
|
||||
&& parent.expression === node
|
||||
&& (!(value instanceof AST_Function) || value.contains_this())) {
|
||||
&& (!(value instanceof AST_Function) || value.contains_this(parent))) {
|
||||
return true;
|
||||
} else if (parent instanceof AST_Array || parent instanceof AST_Object) {
|
||||
return is_modified(parent, parent, level + 1);
|
||||
@@ -848,6 +862,7 @@ merge(Compressor.prototype, {
|
||||
function collapse(statements, compressor) {
|
||||
var scope = compressor.find_parent(AST_Scope).get_defun_scope();
|
||||
if (scope.uses_eval || scope.uses_with) return statements;
|
||||
var args;
|
||||
var candidates = [];
|
||||
var stat_index = statements.length;
|
||||
while (--stat_index >= 0) {
|
||||
@@ -868,7 +883,7 @@ merge(Compressor.prototype, {
|
||||
var one_off = lhs instanceof AST_Symbol && lhs.definition().references.length == 1;
|
||||
var side_effects = value_has_side_effects(candidate);
|
||||
var hit = candidate.name instanceof AST_SymbolFunarg;
|
||||
var abort = false, replaced = false;
|
||||
var abort = false, replaced = false, can_replace = !args || !hit;
|
||||
var tt = new TreeTransformer(function(node, descend) {
|
||||
if (abort) return node;
|
||||
// Skip nodes before `candidate` as quickly as possible
|
||||
@@ -895,7 +910,8 @@ merge(Compressor.prototype, {
|
||||
return node;
|
||||
}
|
||||
// Replace variable with assignment when found
|
||||
if (!(node instanceof AST_SymbolDeclaration)
|
||||
if (can_replace
|
||||
&& !(node instanceof AST_SymbolDeclaration)
|
||||
&& !is_lhs(node, parent)
|
||||
&& lhs.equivalent_to(node)) {
|
||||
CHANGED = replaced = abort = true;
|
||||
@@ -946,6 +962,12 @@ merge(Compressor.prototype, {
|
||||
// Skip (non-executed) functions and (leading) default case in switch statements
|
||||
if (node instanceof AST_Default || node instanceof AST_Scope) return node;
|
||||
});
|
||||
if (!can_replace) {
|
||||
for (var j = compressor.self().argnames.lastIndexOf(candidate.__name || candidate.name) + 1; j < args.length; j++) {
|
||||
args[j].transform(tt);
|
||||
}
|
||||
can_replace = true;
|
||||
}
|
||||
for (var i = stat_index; !abort && i < statements.length; i++) {
|
||||
statements[i].transform(tt);
|
||||
}
|
||||
@@ -991,9 +1013,16 @@ merge(Compressor.prototype, {
|
||||
})) {
|
||||
var fn_strict = compressor.has_directive("use strict");
|
||||
if (fn_strict && fn.body.indexOf(fn_strict) < 0) fn_strict = false;
|
||||
var len = fn.argnames.length;
|
||||
args = iife.args.slice(len);
|
||||
var names = Object.create(null);
|
||||
for (var i = fn.argnames.length; --i >= 0;) {
|
||||
for (var i = len; --i >= 0;) {
|
||||
var sym = fn.argnames[i];
|
||||
var arg = iife.args[i];
|
||||
args.unshift(make_node(AST_VarDef, sym, {
|
||||
name: sym,
|
||||
value: arg
|
||||
}));
|
||||
if (sym.name in names) continue;
|
||||
names[sym.name] = true;
|
||||
if (sym instanceof AST_Expansion) {
|
||||
@@ -1007,9 +1036,9 @@ merge(Compressor.prototype, {
|
||||
elements: elements
|
||||
})
|
||||
}));
|
||||
candidates[0].__name = sym;
|
||||
}
|
||||
} else {
|
||||
var arg = iife.args[i];
|
||||
if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor);
|
||||
else if (has_overlapping_symbol(fn, arg, fn_strict)) arg = null;
|
||||
if (arg) candidates.unshift(make_node(AST_VarDef, sym, {
|
||||
@@ -2265,18 +2294,22 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
def(AST_Node, return_false);
|
||||
def(AST_Constant, return_true);
|
||||
def(AST_Function, function(){
|
||||
def(AST_Lambda, function(scope){
|
||||
var self = this;
|
||||
var result = true;
|
||||
self.walk(new TreeWalker(function(node) {
|
||||
if (!result) return true;
|
||||
if (node instanceof AST_SymbolRef) {
|
||||
var def = node.definition();
|
||||
if (self.enclosed.indexOf(def) >= 0
|
||||
&& self.variables.get(def.name) !== def) {
|
||||
if (member(def, self.enclosed)
|
||||
&& !self.variables.has(def.name)) {
|
||||
if (scope) {
|
||||
var scope_def = scope.find_variable(node);
|
||||
if (def.undeclared ? !scope_def : scope_def === def) return true;
|
||||
}
|
||||
result = false;
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}));
|
||||
return result;
|
||||
@@ -2439,8 +2472,11 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
return true;
|
||||
}
|
||||
if (assign_as_unused(node) instanceof AST_SymbolRef && scope === self
|
||||
&& !is_ref_of(node.left, AST_SymbolBlockDeclaration)) {
|
||||
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;
|
||||
}
|
||||
@@ -2526,9 +2562,11 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
if ((node instanceof AST_Defun || node instanceof AST_DefClass) && node !== self) {
|
||||
var keep = (node.name.definition().id in in_use_ids) || !drop_funcs && node.name.definition().global;
|
||||
var def = node.name.definition();
|
||||
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, node.name);
|
||||
return make_node(AST_EmptyStatement, node);
|
||||
}
|
||||
return node;
|
||||
@@ -2553,7 +2591,7 @@ merge(Compressor.prototype, {
|
||||
if (var_defs.length > 1 && !def.value) {
|
||||
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
|
||||
remove(var_defs, def);
|
||||
remove(sym.orig, def.name);
|
||||
drop_decl(sym, def.name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -2586,7 +2624,7 @@ merge(Compressor.prototype, {
|
||||
} else {
|
||||
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
|
||||
}
|
||||
remove(sym.orig, def.name);
|
||||
drop_decl(sym, def.name);
|
||||
}
|
||||
});
|
||||
if (head.length == 0 && tail.length == 1 && tail[0].name instanceof AST_SymbolVar) {
|
||||
@@ -2595,7 +2633,7 @@ merge(Compressor.prototype, {
|
||||
var def = tail.pop();
|
||||
compressor.warn("Converting duplicated definition of variable {name} to assignment [{file}:{line},{col}]", template(def.name));
|
||||
remove(var_defs, def);
|
||||
remove(def.name.definition().orig, def.name);
|
||||
drop_decl(def.name.definition(), def.name);
|
||||
side_effects.unshift(make_node(AST_Assign, def, {
|
||||
operator: "=",
|
||||
left: make_node(AST_SymbolRef, def.name, def.name),
|
||||
@@ -2627,8 +2665,7 @@ merge(Compressor.prototype, {
|
||||
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) {
|
||||
&& (drop_vars || !def.global)) {
|
||||
if (node instanceof AST_Assign) {
|
||||
return maintain_this_binding(parent, node, node.right.transform(tt));
|
||||
}
|
||||
@@ -2685,6 +2722,14 @@ merge(Compressor.prototype, {
|
||||
col : sym.start.col
|
||||
};
|
||||
}
|
||||
|
||||
function drop_decl(def, decl) {
|
||||
remove(def.orig, decl);
|
||||
if (!def.orig.length) {
|
||||
def.scope.functions.del(def.name);
|
||||
def.scope.variables.del(def.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
self.transform(tt);
|
||||
@@ -4456,46 +4501,53 @@ merge(Compressor.prototype, {
|
||||
if (compressor.option("unused")
|
||||
&& fixed
|
||||
&& d.references.length == 1
|
||||
&& (d.single_use || is_func_expr(fixed)
|
||||
&& !(d.scope.uses_arguments && d.orig[0] instanceof AST_SymbolFunarg)
|
||||
&& !d.scope.uses_eval
|
||||
&& compressor.find_parent(AST_Scope) === fixed.parent_scope)) {
|
||||
&& d.single_use) {
|
||||
var value = fixed.optimize(compressor);
|
||||
return value === fixed ? fixed.clone(true) : value;
|
||||
}
|
||||
if (compressor.option("evaluate") && fixed) {
|
||||
if (d.should_replace === undefined) {
|
||||
var init = fixed.evaluate(compressor);
|
||||
if (init !== fixed && (compressor.option("unsafe_regexp") || !(init instanceof RegExp))) {
|
||||
init = make_node_from_constant(init, fixed);
|
||||
var value_length = init.optimize(compressor).print_to_string().length;
|
||||
var fn;
|
||||
if (has_symbol_ref(fixed)) {
|
||||
fn = function() {
|
||||
var result = init.optimize(compressor);
|
||||
return result === init ? result.clone(true) : result;
|
||||
};
|
||||
} else {
|
||||
value_length = Math.min(value_length, fixed.print_to_string().length);
|
||||
fn = function() {
|
||||
var result = best_of_expression(init.optimize(compressor), fixed);
|
||||
return result === init || result === fixed ? result.clone(true) : result;
|
||||
};
|
||||
}
|
||||
var name_length = d.name.length;
|
||||
var overhead = 0;
|
||||
if (compressor.option("unused") && !compressor.exposed(d)) {
|
||||
overhead = (name_length + 2 + value_length) / d.references.length;
|
||||
}
|
||||
d.should_replace = value_length <= name_length + overhead ? fn : false;
|
||||
} else {
|
||||
d.should_replace = false;
|
||||
if (fixed && d.should_replace === undefined) {
|
||||
var init;
|
||||
if (fixed instanceof AST_This) {
|
||||
if (!(d.orig[0] instanceof AST_SymbolFunarg)
|
||||
&& all(d.references, function(ref) {
|
||||
return d.scope === ref.scope;
|
||||
})) {
|
||||
init = fixed;
|
||||
}
|
||||
} else {
|
||||
var ev = fixed.evaluate(compressor);
|
||||
if (ev !== fixed && (compressor.option("unsafe_regexp") || !(ev instanceof RegExp))) {
|
||||
init = make_node_from_constant(ev, fixed);
|
||||
}
|
||||
}
|
||||
if (d.should_replace) {
|
||||
return d.should_replace();
|
||||
if (init) {
|
||||
var value_length = init.optimize(compressor).print_to_string().length;
|
||||
var fn;
|
||||
if (has_symbol_ref(fixed)) {
|
||||
fn = function() {
|
||||
var result = init.optimize(compressor);
|
||||
return result === init ? result.clone(true) : result;
|
||||
};
|
||||
} else {
|
||||
value_length = Math.min(value_length, fixed.print_to_string().length);
|
||||
fn = function() {
|
||||
var result = best_of_expression(init.optimize(compressor), fixed);
|
||||
return result === init || result === fixed ? result.clone(true) : result;
|
||||
};
|
||||
}
|
||||
var name_length = d.name.length;
|
||||
var overhead = 0;
|
||||
if (compressor.option("unused") && !compressor.exposed(d)) {
|
||||
overhead = (name_length + 2 + value_length) / d.references.length;
|
||||
}
|
||||
d.should_replace = value_length <= name_length + overhead ? fn : false;
|
||||
} else {
|
||||
d.should_replace = false;
|
||||
}
|
||||
}
|
||||
if (d.should_replace) {
|
||||
return d.should_replace();
|
||||
}
|
||||
}
|
||||
return self;
|
||||
|
||||
@@ -4832,11 +4884,11 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
if (is_lhs(self, compressor.parent())) return self;
|
||||
if (compressor.option("properties") && key !== prop) {
|
||||
var node = self.flatten_object(property, compressor);
|
||||
if (node) {
|
||||
expr = self.expression = node.expression;
|
||||
prop = self.property = node.property;
|
||||
if (key !== prop) {
|
||||
var sub = self.flatten_object(property, compressor);
|
||||
if (sub) {
|
||||
expr = self.expression = sub.expression;
|
||||
prop = self.property = sub.property;
|
||||
}
|
||||
}
|
||||
if (compressor.option("properties") && compressor.option("side_effects")
|
||||
@@ -4882,7 +4934,8 @@ merge(Compressor.prototype, {
|
||||
return self;
|
||||
});
|
||||
|
||||
AST_Lambda.DEFMETHOD("contains_this", function() {
|
||||
AST_Lambda.DEFMETHOD("contains_this", function(grandparent) {
|
||||
if (grandparent instanceof AST_New) return false;
|
||||
var result;
|
||||
var self = this;
|
||||
self.walk(new TreeWalker(function(node) {
|
||||
@@ -4894,6 +4947,7 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
|
||||
AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) {
|
||||
if (!compressor.option("properties")) return;
|
||||
var arrows = compressor.option("unsafe_arrows") && compressor.option("ecma") >= 6;
|
||||
var expr = this.expression;
|
||||
if (expr instanceof AST_Object) {
|
||||
@@ -4907,7 +4961,7 @@ merge(Compressor.prototype, {
|
||||
})) break;
|
||||
var value = prop.value;
|
||||
if ((value instanceof AST_Accessor || value instanceof AST_Function)
|
||||
&& value.contains_this()) break;
|
||||
&& value.contains_this(compressor.parent())) break;
|
||||
return make_node(AST_Sub, this, {
|
||||
expression: make_node(AST_Array, expr, {
|
||||
elements: props.map(function(prop) {
|
||||
@@ -4957,10 +5011,8 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
if (is_lhs(self, compressor.parent())) return self;
|
||||
if (compressor.option("properties")) {
|
||||
var node = self.flatten_object(self.property, compressor);
|
||||
if (node) return node.optimize(compressor);
|
||||
}
|
||||
var sub = self.flatten_object(self.property, compressor);
|
||||
if (sub) return sub.optimize(compressor);
|
||||
var ev = self.evaluate(compressor);
|
||||
if (ev !== self) {
|
||||
ev = make_node_from_constant(ev, self).optimize(compressor);
|
||||
|
||||
Reference in New Issue
Block a user