When hoisting variables, try to merge in assignments that follow.
This commit is contained in:
@@ -883,18 +883,23 @@ merge(Compressor.prototype, {
|
|||||||
&& !self.uses_eval
|
&& !self.uses_eval
|
||||||
) {
|
) {
|
||||||
var in_use = [];
|
var in_use = [];
|
||||||
|
var initializations = new Dictionary();
|
||||||
// pass 1: find out which symbols are directly used in
|
// pass 1: find out which symbols are directly used in
|
||||||
// this scope (not in nested scopes).
|
// this scope (not in nested scopes).
|
||||||
var scope = this;
|
var scope = this;
|
||||||
var tw = new TreeWalker(function(node, descend){
|
var tw = new TreeWalker(function(node, descend){
|
||||||
if (node !== self) {
|
if (node !== self) {
|
||||||
if (node instanceof AST_Defun) {
|
if (node instanceof AST_Defun) {
|
||||||
|
initializations.add(node.name.name, node);
|
||||||
return true; // don't go in nested scopes
|
return true; // don't go in nested scopes
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Definitions && scope === self) {
|
if (node instanceof AST_Definitions && scope === self) {
|
||||||
node.definitions.forEach(function(def){
|
node.definitions.forEach(function(def){
|
||||||
if (def.value && def.value.has_side_effects()) {
|
if (def.value) {
|
||||||
def.value.walk(tw);
|
initializations.add(def.name.name, def.value);
|
||||||
|
if (def.value.has_side_effects()) {
|
||||||
|
def.value.walk(tw);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
@@ -919,16 +924,15 @@ merge(Compressor.prototype, {
|
|||||||
for (var i = 0; i < in_use.length; ++i) {
|
for (var i = 0; i < in_use.length; ++i) {
|
||||||
in_use[i].orig.forEach(function(decl){
|
in_use[i].orig.forEach(function(decl){
|
||||||
// undeclared globals will be instanceof AST_SymbolRef
|
// undeclared globals will be instanceof AST_SymbolRef
|
||||||
if (decl instanceof AST_SymbolDeclaration) {
|
var init = initializations.get(decl.name);
|
||||||
decl.init.forEach(function(init){
|
if (init) init.forEach(function(init){
|
||||||
var tw = new TreeWalker(function(node){
|
var tw = new TreeWalker(function(node){
|
||||||
if (node instanceof AST_SymbolRef) {
|
if (node instanceof AST_SymbolRef) {
|
||||||
push_uniq(in_use, node.definition());
|
push_uniq(in_use, node.definition());
|
||||||
}
|
}
|
||||||
});
|
|
||||||
init.walk(tw);
|
|
||||||
});
|
});
|
||||||
}
|
init.walk(tw);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// pass 3: we should drop declarations not in_use
|
// pass 3: we should drop declarations not in_use
|
||||||
@@ -1100,13 +1104,62 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
self = self.transform(tt);
|
self = self.transform(tt);
|
||||||
if (vars_found > 0) hoisted.unshift(make_node(AST_Var, self, {
|
if (vars_found > 0) {
|
||||||
definitions: vars.map(function(def){
|
// collect only vars which don't show up in self's arguments list
|
||||||
def = def.clone();
|
var defs = [];
|
||||||
def.value = null;
|
vars.each(function(def, name){
|
||||||
return def;
|
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);
|
self.body = dirs.concat(hoisted, self.body);
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
|
|||||||
11
lib/scope.js
11
lib/scope.js
@@ -110,9 +110,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
labels.del(l.name);
|
labels.del(l.name);
|
||||||
return true; // no descend again
|
return true; // no descend again
|
||||||
}
|
}
|
||||||
if (node instanceof AST_SymbolDeclaration) {
|
|
||||||
node.init_scope_vars();
|
|
||||||
}
|
|
||||||
if (node instanceof AST_Symbol) {
|
if (node instanceof AST_Symbol) {
|
||||||
node.scope = scope;
|
node.scope = scope;
|
||||||
}
|
}
|
||||||
@@ -128,8 +125,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
// scope. Don't like this fix but seems we can't do any
|
// scope. Don't like this fix but seems we can't do any
|
||||||
// better. IE: please die. Please!
|
// better. IE: please die. Please!
|
||||||
(node.scope = scope.parent_scope).def_function(node);
|
(node.scope = scope.parent_scope).def_function(node);
|
||||||
|
|
||||||
node.init.push(tw.parent());
|
|
||||||
}
|
}
|
||||||
else if (node instanceof AST_SymbolDefun) {
|
else if (node instanceof AST_SymbolDefun) {
|
||||||
// Careful here, the scope where this should be defined is
|
// Careful here, the scope where this should be defined is
|
||||||
@@ -138,14 +133,12 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
// instanceof AST_Scope) but we get to the symbol a bit
|
// instanceof AST_Scope) but we get to the symbol a bit
|
||||||
// later.
|
// later.
|
||||||
(node.scope = scope.parent_scope).def_function(node);
|
(node.scope = scope.parent_scope).def_function(node);
|
||||||
node.init.push(tw.parent());
|
|
||||||
}
|
}
|
||||||
else if (node instanceof AST_SymbolVar
|
else if (node instanceof AST_SymbolVar
|
||||||
|| node instanceof AST_SymbolConst) {
|
|| node instanceof AST_SymbolConst) {
|
||||||
var def = scope.def_variable(node);
|
var def = scope.def_variable(node);
|
||||||
def.constant = node instanceof AST_SymbolConst;
|
def.constant = node instanceof AST_SymbolConst;
|
||||||
def = tw.parent();
|
def = tw.parent();
|
||||||
if (def.value) node.init.push(def);
|
|
||||||
}
|
}
|
||||||
else if (node instanceof AST_SymbolCatch) {
|
else if (node instanceof AST_SymbolCatch) {
|
||||||
// XXX: this is wrong according to ECMA-262 (12.4). the
|
// XXX: this is wrong according to ECMA-262 (12.4). the
|
||||||
@@ -246,10 +239,6 @@ AST_SymbolRef.DEFMETHOD("reference", function() {
|
|||||||
this.frame = this.scope.nesting - def.scope.nesting;
|
this.frame = this.scope.nesting - def.scope.nesting;
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_SymbolDeclaration.DEFMETHOD("init_scope_vars", function(){
|
|
||||||
this.init = [];
|
|
||||||
});
|
|
||||||
|
|
||||||
AST_Label.DEFMETHOD("init_scope_vars", function(){
|
AST_Label.DEFMETHOD("init_scope_vars", function(){
|
||||||
this.references = [];
|
this.references = [];
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -255,6 +255,14 @@ Dictionary.prototype = {
|
|||||||
this._values["$" + key] = val;
|
this._values["$" + key] = val;
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
add: function(key, val) {
|
||||||
|
if (this.has(key)) {
|
||||||
|
this.get(key).push(val);
|
||||||
|
} else {
|
||||||
|
this.set(key, [ val ]);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
},
|
||||||
get: function(key) { return this._values["$" + key] },
|
get: function(key) { return this._values["$" + key] },
|
||||||
del: function(key) {
|
del: function(key) {
|
||||||
if (this.has(key)) {
|
if (this.has(key)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user