optimize constant switch blocks

ref. mishoo/UglifyJS#441
This commit is contained in:
Mihai Bazon
2012-11-14 12:06:07 +02:00
parent 60c0f40250
commit dba8da4800
2 changed files with 258 additions and 2 deletions

View File

@@ -826,10 +826,12 @@ merge(Compressor.prototype, {
(function(def){
def(AST_Statement, function(){ return null });
def(AST_Jump, function(){ return this });
def(AST_BlockStatement, function(){
function block_aborts(){
var n = this.body.length;
return n > 0 && aborts(this.body[n - 1]);
});
};
def(AST_BlockStatement, block_aborts);
def(AST_SwitchBranch, block_aborts);
def(AST_If, function(){
return this.alternative && aborts(this.body) && aborts(this.alternative);
});
@@ -1360,6 +1362,74 @@ merge(Compressor.prototype, {
if (stat instanceof AST_Break && loop_body(compressor.loopcontrol_target(stat.label)) === self)
last_branch.body.pop();
}
var exp = self.expression.evaluate(compressor);
out: if (exp.length == 2) try {
// constant expression
self.expression = exp[0];
if (!compressor.option("dead_code")) break out;
var value = exp[1];
var in_substat = false;
var started = false;
var stopped = false;
var ruined = false;
var tt = new TreeTransformer(function(node, descend, in_list){
if (node instanceof AST_Lambda || node instanceof AST_SimpleStatement) {
// no need to descend these node types
return node;
}
else if (node instanceof AST_Switch) {
if (node === self) {
node = node.clone();
descend(node, this);
return ruined ? node : make_node(AST_BlockStatement, node, {
body: node.body.reduce(function(a, branch){
return a.concat(branch.body);
}, [])
}).transform(compressor);
}
}
else if (node instanceof AST_StatementWithBody || node instanceof AST_Switch || node instanceof AST_Try) {
var save_substat = in_substat;
in_substat = true;
descend(node, this);
in_substat = save_substat;
return node;
}
else if (node instanceof AST_Break && this.loopcontrol_target(node.label) === self) {
if (in_substat) {
// won't handle situations like if (foo) break;
ruined = true;
return node;
} else {
stopped = true;
}
return in_list ? MAP.skip : make_node(AST_EmptyStatement, node);
}
else if (node instanceof AST_SwitchBranch && this.parent() === self) {
if (stopped) return MAP.skip;
if (node instanceof AST_Case) {
var exp = node.expression.evaluate(compressor);
if (exp.length < 2) {
// got a case with non-constant expression, baling out
throw self;
}
if (exp[1] === value || started) {
started = true;
if (aborts(node)) stopped = true;
descend(node, this);
return node;
}
return MAP.skip;
}
descend(node, this);
return node;
}
});
tt.stack = compressor.stack; // so that's able to see parent nodes
self = self.transform(tt);
} catch(ex) {
if (ex !== self) throw ex;
}
return self;
});