enhance reduce_vars (#3942)
This commit is contained in:
300
lib/compress.js
300
lib/compress.js
@@ -447,7 +447,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
|
||||
function mark(tw, def, safe) {
|
||||
tw.safe_ids[def.id] = safe;
|
||||
tw.safe_ids[def.id] = safe && {};
|
||||
}
|
||||
|
||||
function push_ref(def, ref) {
|
||||
@@ -455,44 +455,37 @@ merge(Compressor.prototype, {
|
||||
def.last_ref = ref;
|
||||
}
|
||||
|
||||
function add_assign(tw, def, node) {
|
||||
if (def.fixed === false) return;
|
||||
tw.assigns.add(def.id, node);
|
||||
}
|
||||
|
||||
function set_assign(tw, def, node) {
|
||||
if (def.fixed === false) return;
|
||||
var assigns = tw.assigns.get(def.id);
|
||||
if (assigns) assigns.forEach(function(node) {
|
||||
node.assigns = assigns;
|
||||
});
|
||||
tw.assigns.set(def.id, def.assigns = [ node ]);
|
||||
}
|
||||
|
||||
function safe_to_read(tw, def) {
|
||||
if (def.single_use == "m") return false;
|
||||
if (tw.safe_ids[def.id]) {
|
||||
var safe = tw.safe_ids[def.id];
|
||||
if (safe) {
|
||||
if (!HOP(tw.safe_ids, def.id)) safe.read = safe.read ? true : tw.safe_ids;
|
||||
if (def.fixed == null) {
|
||||
if (is_arguments(def)) return false;
|
||||
if (def.global && def.name == "arguments") return false;
|
||||
def.fixed = make_node(AST_Undefined, def.orig[0]);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
return !safe.assign || safe.assign === tw.safe_ids;
|
||||
}
|
||||
return def.fixed instanceof AST_Defun;
|
||||
}
|
||||
|
||||
function safe_to_assign(tw, def, value) {
|
||||
function safe_to_assign(tw, def) {
|
||||
if (def.fixed === undefined) return true;
|
||||
if (def.fixed === null && def.safe_ids) {
|
||||
def.safe_ids[def.id] = false;
|
||||
delete def.safe_ids;
|
||||
return true;
|
||||
}
|
||||
if (!HOP(tw.safe_ids, def.id)) return false;
|
||||
var safe = tw.safe_ids[def.id];
|
||||
if (!HOP(tw.safe_ids, def.id)) {
|
||||
if (!safe) return false;
|
||||
safe.assign = safe.assign ? true : tw.safe_ids;
|
||||
}
|
||||
if (!safe_to_read(tw, def)) return false;
|
||||
if (def.fixed === false) return false;
|
||||
if (def.fixed != null && (!value || def.references.length > def.assignments)) return false;
|
||||
if (def.fixed != null && safe.read && safe.read !== tw.safe_ids) return false;
|
||||
return all(def.orig, function(sym) {
|
||||
return !(sym instanceof AST_SymbolLambda);
|
||||
});
|
||||
@@ -573,35 +566,40 @@ merge(Compressor.prototype, {
|
||||
mark_assignment_to_arguments(sym);
|
||||
return;
|
||||
}
|
||||
if (sym.fixed) delete sym.fixed;
|
||||
var d = sym.definition();
|
||||
var safe = safe_to_assign(tw, d, node.right);
|
||||
d.assignments++;
|
||||
var fixed = d.fixed;
|
||||
if (!fixed && node.operator != "=") return;
|
||||
var eq = node.operator == "=";
|
||||
var value = eq ? node.right : node;
|
||||
if (is_modified(compressor, tw, node, value, 0)) return;
|
||||
sym.fixed = d.fixed = eq ? function() {
|
||||
return node.right;
|
||||
} : function() {
|
||||
var value = fixed instanceof AST_Node ? fixed : fixed();
|
||||
return value && make_node(AST_Binary, node, {
|
||||
operator: node.operator.slice(0, -1),
|
||||
left: value,
|
||||
right: node.right
|
||||
});
|
||||
};
|
||||
if (!safe) return;
|
||||
push_ref(d, sym);
|
||||
mark(tw, d, false);
|
||||
node.right.walk(tw);
|
||||
mark(tw, d, true);
|
||||
if (eq) {
|
||||
mark_escaped(tw, d, sym.scope, node, value, 0, 1);
|
||||
set_assign(tw, d, node);
|
||||
var safe = (eq || safe_to_read(tw, d)) && safe_to_assign(tw, d);
|
||||
var fixed = d.fixed;
|
||||
if (safe) {
|
||||
push_ref(d, sym);
|
||||
mark(tw, d, false);
|
||||
node.right.walk(tw);
|
||||
mark(tw, d, true);
|
||||
if (eq) mark_escaped(tw, d, sym.scope, node, value, 0, 1);
|
||||
} else {
|
||||
add_assign(tw, d, node);
|
||||
descend();
|
||||
}
|
||||
if (fixed !== false && d.fixed !== false) {
|
||||
if (eq) {
|
||||
sym.fixed = d.fixed = function() {
|
||||
return node.right;
|
||||
};
|
||||
} else {
|
||||
if (fixed == null) fixed = make_node(AST_Undefined, d.orig[0]);
|
||||
sym.fixed = d.fixed = function() {
|
||||
var value = fixed instanceof AST_Node ? fixed : fixed();
|
||||
return value && make_node(AST_Binary, node, {
|
||||
operator: node.operator.slice(0, -1),
|
||||
left: value,
|
||||
right: node.right
|
||||
});
|
||||
};
|
||||
}
|
||||
sym.fixed.assigns = eq || !fixed.assigns ? [] : fixed.assigns.slice();
|
||||
sym.fixed.assigns.push(node);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
@@ -740,13 +738,13 @@ merge(Compressor.prototype, {
|
||||
fn.argnames.forEach(function(arg, i) {
|
||||
var d = arg.definition();
|
||||
if (d.fixed === undefined && (!fn.uses_arguments || tw.has_directive("use strict"))) {
|
||||
tw.loop_ids[d.id] = tw.in_loop;
|
||||
mark(tw, d, true);
|
||||
var value = iife.args[i];
|
||||
d.fixed = function() {
|
||||
var j = fn.argnames.indexOf(arg);
|
||||
return (j < 0 ? value : iife.args[j]) || make_node(AST_Undefined, iife);
|
||||
};
|
||||
tw.loop_ids[d.id] = tw.in_loop;
|
||||
mark(tw, d, true);
|
||||
} else {
|
||||
d.fixed = false;
|
||||
}
|
||||
@@ -787,7 +785,6 @@ merge(Compressor.prototype, {
|
||||
this.definition().fixed = false;
|
||||
});
|
||||
def(AST_SymbolRef, function(tw, descend, compressor) {
|
||||
if (this.fixed) delete this.fixed;
|
||||
var d = this.definition();
|
||||
push_ref(d, this);
|
||||
if (d.references.length == 1
|
||||
@@ -826,6 +823,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
mark_escaped(tw, d, this.scope, this, value, 0, 1);
|
||||
}
|
||||
this.fixed = d.fixed;
|
||||
var parent;
|
||||
if (d.fixed instanceof AST_Defun
|
||||
&& !((parent = tw.parent()) instanceof AST_Call && parent.expression === this)) {
|
||||
@@ -864,51 +862,63 @@ merge(Compressor.prototype, {
|
||||
mark_assignment_to_arguments(exp);
|
||||
return;
|
||||
}
|
||||
if (exp.fixed) delete exp.fixed;
|
||||
var d = exp.definition();
|
||||
var safe = safe_to_assign(tw, d, true);
|
||||
d.assignments++;
|
||||
var safe = safe_to_read(tw, d) && safe_to_assign(tw, d);
|
||||
var fixed = d.fixed;
|
||||
if (!fixed) return;
|
||||
d.fixed = function() {
|
||||
var value = fixed instanceof AST_Node ? fixed : fixed();
|
||||
return value && make_node(AST_Binary, node, {
|
||||
operator: node.operator.slice(0, -1),
|
||||
left: make_node(AST_UnaryPrefix, node, {
|
||||
operator: "+",
|
||||
expression: value
|
||||
}),
|
||||
right: make_node(AST_Number, node, {
|
||||
value: 1
|
||||
})
|
||||
});
|
||||
};
|
||||
exp.fixed = node instanceof AST_UnaryPrefix ? d.fixed : function() {
|
||||
var value = fixed instanceof AST_Node ? fixed : fixed();
|
||||
return value && make_node(AST_UnaryPrefix, node, {
|
||||
operator: "+",
|
||||
expression: value
|
||||
});
|
||||
};
|
||||
if (!safe) return;
|
||||
push_ref(d, exp);
|
||||
mark(tw, d, true);
|
||||
add_assign(tw, d, node);
|
||||
if (safe) {
|
||||
push_ref(d, exp);
|
||||
mark(tw, d, true);
|
||||
} else {
|
||||
descend();
|
||||
}
|
||||
if (fixed !== false && d.fixed !== false) {
|
||||
if (fixed == null) fixed = make_node(AST_Undefined, d.orig[0]);
|
||||
d.fixed = function() {
|
||||
var value = fixed instanceof AST_Node ? fixed : fixed();
|
||||
return value && make_node(AST_Binary, node, {
|
||||
operator: node.operator.slice(0, -1),
|
||||
left: make_node(AST_UnaryPrefix, node, {
|
||||
operator: "+",
|
||||
expression: value
|
||||
}),
|
||||
right: make_node(AST_Number, node, {
|
||||
value: 1
|
||||
})
|
||||
});
|
||||
};
|
||||
d.fixed.assigns = fixed.assigns ? fixed.assigns.slice() : [];
|
||||
d.fixed.assigns.push(node);
|
||||
if (node instanceof AST_UnaryPrefix) {
|
||||
exp.fixed = d.fixed;
|
||||
} else {
|
||||
exp.fixed = function() {
|
||||
var value = fixed instanceof AST_Node ? fixed : fixed();
|
||||
return value && make_node(AST_UnaryPrefix, node, {
|
||||
operator: "+",
|
||||
expression: value
|
||||
});
|
||||
};
|
||||
exp.fixed.assigns = fixed.assigns;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
def(AST_VarDef, function(tw, descend) {
|
||||
var node = this;
|
||||
var d = node.name.definition();
|
||||
if (node.value) {
|
||||
if (safe_to_assign(tw, d, node.value)) {
|
||||
d.fixed = function() {
|
||||
return node.value;
|
||||
};
|
||||
if (safe_to_assign(tw, d)) {
|
||||
tw.loop_ids[d.id] = tw.in_loop;
|
||||
mark(tw, d, false);
|
||||
descend();
|
||||
mark(tw, d, true);
|
||||
set_assign(tw, d, node);
|
||||
if (d.fixed !== false) {
|
||||
d.fixed = function() {
|
||||
return node.value;
|
||||
};
|
||||
d.fixed.assigns = [ node ];
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
d.fixed = false;
|
||||
@@ -933,8 +943,6 @@ merge(Compressor.prototype, {
|
||||
reset_flags(node);
|
||||
return node.reduce_vars(tw, descend, compressor);
|
||||
} : reset_flags);
|
||||
// Assignment chains
|
||||
tw.assigns = new Dictionary();
|
||||
// Flow control for visiting `AST_Defun`s
|
||||
tw.defun_ids = Object.create(null);
|
||||
tw.defun_visited = Object.create(null);
|
||||
@@ -951,6 +959,7 @@ merge(Compressor.prototype, {
|
||||
function reset_flags(node) {
|
||||
node._squeezed = false;
|
||||
node._optimized = false;
|
||||
delete node.fixed;
|
||||
if (node instanceof AST_Scope) delete node._var_names;
|
||||
}
|
||||
});
|
||||
@@ -4222,7 +4231,7 @@ merge(Compressor.prototype, {
|
||||
})) return;
|
||||
return sym;
|
||||
};
|
||||
var chained = Object.create(null);
|
||||
var assign_in_use = Object.create(null);
|
||||
var in_use = [];
|
||||
var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use
|
||||
var value_read = Object.create(null);
|
||||
@@ -4235,7 +4244,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
});
|
||||
}
|
||||
var assign_in_use = new Dictionary();
|
||||
var assignments = new Dictionary();
|
||||
var var_defs_by_id = new Dictionary();
|
||||
var initializations = new Dictionary();
|
||||
// pass 1: find out which symbols are directly used in
|
||||
@@ -4282,7 +4291,7 @@ merge(Compressor.prototype, {
|
||||
} else {
|
||||
initializations.add(node_def.id, def.value);
|
||||
}
|
||||
match_assigns(node_def, def);
|
||||
assignments.add(node_def.id, def);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
@@ -4300,6 +4309,33 @@ merge(Compressor.prototype, {
|
||||
init.walk(tw);
|
||||
});
|
||||
}
|
||||
Object.keys(assign_in_use).forEach(function(id) {
|
||||
var assigns = assign_in_use[id];
|
||||
if (!assigns) {
|
||||
delete assign_in_use[id];
|
||||
return;
|
||||
}
|
||||
assigns = assigns.reduce(function(in_use, assigns) {
|
||||
assigns.forEach(function(assign) {
|
||||
push_uniq(in_use, assign);
|
||||
});
|
||||
return in_use;
|
||||
}, []);
|
||||
var in_use = (assignments.get(id) || []).filter(function(node) {
|
||||
return find_if(node instanceof AST_Unary ? function(assign) {
|
||||
return assign === node;
|
||||
} : function(assign) {
|
||||
if (assign === node) return true;
|
||||
if (assign instanceof AST_Unary) return false;
|
||||
return get_rvalue(assign) === get_rvalue(node);
|
||||
}, assigns);
|
||||
});
|
||||
if (assigns.length == in_use.length) {
|
||||
assign_in_use[id] = in_use;
|
||||
} else {
|
||||
delete assign_in_use[id];
|
||||
}
|
||||
});
|
||||
var drop_fn_name = compressor.option("keep_fnames") ? return_false : compressor.option("ie8") ? function(def) {
|
||||
return !compressor.exposed(def) && def.references.length == def.replaced;
|
||||
} : function(def) {
|
||||
@@ -4487,7 +4523,7 @@ merge(Compressor.prototype, {
|
||||
right: def.value
|
||||
});
|
||||
var index = indexOf_assign(sym, def);
|
||||
if (index >= 0) assign_in_use.get(sym.id)[index] = assign;
|
||||
if (index >= 0) assign_in_use[sym.id][index] = assign;
|
||||
sym.eliminated++;
|
||||
return assign.transform(tt);
|
||||
}));
|
||||
@@ -4605,30 +4641,20 @@ merge(Compressor.prototype, {
|
||||
};
|
||||
}
|
||||
|
||||
function match_assigns(def, node) {
|
||||
if (!def.fixed) return;
|
||||
if (!def.assigns) return;
|
||||
if (find_if(node instanceof AST_Unary ? function(assign) {
|
||||
return assign === node;
|
||||
} : function(assign) {
|
||||
if (assign === node) return true;
|
||||
if (assign instanceof AST_Unary) return false;
|
||||
return get_rvalue(assign) === get_rvalue(node);
|
||||
}, def.assigns)) {
|
||||
assign_in_use.add(def.id, node);
|
||||
}
|
||||
function track_assigns(def, node) {
|
||||
if (def.scope !== self) return false;
|
||||
if (!def.fixed || !node.fixed) assign_in_use[def.id] = false;
|
||||
return assign_in_use[def.id] !== false;
|
||||
}
|
||||
|
||||
function add_assigns(def, node) {
|
||||
if (!assign_in_use[def.id]) assign_in_use[def.id] = [];
|
||||
if (node.fixed.assigns) push_uniq(assign_in_use[def.id], node.fixed.assigns);
|
||||
}
|
||||
|
||||
function indexOf_assign(def, node) {
|
||||
if (!def.fixed) return;
|
||||
if (!def.assigns) return;
|
||||
var assigns = assign_in_use.get(def.id);
|
||||
if (!assigns) return;
|
||||
if (assigns.length != def.assigns.length) return;
|
||||
var index = assigns.indexOf(node);
|
||||
if (index >= 0 || !chained[def.id] || node.assigns && all(node.assigns, function(assign) {
|
||||
return assign.write_only || assign.operator == "=" || assign instanceof AST_VarDef;
|
||||
})) return index;
|
||||
var nodes = assign_in_use[def.id];
|
||||
return nodes && nodes.indexOf(node);
|
||||
}
|
||||
|
||||
function verify_safe_usage(def, read, modified) {
|
||||
@@ -4653,14 +4679,12 @@ merge(Compressor.prototype, {
|
||||
|
||||
function scan_ref_scoped(node, descend, init) {
|
||||
if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef) {
|
||||
var node_def = node.left.definition();
|
||||
if (node.operator != "=") chained[node_def.id] = true;
|
||||
match_assigns(node_def, node);
|
||||
var def = node.left.definition();
|
||||
if (def.scope === self) assignments.add(def.id, node);
|
||||
}
|
||||
if (node instanceof AST_Unary && node.expression instanceof AST_SymbolRef) {
|
||||
var node_def = node.expression.definition();
|
||||
chained[node_def.id] = true;
|
||||
match_assigns(node_def, node);
|
||||
var def = node.expression.definition();
|
||||
if (def.scope === self) assignments.add(def.id, node);
|
||||
}
|
||||
var node_def, props = [], sym = assign_as_unused(node, props);
|
||||
if (sym && self.variables.get(sym.name) === (node_def = sym.definition())) {
|
||||
@@ -4686,6 +4710,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
}
|
||||
track_assigns(node_def, sym);
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_SymbolRef) {
|
||||
@@ -4694,6 +4719,7 @@ merge(Compressor.prototype, {
|
||||
in_use_ids[node_def.id] = true;
|
||||
in_use.push(node_def);
|
||||
}
|
||||
if (track_assigns(node_def, node)) add_assigns(node_def, node);
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_Scope) {
|
||||
@@ -7641,7 +7667,8 @@ merge(Compressor.prototype, {
|
||||
def.replaced++;
|
||||
return value;
|
||||
}
|
||||
if (fixed && def.should_replace === undefined) {
|
||||
var local = self.fixed !== def.fixed;
|
||||
if (fixed && (local || def.should_replace !== false)) {
|
||||
var init;
|
||||
if (fixed instanceof AST_This) {
|
||||
if (!(def.orig[0] instanceof AST_SymbolFunarg) && same_scope(def)) {
|
||||
@@ -7659,36 +7686,33 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
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;
|
||||
};
|
||||
if (!local && def.should_replace === undefined) {
|
||||
var value_length = init.optimize(compressor).print_to_string().length;
|
||||
if (!has_symbol_ref(fixed)) {
|
||||
value_length = Math.min(value_length, fixed.print_to_string().length);
|
||||
}
|
||||
var name_length = def.name.length;
|
||||
if (compressor.option("unused") && !compressor.exposed(def)) {
|
||||
var referenced = def.references.length - def.replaced;
|
||||
name_length += (name_length + 2 + value_length) / (referenced - def.assignments);
|
||||
}
|
||||
var delta = value_length - Math.floor(name_length);
|
||||
def.should_replace = delta < compressor.eval_threshold;
|
||||
}
|
||||
var name_length = def.name.length;
|
||||
if (compressor.option("unused") && !compressor.exposed(def)) {
|
||||
var referenced = def.references.length - def.replaced;
|
||||
name_length += (name_length + 2 + value_length) / (referenced - def.assignments);
|
||||
if (local || def.should_replace) {
|
||||
var value;
|
||||
if (has_symbol_ref(fixed)) {
|
||||
value = init.optimize(compressor);
|
||||
if (value === init) value = value.clone(true);
|
||||
} else {
|
||||
value = best_of_expression(init.optimize(compressor), fixed);
|
||||
if (value === init || value === fixed) value = value.clone(true);
|
||||
}
|
||||
def.replaced++;
|
||||
return value;
|
||||
}
|
||||
var delta = value_length - Math.floor(name_length);
|
||||
def.should_replace = delta < compressor.eval_threshold ? fn : false;
|
||||
} else {
|
||||
def.should_replace = false;
|
||||
}
|
||||
}
|
||||
if (def.should_replace) {
|
||||
var value = def.should_replace();
|
||||
def.replaced++;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user