more progress on the compressor (WIP)

This commit is contained in:
Mihai Bazon
2012-09-10 15:54:17 +03:00
parent 16b12c6287
commit a41e6cfabb
4 changed files with 134 additions and 20 deletions

View File

@@ -289,8 +289,8 @@ function Compressor(options, false_by_default) {
});
function best_of(ast1, ast2) {
return ast1.print_to_string({ beautify: false }).length >
ast2.print_to_string({ beautify: false }).length
return ast1.print_to_string().length >
ast2.print_to_string().length
? ast2 : ast1;
};
@@ -448,14 +448,12 @@ function Compressor(options, false_by_default) {
self.operator = "||";
self.left = self.left.negate(compressor);
self.right = self.right.negate(compressor);
//return best_of(basic_negation(this), self);
return self;
return best_of(basic_negation(this), 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 best_of(basic_negation(this), self);
}
return basic_negation(this);
});
@@ -692,18 +690,19 @@ function Compressor(options, false_by_default) {
}
}
}
if (self.condition instanceof AST_UnaryPrefix
&& self.condition.operator == "!") {
self.condition = self.condition.expression;
var negated = self.condition.negate(compressor);
var negated_is_best = best_of(self.condition, negated) === negated;
if (self.alternative && negated_is_best) {
self.condition = negated;
var tmp = self.body;
self.body = self.alternative || make_node(AST_EmptyStatement, self);
self.body = self.alternative || new AST_EmptyStatement();
self.alternative = tmp;
}
if (self.body instanceof AST_EmptyStatement
&& self.alternative instanceof AST_EmptyStatement) {
return make_node(AST_SimpleStatement, self.condition, {
body: self.condition
}).optimize(compressor);
});
}
if (self.body instanceof AST_SimpleStatement
&& self.alternative instanceof AST_SimpleStatement) {
@@ -718,7 +717,14 @@ function Compressor(options, false_by_default) {
if ((!self.alternative
|| self.alternative instanceof AST_EmptyStatement)
&& self.body instanceof AST_SimpleStatement) {
return make_node(AST_SimpleStatement, self, {
if (negated_is_best) return make_node(AST_SimpleStatement, self, {
body: make_node(AST_Binary, self, {
operator : "||",
left : negated,
right : self.body.body
}).optimize(compressor)
});
else return make_node(AST_SimpleStatement, self, {
body: make_node(AST_Binary, self, {
operator : "&&",
left : self.condition,
@@ -843,7 +849,61 @@ function Compressor(options, false_by_default) {
self = self.clone();
self.expression = self.expression.squeeze(compressor);
self.args = do_list(self.args, compressor);
return self;
return self.optimize(compressor);
});
AST_Call.DEFMETHOD("optimize", function(compressor){
if (compressor.option("unsafe")) {
var exp = this.expression;
if (exp instanceof AST_SymbolRef && exp.undeclared) {
switch (exp.name) {
case "Array":
if (this.args.length != 1) {
return make_node(AST_Array, this, {
elements: this.args
}).optimize(compressor);
}
break;
case "Object":
if (this.args.length == 0) {
return make_node(AST_Object, this, {
properties: []
}).optimize(compressor);
}
break;
case "String":
return make_node(AST_Binary, this, {
left: this.args[0],
operator: "+",
right: make_node(AST_String, this, { value: "" })
});
}
}
else if (exp instanceof AST_Dot && exp.property == "toString" && this.args.length == 0) {
return make_node(AST_Binary, this, {
left: exp.expression,
operator: "+",
right: make_node(AST_String, this, { value: "" })
});
}
}
return this;
});
AST_New.DEFMETHOD("optimize", function(compressor){
if (compressor.option("unsafe")) {
var exp = this.expression;
if (exp instanceof AST_SymbolRef && exp.undeclared) {
switch (exp.name) {
case "Object":
case "RegExp":
case "Function":
case "Error":
case "Array":
return make_node(AST_Call, this, this).optimize(compressor);
}
}
}
});
SQUEEZE(AST_Seq, function(self, compressor){
@@ -914,6 +974,15 @@ function Compressor(options, false_by_default) {
});
AST_Binary.DEFMETHOD("optimize", function(compressor){
if (compressor.option("comparations")) switch (this.operator) {
case "===":
case "!==":
if ((this.left.is_string() && this.right.is_string()) ||
(this.left.is_boolean() && this.right.is_boolean())) {
this.operator = this.operator.substr(0, 2);
}
break;
}
if (compressor.option("booleans") && compressor.in_boolean_context()) switch (this.operator) {
case "&&":
var ll = this.left.evaluate(compressor), left = ll[0];
@@ -984,12 +1053,39 @@ function Compressor(options, false_by_default) {
return self.alternative;
}
}
var rev = self.clone();
rev.condition = cond[0].negate(compressor);
var tmp = rev.consequent;
rev.consequent = rev.alternative;
rev.alternative = tmp;
return best_of(self, rev);
var negated = cond[0].negate(compressor);
if (best_of(cond[0], negated) === negated) {
self = make_node(AST_Conditional, self, {
condition: negated,
consequent: self.alternative,
alternative: self.consequent
});
}
var consequent = self.consequent;
var alternative = self.alternative;
if (consequent instanceof AST_Assign
&& alternative instanceof AST_Assign
&& consequent.operator == alternative.operator
// XXX: this is a rather expensive way to test two node's equivalence:
&& consequent.left.print_to_string() == alternative.left.print_to_string()
) {
/*
* Stuff like this:
* if (foo) exp = something; else exp = something_else;
* ==>
* exp = foo ? something : something_else;
*/
self = make_node(AST_Assign, self, {
operator: consequent.operator,
left: consequent.left,
right: make_node(AST_Conditional, self, {
condition: self.condition,
consequent: consequent.right,
alternative: alternative.right
})
});
}
return self;
});
SQUEEZE(AST_Array, function(self, compressor){

View File

@@ -313,6 +313,9 @@ function OutputStream(options) {
};
AST_Node.DEFMETHOD("print_to_string", function(options){
options = defaults(options, {
beautify: false
});
var s = OutputStream(options);
this.print(s);
return s.get();

View File

@@ -97,9 +97,9 @@ function repeat_string(str, i) {
};
function defaults(args, defs) {
var ret = {};
if (args === true)
args = {};
var ret = args || {};
for (var i in defs) if (HOP(defs, i)) {
ret[i] = (args && HOP(args, i)) ? args[i] : defs[i];
}

View File

@@ -72,3 +72,18 @@ ifs_3_should_warn: {
var jj; foo(); // 2
}
}
ifs_4: {
options = {
conditionals: true
};
input: {
if (foo && bar) {
x(foo)[10].bar.baz = something();
} else
x(foo)[10].bar.baz = something_else();
}
expect: {
x(foo)[10].bar.baz = (foo && bar) ? something() : something_else();
}
}