resolve constant expressions

This commit is contained in:
Mihai Bazon
2012-09-03 15:47:15 +03:00
parent f702264617
commit f03138daa8

View File

@@ -60,6 +60,9 @@ function Compressor(options, false_by_default) {
keep_comps : !false_by_default, keep_comps : !false_by_default,
drop_debugger : !false_by_default, drop_debugger : !false_by_default,
unsafe : !false_by_default, unsafe : !false_by_default,
ifs : !false_by_default,
comparations : !false_by_default,
evaluate : !false_by_default,
warnings : true warnings : true
}); });
@@ -86,6 +89,7 @@ function Compressor(options, false_by_default) {
}); });
function make_node(ctor, orig, props) { function make_node(ctor, orig, props) {
if (!props) props = {};
if (!props.start) props.start = orig.start; if (!props.start) props.start = orig.start;
if (!props.end) props.end = orig.end; if (!props.end) props.end = orig.end;
return new ctor(props); return new ctor(props);
@@ -106,23 +110,6 @@ function Compressor(options, false_by_default) {
}); });
}; };
SQUEEZE(AST_Debugger, function(self, compressor){
if (compressor.option("drop_debugger"))
return new AST_EmptyStatement(self);
});
SQUEEZE(AST_LabeledStatement, function(self, compressor){
self = self.clone();
self.body = self.body.squeeze(compressor);
return self.label.references.length == 0 ? self.body : self;
});
SQUEEZE(AST_Statement, function(self, compressor){
self = self.clone();
self.body = self.body.squeeze(compressor);
return self;
});
function tighten_body(statements, compressor) { function tighten_body(statements, compressor) {
statements = do_list(statements, compressor); statements = do_list(statements, compressor);
statements = eliminate_spurious_blocks(statements); statements = eliminate_spurious_blocks(statements);
@@ -219,6 +206,238 @@ function Compressor(options, false_by_default) {
return statements; return statements;
} }
/* -----[ boolean/negation helpers ]----- */
// methods to determine whether an expression has a boolean result type
(function (def){
var unary_bool = [ "!", "delete" ];
var binary_bool = [ "in", "instanceof", "==", "!=", "===", "!==", "<", "<=", ">=", ">" ];
def(AST_Node, function(){ return false });
def(AST_UnaryPrefix, function(){
return member(this.operator, unary_bool);
});
def(AST_Binary, function(){
return member(this.operator, binary_bool) ||
( (this.operator == "&&" || this.operator == "||") &&
this.left.is_boolean() && this.right.is_boolean() );
});
def(AST_Conditional, function(){
return this.consequent.is_boolean() && this.alternative.is_boolean();
});
def(AST_Assign, function(){
return this.operator == "=" && this.right.is_boolean();
});
def(AST_Seq, function(){
return this.second.is_boolean();
});
def(AST_True, function(){ return true });
def(AST_False, function(){ return true });
})(function(node, func){
node.DEFMETHOD("is_boolean", func);
});
// methods to determine if an expression has a string result type
(function (def){
def(AST_Node, function(){ return false });
def(AST_String, function(){ return true });
def(AST_UnaryPrefix, function(){
return this.operator == "typeof";
});
def(AST_Binary, function(){
return this.operator == "+" &&
(this.left.is_string() || this.right.is_string());
});
def(AST_Assign, function(){
return this.operator == "=" && this.right.is_string();
});
})(function(node, func){
node.DEFMETHOD("is_string", func);
});
// function best_of(ast1, ast2) {
// return ast1.print_to_string().length > ast2.print_to_string().length ? ast2 : ast1;
// };
// methods to evaluate a constant expression
(function (def){
AST_Node.DEFMETHOD("evaluate", function(compressor, constant, not_constant){
if (!compressor.option("evaluate")) return this;
try {
var val = this._eval(), ast;
switch (typeof val) {
case "string":
ast = make_node(AST_String, this, {
value: val
});
break;
case "number":
ast = make_node(AST_Number, this, {
value: val
});
break;
case "boolean":
ast = make_node(val ? AST_True : AST_False, this);
break;
case "undefined":
ast = make_node(AST_Undefined, this);
break;
default:
if (val === null) {
ast = make_node(AST_Null, this);
break;
}
throw new Error(string_template("Can't handle constant of type: {type}", {
type: typeof val
}));
}
if (constant) return constant(ast, val);
return ast;
} catch(ex) {
if (ex !== def) throw ex;
if (not_constant) return not_constant(this);
return this;
}
});
function evaluate(node) {
return node._eval();
};
def(AST_Node, function(){
throw def; // not constant
});
def(AST_Constant, function(){
return this.getValue();
});
def(AST_UnaryPrefix, function(){
var e = this.expression;
switch (this.operator) {
case "!": return !evaluate(e);
case "typeof": return typeof evaluate(e);
case "~": return ~evaluate(e);
case "-": return -evaluate(e);
case "+": return +evaluate(e);
}
throw def;
});
def(AST_Binary, function(){
var left = this.left, right = this.right;
switch (this.operator) {
case "&&" : return evaluate(left) && evaluate(right);
case "||" : return evaluate(left) || evaluate(right);
case "|" : return evaluate(left) | evaluate(right);
case "&" : return evaluate(left) & evaluate(right);
case "^" : return evaluate(left) ^ evaluate(right);
case "+" : return evaluate(left) + evaluate(right);
case "*" : return evaluate(left) * evaluate(right);
case "/" : return evaluate(left) / evaluate(right);
case "%" : return evaluate(left) % evaluate(right);
case "-" : return evaluate(left) - evaluate(right);
case "<<" : return evaluate(left) << evaluate(right);
case ">>" : return evaluate(left) >> evaluate(right);
case ">>>" : return evaluate(left) >>> evaluate(right);
case "==" : return evaluate(left) == evaluate(right);
case "===" : return evaluate(left) === evaluate(right);
case "!=" : return evaluate(left) != evaluate(right);
case "!==" : return evaluate(left) !== evaluate(right);
case "<" : return evaluate(left) < evaluate(right);
case "<=" : return evaluate(left) <= evaluate(right);
case ">" : return evaluate(left) > evaluate(right);
case ">=" : return evaluate(left) >= evaluate(right);
case "in" : return evaluate(left) in evaluate(right);
case "instanceof" : return evaluate(left) instanceof evaluate(right);
}
throw def;
});
def(AST_Conditional, function(){
return evaluate(this.condition)
? evaluate(this.consequent)
: evaluate(this.alternative);
});
})(function(node, func){
node.DEFMETHOD("_eval", func);
});
// method to negate an expression
(function(def){
function basic_negation(exp) {
return make_node(AST_UnaryPrefix, exp, {
operator: "!",
expression: exp
});
};
def(AST_Node, function(){
return basic_negation(this);
});
def(AST_Statement, function(){
throw new Error("Cannot evaluate a statement");
});
def(AST_UnaryPrefix, function(){
if (this.operator == "!" && this.expression.is_boolean())
return this.expression();
return basic_negation(this);
});
def(AST_Seq, function(compressor){
var self = this.clone();
self.second = self.second.negate(compressor);
return self;
});
def(AST_Conditional, function(){
var self = this.clone();
self.consequent = self.consequent.negate(compressor);
self.alternative = self.alternative.negate(compressor);
//return best_of(basic_negation(this), self);
return self;
});
def(AST_Binary, function(compressor){
var self = this.clone(), op = this.operator;
if (compressor.option("comparations")) switch (op) {
case "<=" : self.operator = ">" ; return self;
case "<" : self.operator = ">=" ; return self;
case ">=" : self.operator = "<" ; return self;
case ">" : self.operator = "<=" ; return self;
}
switch (op) {
case "==" : self.operator = "!="; return self;
case "!=" : self.operator = "=="; return self;
case "===": self.operator = "!=="; return self;
case "!==": self.operator = "==="; return self;
case "&&":
self.operator = "||";
self.left = self.left.negate(compressor);
self.right = self.right.negate(compressor);
//return best_of(basic_negation(this), self);
return self;
case "||":
self.operator = "&&";
self.left = self.left.negate(compressor);
self.right = self.right.negate(compressor);
//return best_of(basic_negation(this), self);
return self;
}
return basic_negation(this);
});
})(function(node, func){
node.DEFMETHOD("negate", func);
});
/* -----[ node squeezers ]----- */
SQUEEZE(AST_Debugger, function(self, compressor){
if (compressor.option("drop_debugger"))
return new AST_EmptyStatement(self);
});
SQUEEZE(AST_LabeledStatement, function(self, compressor){
self = self.clone();
self.body = self.body.squeeze(compressor);
return self.label.references.length == 0 ? self.body : self;
});
SQUEEZE(AST_Statement, function(self, compressor){
self = self.clone();
self.body = self.body.squeeze(compressor);
return self;
});
SQUEEZE(AST_BlockStatement, function(self, compressor){ SQUEEZE(AST_BlockStatement, function(self, compressor){
self = self.clone(); self = self.clone();
self.body = tighten_body(self.body, compressor); self.body = tighten_body(self.body, compressor);
@@ -279,9 +498,14 @@ function Compressor(options, false_by_default) {
self.body = self.body.squeeze(compressor); self.body = self.body.squeeze(compressor);
if (self.alternative) if (self.alternative)
self.alternative = self.alternative.squeeze(compressor); self.alternative = self.alternative.squeeze(compressor);
if (!compressor.option("ifs")) return self;
return self; return self;
}); });
AST_If.DEFMETHOD("switch_branches", function(){
});
SQUEEZE(AST_Switch, function(self, compressor){ SQUEEZE(AST_Switch, function(self, compressor){
self = self.clone(); self = self.clone();
self.expression = self.expression.squeeze(compressor); self.expression = self.expression.squeeze(compressor);
@@ -372,7 +596,20 @@ function Compressor(options, false_by_default) {
return self; return self;
}); });
SQUEEZE(AST_UnaryPrefix, function(self, compressor){
self = self.clone();
self.expression = self.expression.squeeze(compressor);
return self.evaluate(compressor);
});
SQUEEZE(AST_Binary, function(self, compressor){ SQUEEZE(AST_Binary, function(self, compressor){
self = self.clone();
self.left = self.left.squeeze(compressor);
self.right = self.right.squeeze(compressor);
return self.evaluate(compressor);
});
SQUEEZE(AST_Assign, function(self, compressor){
self = self.clone(); self = self.clone();
self.left = self.left.squeeze(compressor); self.left = self.left.squeeze(compressor);
self.right = self.right.squeeze(compressor); self.right = self.right.squeeze(compressor);