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:
@@ -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;
|
||||
|
||||
@@ -45,11 +45,10 @@ chained_evaluation_2: {
|
||||
}
|
||||
expect: {
|
||||
(function() {
|
||||
var a = "long piece of string";
|
||||
(function() {
|
||||
var c;
|
||||
c = f(a);
|
||||
c.bar = a;
|
||||
var c, b = "long piece of string";
|
||||
c = f(b);
|
||||
c.bar = b;
|
||||
})();
|
||||
})();
|
||||
}
|
||||
|
||||
19
test/compress/issue-1787.js
Normal file
19
test/compress/issue-1787.js
Normal file
@@ -0,0 +1,19 @@
|
||||
unary_prefix: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function() {
|
||||
var x = -(2 / 3);
|
||||
return x;
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log(function() {
|
||||
return -2 / 3;
|
||||
}());
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
Reference in New Issue
Block a user