more AST_If optimizations
authorMihai Bazon <mihai@bazon.net>
Wed, 12 Sep 2012 13:10:03 +0000 (16:10 +0300)
committerMihai Bazon <mihai@bazon.net>
Wed, 12 Sep 2012 13:10:03 +0000 (16:10 +0300)
lib/compress.js
lib/utils.js

index 06b133f..02b9e24 100644 (file)
@@ -122,17 +122,17 @@ function Compressor(options, false_by_default) {
     };
 
     function SQUEEZE(nodetype, squeeze) {
-        nodetype.DEFMETHOD("squeeze", function(compressor){
+        nodetype.DEFMETHOD("squeeze", function(compressor, block, index){
             compressor.push_node(this);
-            var new_node = squeeze(this, compressor);
+            var new_node = squeeze(this, compressor, block, index);
             compressor.pop_node();
             return new_node !== undefined ? new_node : this;
         });
     };
 
     function do_list(array, compressor, splice_blocks) {
-        return MAP(array, function(node){
-            node = node.squeeze(compressor);
+        return MAP(array, function(node, i){
+            node = node.squeeze(compressor, array, i);
             if (splice_blocks) {
                 if (node instanceof AST_BlockStatement) {
                     return MAP.splice(eliminate_spurious_blocks(node.body));
@@ -222,7 +222,8 @@ function Compressor(options, false_by_default) {
             }
             else if (i == last
                      && cur instanceof AST_Exit && cur.value
-                     && a.length == 1 && prev instanceof AST_SimpleStatement) {
+                     && a.length > 0
+                     && prev instanceof AST_SimpleStatement) {
                 // it only makes sense to do this transformation
                 // if the AST gets to a single statement.
                 var seq = make_node(AST_Seq, prev, {
@@ -230,7 +231,9 @@ function Compressor(options, false_by_default) {
                     second: cur.value
                 });
                 cur.value = seq;
-                return [ cur ];
+                a.pop();
+                a.push(cur);
+                return a;
             }
             else {
                 a.push(cur);
@@ -459,7 +462,9 @@ function Compressor(options, false_by_default) {
             return basic_negation(this);
         });
     })(function(node, func){
-        node.DEFMETHOD("negate", func);
+        node.DEFMETHOD("negate", function(compressor){
+            return func.call(this, compressor).optimize(compressor);
+        });
     });
 
     // determine if expression has side effects
@@ -494,6 +499,15 @@ function Compressor(options, false_by_default) {
         node.DEFMETHOD("has_side_effects", func);
     });
 
+    // tell me if a statement aborts
+    (function(def){
+        def(AST_StatementBase, function(){ return null });
+        def(AST_Jump, function(){ return this });
+        def(AST_BlockStatement, function(){ return this.body[this.body.length - 1].aborts() });
+    })(function(node, func){
+        node.DEFMETHOD("aborts", func);
+    });
+
     /* -----[ node squeezers ]----- */
 
     SQUEEZE(AST_Debugger, function(self, compressor){
@@ -516,9 +530,13 @@ function Compressor(options, false_by_default) {
     SQUEEZE(AST_BlockStatement, function(self, compressor){
         self = self.clone();
         self.body = tighten_body(self.body, compressor);
-        if (self.body.length == 1)
-            return self.body[0];
-        return self;
+        return self.optimize(compressor);
+    });
+
+    AST_BlockStatement.DEFMETHOD("optimize", function(compressor){
+        if (this.body.length == 1)
+            return this.body[0];
+        return this;
     });
 
     SQUEEZE(AST_Block, function(self, compressor){
@@ -705,16 +723,16 @@ function Compressor(options, false_by_default) {
         return self;
     });
 
-    SQUEEZE(AST_If, function(self, compressor){
+    SQUEEZE(AST_If, function(self, compressor, block, index){
         self = self.clone();
         self.condition = self.condition.squeeze(compressor);
         self.body = self.body.squeeze(compressor);
         if (self.alternative)
             self.alternative = self.alternative.squeeze(compressor);
-        return self.optimize(compressor);
+        return self.optimize(compressor, block, index);
     });
 
-    AST_If.DEFMETHOD("optimize", function(compressor){
+    AST_If.DEFMETHOD("optimize", function(compressor, block, index){
         var self = this;
         if (!compressor.option("conditionals")) return self;
         // if condition can be statically determined, warn and drop
@@ -809,6 +827,49 @@ function Compressor(options, false_by_default) {
                 }).optimize(compressor)
             });
         }
+        if (self.body instanceof AST_Return
+            && !self.body.value
+            && !self.alternative
+            && index < block.length - 1) {
+            if (compressor.parent() instanceof AST_Lambda) {
+                var rest = tighten_body(block.slice(index + 1), compressor);
+                var cond = negated_is_best ? negated : self.condition.negate(compressor);
+                while (rest[0] instanceof AST_If && rest[0].body instanceof AST_Return && !rest[0].alternative) {
+                    cond = make_node(AST_Binary, rest[0], {
+                        operator: "&&",
+                        left: cond,
+                        right: rest[0].condition.negate(compressor)
+                    });
+                    rest.shift();
+                }
+                return MAP.last(make_node(AST_If, self, {
+                    condition: cond,
+                    body: make_node(AST_BlockStatement, block[index + 1], {
+                        body: rest
+                    }).optimize(compressor)
+                }).optimize(compressor));
+            }
+        }
+        if (self.body instanceof AST_If
+            && !self.body.alternative
+            && !self.alternative) {
+            self.condition = make_node(AST_Binary, self.condition, {
+                operator: "&&",
+                left: self.condition,
+                right: self.body.condition
+            });
+            self.body = self.body.body;
+        }
+        var abort = self.body.aborts();
+        if (abort) {
+            if (self.alternative) {
+                var alt = self.alternative;
+                self.alternative = null;
+                return make_node(AST_BlockStatement, self, {
+                    body: [ self, alt ]
+                }).optimize(compressor);
+            }
+        }
         return self;
     });
 
index e9a2f80..6f8334e 100644 (file)
@@ -113,6 +113,8 @@ var MAP = (function(){
         var ret = [], top = [], i;
         function doit() {
             var val = f.call(o, a[i], i);
+            var is_last = val instanceof Last;
+            if (is_last) val = val.v;
             if (val instanceof AtTop) {
                 val = val.v;
                 if (val instanceof Splice) {
@@ -128,16 +130,23 @@ var MAP = (function(){
                     ret.push(val);
                 }
             }
+            return is_last;
         };
-        if (a instanceof Array) for (i = 0; i < a.length; ++i) doit();
-        else for (i in a) if (HOP(a, i)) doit();
+        if (a instanceof Array) {
+            for (i = 0; i < a.length; ++i) if (doit()) break;
+        }
+        else {
+            for (i in a) if (HOP(a, i)) if (doit()) break;
+        }
         return top.concat(ret);
     };
     MAP.at_top = function(val) { return new AtTop(val) };
     MAP.splice = function(val) { return new Splice(val) };
+    MAP.last = function(val) { return new Last(val) };
     var skip = MAP.skip = {};
     function AtTop(val) { this.v = val };
     function Splice(val) { this.v = val };
+    function Last(val) { this.v = val };
     return MAP;
 })();