From 320c110b331be9c6cb20f4db1d63aaaa98eef739 Mon Sep 17 00:00:00 2001 From: Mihai Bazon Date: Wed, 5 Dec 2012 12:30:25 +0200 Subject: [PATCH] When hoisting variables, try to merge in assignments that follow. --- lib/compress.js | 89 +++++++++++++++++++++++++++++++++++++++---------- lib/scope.js | 11 ------ lib/utils.js | 8 +++++ 3 files changed, 79 insertions(+), 29 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index c1cb1464..2355c5e6 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -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; diff --git a/lib/scope.js b/lib/scope.js index dc637cc5..758b61f7 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -110,9 +110,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){ labels.del(l.name); return true; // no descend again } - if (node instanceof AST_SymbolDeclaration) { - node.init_scope_vars(); - } if (node instanceof AST_Symbol) { 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 // better. IE: please die. Please! (node.scope = scope.parent_scope).def_function(node); - - node.init.push(tw.parent()); } else if (node instanceof AST_SymbolDefun) { // 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 // later. (node.scope = scope.parent_scope).def_function(node); - node.init.push(tw.parent()); } else if (node instanceof AST_SymbolVar || node instanceof AST_SymbolConst) { var def = scope.def_variable(node); def.constant = node instanceof AST_SymbolConst; def = tw.parent(); - if (def.value) node.init.push(def); } else if (node instanceof AST_SymbolCatch) { // 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; }); -AST_SymbolDeclaration.DEFMETHOD("init_scope_vars", function(){ - this.init = []; -}); - AST_Label.DEFMETHOD("init_scope_vars", function(){ this.references = []; }); diff --git a/lib/utils.js b/lib/utils.js index 15eed9ba..c95b9824 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -255,6 +255,14 @@ Dictionary.prototype = { this._values["$" + key] = val; 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] }, del: function(key) { if (this.has(key)) { -- 2.34.1