support for hoisting declarations
authorMihai Bazon <mihai@bazon.net>
Wed, 5 Sep 2012 10:43:34 +0000 (13:43 +0300)
committerMihai Bazon <mihai@bazon.net>
Wed, 5 Sep 2012 10:43:34 +0000 (13:43 +0300)
and finally it seems we beat v1 in terms of compression

lib/compress.js

index 71b20ed..3d2f540 100644 (file)
@@ -64,7 +64,8 @@ function Compressor(options, false_by_default) {
         evaluate      : !false_by_default,
         booleans      : !false_by_default,
         dwloops       : !false_by_default,
-        hoist         : !false_by_default,
+        hoist_funs    : !false_by_default,
+        hoist_vars    : !false_by_default,
 
         warnings      : true
     });
@@ -102,7 +103,7 @@ function Compressor(options, false_by_default) {
     };
 };
 
-(function(){
+(function(undefined){
 
     AST_Node.DEFMETHOD("squeeze", function(){
         return this;
@@ -124,7 +125,7 @@ function Compressor(options, false_by_default) {
             compressor.push_node(this);
             var new_node = squeeze(this, compressor);
             compressor.pop_node();
-            return new_node || this;
+            return new_node !== undefined ? new_node : this;
         });
     };
 
@@ -148,12 +149,9 @@ function Compressor(options, false_by_default) {
 
     function eliminate_spurious_blocks(statements) {
         return statements.reduce(function(a, stat){
-            if (stat.TYPE == "BlockStatement") {
-                // XXX: no instanceof here because we would catch
-                // AST_Lambda-s and other blocks too.  perhaps we
-                // should refine the hierarchy.
+            if (stat instanceof AST_BlockStatement) {
                 a.push.apply(a, stat.body);
-            } else {
+            } else if (!(stat instanceof AST_EmptyStatement)) {
                 a.push(stat);
             }
             return a;
@@ -494,7 +492,49 @@ function Compressor(options, false_by_default) {
     });
 
     AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){
-        if (compressor.option("hoist")) {
+        var hoist_funs = compressor.option("hoist_funs");
+        var hoist_vars = compressor.option("hoist_vars");
+        if (hoist_funs || hoist_vars) {
+            var self = this;
+            var hoisted = [];
+            var defuns = {};
+            var vars = {}, vars_found = 0;
+            var tw = new TreeWalker(function(node){
+                if (node !== self) {
+                    if (node instanceof AST_Defun && hoist_funs) {
+                        hoisted.push(node.clone());
+                        node.hoisted = true;
+                        defuns[node.name.name] = true;
+                    }
+                    if (node instanceof AST_Var && hoist_vars) {
+                        node.definitions.forEach(function(def){
+                            vars[def.name.name] = def;
+                            ++vars_found;
+                        });
+                        node.hoisted = true;
+                    }
+                    if (node instanceof AST_Scope)
+                        return true;
+                }
+            });
+            self.walk(tw);
+            if (vars_found > 0) {
+                if (self instanceof AST_Lambda && !self.uses_arguments) {
+                    for (var i in vars) if (HOP(vars, i)) {
+                        self.argnames.push(vars[i].name);
+                    }
+                } else {
+                    var node = make_node(AST_Var, self, {
+                        definitions: Object.keys(vars).map(function(name){
+                            var def = vars[name].clone();
+                            def.value = null;
+                            return def;
+                        })
+                    });
+                    hoisted.unshift(node);
+                }
+            }
+            self.body = hoisted.concat(self.body);
         }
     });
 
@@ -665,13 +705,49 @@ function Compressor(options, false_by_default) {
 
     AST_Definitions.DEFMETHOD("remove_initializers", function(){
         this.definitions = this.definitions.map(function(def){
-            var def = def.clone();
+            def = def.clone();
             def.value = null;
             return def;
         });
     });
 
+    AST_Definitions.DEFMETHOD("to_assignments", function(){
+        var assignments = this.definitions.reduce(function(a, def){
+            if (def.value) {
+                a.push(make_node(AST_Assign, def, {
+                    operator : "=",
+                    left     : def.name,
+                    right    : def.value
+                }));
+            }
+            return a;
+        }, []);
+        if (assignments.length == 0) return null;
+        return (function seq(list){
+            var first = list[0];
+            if (list.length == 1) return first;
+            return make_node(AST_Seq, first, {
+                first: first,
+                second: seq(list.slice(1))
+            });
+        })(assignments);
+    });
+
     SQUEEZE(AST_Definitions, function(self, compressor){
+        if (self.hoisted) {
+            var seq = self.to_assignments();
+            var p = compressor.parent();
+            if (seq) seq = seq.squeeze(compressor);
+            if (p instanceof AST_ForIn && p.init === self) {
+                if (seq == null) return self.definitions[0].name; //XXX: is this fine?
+                return seq;
+            }
+            if (p instanceof AST_For && p.init === self) {
+                return seq;
+            }
+            if (!seq) return make_node(AST_EmptyStatement, self);
+            return make_node(AST_SimpleStatement, self, { body: seq });
+        }
         self = self.clone();
         self.definitions = do_list(self.definitions, compressor);
         return self;
@@ -684,10 +760,11 @@ function Compressor(options, false_by_default) {
     });
 
     SQUEEZE(AST_Lambda, function(self, compressor){
+        if (self.hoisted) return make_node(AST_EmptyStatement, self);
         self = self.clone();
-        self.hoist_declarations(compressor);
         if (self.name) self.name = self.name.squeeze(compressor);
         self.argnames = do_list(self.argnames, compressor);
+        self.hoist_declarations(compressor);
         self.body = tighten_body(self.body, compressor);
         return self;
     });