When hoisting variables, try to merge in assignments that follow.

This commit is contained in:
Mihai Bazon
2012-12-05 12:30:25 +02:00
parent dbe33bbfc5
commit 320c110b33
3 changed files with 79 additions and 29 deletions

View File

@@ -883,18 +883,23 @@ merge(Compressor.prototype, {
&& !self.uses_eval
) {
var in_use = [];
var initializations = new Dictionary();
// pass 1: find out which symbols are directly used in
// this scope (not in nested scopes).
var scope = this;
var tw = new TreeWalker(function(node, descend){
if (node !== self) {
if (node instanceof AST_Defun) {
initializations.add(node.name.name, node);
return true; // don't go in nested scopes
}
if (node instanceof AST_Definitions && scope === self) {
node.definitions.forEach(function(def){
if (def.value && def.value.has_side_effects()) {
def.value.walk(tw);
if (def.value) {
initializations.add(def.name.name, def.value);
if (def.value.has_side_effects()) {
def.value.walk(tw);
}
}
});
return true;
@@ -919,16 +924,15 @@ merge(Compressor.prototype, {
for (var i = 0; i < in_use.length; ++i) {
in_use[i].orig.forEach(function(decl){
// undeclared globals will be instanceof AST_SymbolRef
if (decl instanceof AST_SymbolDeclaration) {
decl.init.forEach(function(init){
var tw = new TreeWalker(function(node){
if (node instanceof AST_SymbolRef) {
push_uniq(in_use, node.definition());
}
});
init.walk(tw);
var init = initializations.get(decl.name);
if (init) init.forEach(function(init){
var tw = new TreeWalker(function(node){
if (node instanceof AST_SymbolRef) {
push_uniq(in_use, node.definition());
}
});
}
init.walk(tw);
});
});
}
// pass 3: we should drop declarations not in_use
@@ -1100,13 +1104,62 @@ merge(Compressor.prototype, {
}
);
self = self.transform(tt);
if (vars_found > 0) hoisted.unshift(make_node(AST_Var, self, {
definitions: vars.map(function(def){
def = def.clone();
def.value = null;
return def;
})
}));
if (vars_found > 0) {
// collect only vars which don't show up in self's arguments list
var defs = [];
vars.each(function(def, name){
if (self instanceof AST_Lambda
&& find_if(function(x){ return x.name == def.name.name },
self.argnames)) {
vars.del(name);
} else {
def = def.clone();
def.value = null;
defs.push(def);
vars.set(name, def);
}
});
if (defs.length > 0) {
// try to merge in assignments
for (var i = 0; i < self.body.length;) {
if (self.body[i] instanceof AST_SimpleStatement) {
var expr = self.body[i].body, sym, assign;
if (expr instanceof AST_Assign
&& expr.operator == "="
&& (sym = expr.left) instanceof AST_Symbol
&& vars.has(sym.name))
{
var def = vars.get(sym.name);
if (def.value) break;
def.value = expr.right;
remove(defs, def);
defs.push(def);
self.body.splice(i, 1);
continue;
}
if (expr instanceof AST_Seq
&& (assign = expr.car) instanceof AST_Assign
&& assign.operator == "="
&& (sym = assign.left) instanceof AST_Symbol
&& vars.has(sym.name))
{
var def = vars.get(sym.name);
if (def.value) break;
def.value = assign.right;
remove(defs, def);
defs.push(def);
self.body[i].body = expr.cdr;
continue;
}
}
break;
}
defs = make_node(AST_Var, self, {
definitions: defs
});
hoisted.push(defs);
};
}
self.body = dirs.concat(hoisted, self.body);
}
return self;