When hoisting variables, try to merge in assignments that follow.
authorMihai Bazon <mihai@bazon.net>
Wed, 5 Dec 2012 10:30:25 +0000 (12:30 +0200)
committerMihai Bazon <mihai@bazon.net>
Wed, 5 Dec 2012 10:30:25 +0000 (12:30 +0200)
lib/compress.js
lib/scope.js
lib/utils.js

index c1cb146..2355c5e 100644 (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;
index dc637cc..758b61f 100644 (file)
@@ -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 = [];
 });
index 15eed9b..c95b982 100644 (file)
@@ -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)) {