implement delayed resolution for reduce_vars (#1788)

Although it would be nice to enforce `AST_Node` cloning during transformation, that ship has sailed a long time ago.

We now get the assigned value when resolving `AST_SymbolRef` instead of `reset_opt_flags()`, which has the added advantage of improved compressor efficiency.

fixes #1787
This commit is contained in:
Alex Lam S.L
2017-04-05 21:06:42 +08:00
committed by GitHub
parent 9b6bc67c33
commit ff289b90a9
3 changed files with 53 additions and 23 deletions

View File

@@ -273,7 +273,7 @@ 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;
}
}
@@ -283,7 +283,9 @@ merge(Compressor.prototype, {
if (node instanceof AST_VarDef) {
var d = node.name.definition();
if (d.fixed == null) {
d.fixed = node.value;
d.fixed = node.value && function() {
return node.value;
};
mark_as_safe(d);
} else if (node.value) {
d.fixed = false;
@@ -314,7 +316,9 @@ 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);
d.fixed = function() {
return iife.args[i] || make_node(AST_Undefined, iife);
};
mark_as_safe(d);
});
}
@@ -409,6 +413,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++)) {
@@ -1487,15 +1497,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;
@@ -2689,11 +2699,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
@@ -3080,7 +3091,7 @@ merge(Compressor.prototype, {
&& (e.operator == "*" || e.operator == "/" || e.operator == "%")) {
self.expression = e.left;
e.left = self;
return e.optimize(compressor);
return e;
}
// avoids infinite recursion of numerals
if (self.operator != "-"
@@ -3511,12 +3522,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 init = fixed.evaluate(compressor);
if (init !== fixed) {
init = make_node_from_constant(init, fixed).optimize(compressor);
init = best_of_expression(init, fixed);
var value = init.print_to_string().length;
var name = d.name.length;
var freq = d.references.length;