Actually let's move away those monsters from the evaluate function

ev() should do a single thing — evaluate constant expressions.  if that's
not possible, just return the original node.  it's not the best place for
partial evaluation there, instead doing it in the compress functions.
This commit is contained in:
Mihai Bazon
2013-09-22 14:54:32 +03:00
parent 583fac0a0f
commit 037199bfe2
2 changed files with 83 additions and 82 deletions

View File

@@ -637,12 +637,6 @@ merge(Compressor.prototype, {
if (!compressor.option("evaluate")) return [ this ];
try {
var val = this._eval(compressor);
if (val instanceof AST_Node) {
// we didn't really reduce it to a constant, so
// the return array should contain only one
// node; but perhaps it's a better one.
return [ best_of(val, this) ];
}
return [ best_of(make_node_from_constant(compressor, val, this), this), val ];
} catch(ex) {
if (ex !== def) throw ex;
@@ -704,22 +698,7 @@ merge(Compressor.prototype, {
case "|" : return ev(left, c) | ev(right, c);
case "&" : return ev(left, c) & ev(right, c);
case "^" : return ev(left, c) ^ ev(right, c);
case "+" :
// handle concatenating strings even if the left part cannot
// be evaluated, e.g. (variable + "str") + "str"
if (left instanceof AST_Binary && left.operator == "+" && left.is_string(c)) {
return make_node(AST_Binary, this, {
operator: "+",
left: left.left,
right: make_node(AST_String, null, {
value : "" + ev(left.right, c) + ev(right, c),
start : left.right.start,
end : right.end
})
});
} else {
return ev(left, c) + ev(right, c);
}
case "+" : return ev(left, c) + ev(right, c);
case "*" : return ev(left, c) * ev(right, c);
case "/" : return ev(left, c) / ev(right, c);
case "%" : return ev(left, c) % ev(right, c);
@@ -750,65 +729,6 @@ merge(Compressor.prototype, {
if (d && d.constant && d.init) return ev(d.init, compressor);
throw def;
});
def(AST_Call, function(compressor){
if (compressor.option("unsafe")) {
if (this.expression instanceof AST_Dot
&& this.expression.expression instanceof AST_Array
&& this.expression.property == "join") {
var separator = this.args.length == 0 ? "," : ev(this.args[0], compressor);
if (separator instanceof AST_Node) throw def; // not a constant
var elements = this.expression.expression.elements.map(function(el){
return el.evaluate(compressor); // doesn't throw.
});
elements = elements.reduce(function(a, el){
if (a.length == 0 || el.length == 1) {
a.push(el);
} else {
var last = a[a.length - 1];
if (last.length == 2) {
// it's a constant
var val = "" + last[1] + separator + el[1];
a[a.length - 1] = [
make_node_from_constant(compressor, val, last[0]),
val
];
} else {
a.push(el);
}
}
return a;
}, []);
if (elements.length == 0) return "";
if (elements.length == 1) return elements[0][0];
if (separator == "") {
var first;
if (elements[0][0] instanceof AST_String
|| elements[1][0] instanceof AST_String) {
first = elements.shift()[0];
} else {
first = make_node(AST_String, this, { value: "" });
}
return elements.reduce(function(prev, el){
return make_node(AST_Binary, el[0], {
operator : "+",
left : prev,
right : el[0],
});
}, first);
}
// need this awkward cloning to not affect original element
// best_of will decide which one to get through.
var node = this.clone();
node.expression = node.expression.clone();
node.expression.expression = node.expression.expression.clone();
node.expression.expression.elements = elements.map(function(el){
return el[0];
});
return node;
}
}
throw def;
});
})(function(node, func){
node.DEFMETHOD("_eval", func);
});
@@ -1759,6 +1679,53 @@ merge(Compressor.prototype, {
right: exp.expression
}).transform(compressor);
}
else if (exp instanceof AST_Dot && exp.expression instanceof AST_Array && exp.property == "join") EXIT: {
var separator = self.args.length == 0 ? "," : self.args[0].evaluate(compressor)[1];
if (separator == null) break EXIT; // not a constant
var elements = exp.expression.elements.reduce(function(a, el){
el = el.evaluate(compressor);
if (a.length == 0 || el.length == 1) {
a.push(el);
} else {
var last = a[a.length - 1];
if (last.length == 2) {
// it's a constant
var val = "" + last[1] + separator + el[1];
a[a.length - 1] = [ make_node_from_constant(compressor, val, last[0]), val ];
} else {
a.push(el);
}
}
return a;
}, []);
if (elements.length == 0) return make_node(AST_String, self, { value: "" });
if (elements.length == 1) return elements[0][0];
if (separator == "") {
var first;
if (elements[0][0] instanceof AST_String
|| elements[1][0] instanceof AST_String) {
first = elements.shift()[0];
} else {
first = make_node(AST_String, self, { value: "" });
}
return elements.reduce(function(prev, el){
return make_node(AST_Binary, el[0], {
operator : "+",
left : prev,
right : el[0],
});
}, first).transform(compressor);
}
// need this awkward cloning to not affect original element
// best_of will decide which one to get through.
var node = self.clone();
node.expression = node.expression.clone();
node.expression.expression = node.expression.expression.clone();
node.expression.expression.elements = elements.map(function(el){
return el[0];
});
return best_of(self, node);
}
}
if (compressor.option("side_effects")) {
if (self.expression instanceof AST_Function
@@ -1988,6 +1955,40 @@ merge(Compressor.prototype, {
&& self.left.operator == "+" && self.left.is_string(compressor)) {
return self.left;
}
if (compressor.option("evaluate")) {
if (self.operator == "+") {
if (self.left instanceof AST_Constant
&& self.right instanceof AST_Binary
&& self.right.operator == "+"
&& self.right.left instanceof AST_Constant
&& self.right.is_string(compressor)) {
self = make_node(AST_Binary, self, {
operator: "+",
left: make_node(AST_String, null, {
value: self.left.getValue() + self.right.left.getValue(),
start: self.left.start,
end: self.right.left.end
}),
right: self.right.right
});
}
if (self.right instanceof AST_Constant
&& self.left instanceof AST_Binary
&& self.left.operator == "+"
&& self.left.right instanceof AST_Constant
&& self.left.is_string(compressor)) {
self = make_node(AST_Binary, self, {
operator: "+",
left: self.left.left,
right: make_node(AST_String, null, {
value: self.left.right.getValue() + self.right.getValue(),
start: self.left.right.start,
end: self.right.end
})
});
}
}
}
return self.evaluate(compressor)[0];
});

View File

@@ -69,6 +69,6 @@ constant_join_2: {
var e = [ "foo", "bar", boo(),
"foo+1+2+3+bar",
"baz", "x", "y" ].join("really-long-separator");
var f = "str" + ("str" + variable) + "foobar" + ("moo" + foo);
var f = "strstr" + variable + "foobar" + ("moo" + foo);
}
}