Move support for `negate_iife` in the compressor, rather than code generator
authorMihai Bazon <mihai.bazon@gmail.com>
Tue, 20 Aug 2013 14:45:52 +0000 (17:45 +0300)
committerMihai Bazon <mihai.bazon@gmail.com>
Tue, 20 Aug 2013 14:45:52 +0000 (17:45 +0300)
(the code generator doesn't maintain enough context to know whether
the return value is important or discarded)

Fixes #272

README.md
lib/compress.js
lib/output.js
test/compress/negate-iife.js [new file with mode: 0644]

index b735eb6..ad394b2 100644 (file)
--- a/README.md
+++ b/README.md
@@ -209,6 +209,9 @@ to set `true`; it's effectively a shortcut for `foo=true`).
   and `x = something(), x` into `x = something()`
 - `warnings` -- display warnings when dropping unreachable code or unused
   declarations etc.
+- `negate_iife` -- negate "Immediately-Called Function Expressions"
+  where the return value is discarded, to avoid the parens that the
+  code generator would insert.
 
 ### The `unsafe` option
 
@@ -296,10 +299,6 @@ can pass additional arguments that control the code output:
   you pass `false` then whenever possible we will use a newline instead of a
   semicolon, leading to more readable output of uglified code (size before
   gzip could be smaller; size after gzip insignificantly larger).
-- `negate-iife` (default `!beautify`) -- prefer negation, rather than
-  parens, for "Immediately-Called Function Expressions".  This defaults to
-  `true` when beautification is off, and `false` if beautification is on;
-  pass it manually to force a value.
 
 ### Keeping copyright notices or other comments
 
index 8dfbc72..8d936ba 100644 (file)
@@ -66,6 +66,7 @@ function Compressor(options, false_by_default) {
         join_vars     : !false_by_default,
         cascade       : !false_by_default,
         side_effects  : !false_by_default,
+        negate_iife   : !false_by_default,
         screw_ie8     : false,
 
         warnings      : true,
@@ -214,6 +215,11 @@ merge(Compressor.prototype, {
                 statements = join_consecutive_vars(statements, compressor);
             }
         } while (CHANGED);
+
+        if (compressor.option("negate_iife")) {
+            negate_iifes(statements, compressor);
+        }
+
         return statements;
 
         function eliminate_spurious_blocks(statements) {
@@ -497,6 +503,40 @@ merge(Compressor.prototype, {
             }, []);
         };
 
+        function negate_iifes(statements, compressor) {
+            statements.forEach(function(stat){
+                if (stat instanceof AST_SimpleStatement) {
+                    stat.body = (function transform(thing) {
+                        return thing.transform(new TreeTransformer(function(node){
+                            if (node instanceof AST_Call && node.expression instanceof AST_Function) {
+                                return make_node(AST_UnaryPrefix, node, {
+                                    operator: "!",
+                                    expression: node
+                                });
+                            }
+                            else if (node instanceof AST_Call) {
+                                node.expression = transform(node.expression);
+                            }
+                            else if (node instanceof AST_Seq) {
+                                node.car = transform(node.car);
+                            }
+                            else if (node instanceof AST_Conditional) {
+                                var expr = transform(node.condition);
+                                if (expr !== node.condition) {
+                                    // it has been negated, reverse
+                                    node.condition = expr;
+                                    var tmp = node.consequent;
+                                    node.consequent = node.alternative;
+                                    node.alternative = tmp;
+                                }
+                            }
+                            return node;
+                        }));
+                    })(stat.body);
+                }
+            });
+        };
+
     };
 
     function extract_declarations_from_unreachable_code(compressor, stat, target) {
index 6d0dac5..b7bcd1e 100644 (file)
@@ -61,7 +61,6 @@ function OutputStream(options) {
         comments      : false,
         preserve_line : false,
         screw_ie8     : false,
-        negate_iife   : !(options && options.beautify),
     }, true);
 
     var indentation = 0;
@@ -351,21 +350,17 @@ function OutputStream(options) {
 
     AST_Node.DEFMETHOD("print", function(stream, force_parens){
         var self = this, generator = self._codegen;
-        stream.push_node(self);
-        var needs_parens = self.needs_parens(stream);
-        var fc = self instanceof AST_Function && stream.option("negate_iife");
-        if (force_parens || (needs_parens && !fc)) {
-            stream.with_parens(function(){
-                self.add_comments(stream);
-                self.add_source_map(stream);
-                generator(self, stream);
-            });
-        } else {
+        function doit() {
             self.add_comments(stream);
-            if (needs_parens && fc) stream.print("!");
             self.add_source_map(stream);
             generator(self, stream);
         }
+        stream.push_node(self);
+        if (force_parens || self.needs_parens(stream)) {
+            stream.with_parens(doit);
+        } else {
+            doit();
+        }
         stream.pop_node();
     });
 
diff --git a/test/compress/negate-iife.js b/test/compress/negate-iife.js
new file mode 100644 (file)
index 0000000..0362ffc
--- /dev/null
@@ -0,0 +1,76 @@
+negate_iife_1: {
+    options = {
+        negate_iife: true
+    };
+    input: {
+        (function(){ stuff() })();
+    }
+    expect: {
+        !function(){ stuff() }();
+    }
+}
+
+negate_iife_2: {
+    options = {
+        negate_iife: true
+    };
+    input: {
+        (function(){ return {} })().x = 10; // should not transform this one
+    }
+    expect: {
+        (function(){ return {} })().x = 10;
+    }
+}
+
+negate_iife_3: {
+    options = {
+        negate_iife: true,
+    };
+    input: {
+        (function(){ return true })() ? console.log(true) : console.log(false);
+    }
+    expect: {
+        !function(){ return true }() ? console.log(false) : console.log(true);
+    }
+}
+
+negate_iife_3: {
+    options = {
+        negate_iife: true,
+        sequences: true
+    };
+    input: {
+        (function(){ return true })() ? console.log(true) : console.log(false);
+        (function(){
+            console.log("something");
+        })();
+    }
+    expect: {
+        !function(){ return true }() ? console.log(false) : console.log(true), function(){
+            console.log("something");
+        }();
+    }
+}
+
+negate_iife_4: {
+    options = {
+        negate_iife: true,
+        sequences: true,
+        conditionals: true,
+    };
+    input: {
+        if ((function(){ return true })()) {
+            console.log(true);
+        } else {
+            console.log(false);
+        }
+        (function(){
+            console.log("something");
+        })();
+    }
+    expect: {
+        !function(){ return true }() ? console.log(false) : console.log(true), function(){
+            console.log("something");
+        }();
+    }
+}