[Pkg-javascript-commits] [uglifyjs] 23/228: clean up `negate_iife` - remove extra tree scanning phase for `negate_iife` - `negate_iife` now only deals with the narrowest form, i.e. IIFE sitting directly under `AST_SimpleStatement` - `booleans`, `conditionals` etc. will now take care the rest via more accurate accounting - `a(); void b(); ` => `a(); b(); `

Jonas Smedegaard dr at jones.dk
Sat Apr 15 14:25:13 UTC 2017


This is an automated email from the git hooks/post-receive script.

js pushed a commit to branch master
in repository uglifyjs.

commit f0ff6189be3a75cd4ccb1c38051ec27f9b30d67f
Author: alexlamsl <alexlamsl at gmail.com>
Date:   Sat Feb 18 19:11:57 2017 +0800

    clean up `negate_iife`
    - remove extra tree scanning phase for `negate_iife`
    - `negate_iife` now only deals with the narrowest form, i.e. IIFE sitting directly under `AST_SimpleStatement`
    - `booleans`, `conditionals` etc. will now take care the rest via more accurate accounting
    - `a(); void b();` => `a(); b();`
    
    fixes #1288
    closes #1451
---
 lib/compress.js              | 142 ++++++++++++++++++++---------------------
 lib/output.js                |  25 --------
 lib/utils.js                 |  23 +++++++
 test/compress/negate-iife.js | 148 +++++++++++++++++++++++++++++++++++++++++++
 test/compress/sequences.js   |  38 +++++++++++
 5 files changed, 277 insertions(+), 99 deletions(-)

diff --git a/lib/compress.js b/lib/compress.js
index 536b751..b66c558 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -278,6 +278,15 @@ merge(Compressor.prototype, {
         return x;
     };
 
+    var readOnlyPrefix = makePredicate("! ~ + - void typeof");
+    function statement_to_expression(stat) {
+        if (stat.body instanceof AST_UnaryPrefix && readOnlyPrefix(stat.body.operator)) {
+            return stat.body.expression;
+        } else {
+            return stat.body;
+        }
+    }
+
     function tighten_body(statements, compressor) {
         var CHANGED, max_iter = 10;
         do {
@@ -303,10 +312,6 @@ merge(Compressor.prototype, {
             }
         } while (CHANGED && max_iter-- > 0);
 
-        if (compressor.option("negate_iife")) {
-            negate_iifes(statements, compressor);
-        }
-
         return statements;
 
         function collapse_single_use_vars(statements, compressor) {
@@ -753,7 +758,7 @@ merge(Compressor.prototype, {
                     if (seqLength(seq) >= compressor.sequences_limit) {
                         push_seq();
                     }
-                    seq.push(stat.body);
+                    seq.push(seq.length > 0 ? statement_to_expression(stat) : stat.body);
                 } else {
                     push_seq();
                     ret.push(stat);
@@ -802,7 +807,7 @@ merge(Compressor.prototype, {
                                 stat.init = cons_seq(stat.init);
                             }
                             else if (!stat.init) {
-                                stat.init = prev.body;
+                                stat.init = statement_to_expression(prev);
                                 ret.pop();
                             }
                         } catch(ex) {
@@ -859,50 +864,6 @@ merge(Compressor.prototype, {
             }, []);
         };
 
-        function negate_iifes(statements, compressor) {
-            function is_iife_call(node) {
-                if (node instanceof AST_Call) {
-                    return node.expression instanceof AST_Function || is_iife_call(node.expression);
-                }
-                return false;
-            }
-
-            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_New) {
-                                return node;
-                            }
-                            if (is_iife_call(node)) {
-                                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_functions_from_statement_array(statements) {
@@ -1007,7 +968,15 @@ merge(Compressor.prototype, {
         return ast1.print_to_string().length >
             ast2.print_to_string().length
             ? ast2 : ast1;
-    };
+    }
+
+    function best_of_statement(ast1, ast2) {
+        return best_of(make_node(AST_SimpleStatement, ast1, {
+            body: ast1
+        }), make_node(AST_SimpleStatement, ast2, {
+            body: ast2
+        })).body;
+    }
 
     // methods to evaluate a constant expression
     (function (def){
@@ -1227,7 +1196,17 @@ merge(Compressor.prototype, {
                 operator: "!",
                 expression: exp
             });
-        };
+        }
+        function best(orig, alt, first_in_statement) {
+            var negated = basic_negation(orig);
+            if (first_in_statement) {
+                var stat = make_node(AST_SimpleStatement, alt, {
+                    body: alt
+                });
+                return best_of(negated, stat) === stat ? alt : negated;
+            }
+            return best_of(negated, alt);
+        }
         def(AST_Node, function(){
             return basic_negation(this);
         });
@@ -1247,13 +1226,13 @@ merge(Compressor.prototype, {
             self.cdr = self.cdr.negate(compressor);
             return self;
         });
-        def(AST_Conditional, function(compressor){
+        def(AST_Conditional, function(compressor, first_in_statement){
             var self = this.clone();
             self.consequent = self.consequent.negate(compressor);
             self.alternative = self.alternative.negate(compressor);
-            return best_of(basic_negation(this), self);
+            return best(this, self, first_in_statement);
         });
-        def(AST_Binary, function(compressor){
+        def(AST_Binary, function(compressor, first_in_statement){
             var self = this.clone(), op = this.operator;
             if (compressor.option("unsafe_comps")) {
                 switch (op) {
@@ -1270,20 +1249,20 @@ merge(Compressor.prototype, {
               case "!==": self.operator = "==="; return self;
               case "&&":
                 self.operator = "||";
-                self.left = self.left.negate(compressor);
+                self.left = self.left.negate(compressor, first_in_statement);
                 self.right = self.right.negate(compressor);
-                return best_of(basic_negation(this), self);
+                return best(this, self, first_in_statement);
               case "||":
                 self.operator = "&&";
-                self.left = self.left.negate(compressor);
+                self.left = self.left.negate(compressor, first_in_statement);
                 self.right = self.right.negate(compressor);
-                return best_of(basic_negation(this), self);
+                return best(this, self, first_in_statement);
             }
             return basic_negation(this);
         });
     })(function(node, func){
-        node.DEFMETHOD("negate", function(compressor){
-            return func.call(this, compressor);
+        node.DEFMETHOD("negate", function(compressor, first_in_statement){
+            return func.call(this, compressor, first_in_statement);
         });
     });
 
@@ -1954,8 +1933,8 @@ merge(Compressor.prototype, {
             return make_node(AST_SimpleStatement, self, {
                 body: make_node(AST_Conditional, self, {
                     condition   : self.condition,
-                    consequent  : self.body.body,
-                    alternative : self.alternative.body
+                    consequent  : statement_to_expression(self.body),
+                    alternative : statement_to_expression(self.alternative)
                 })
             }).transform(compressor);
         }
@@ -1971,14 +1950,14 @@ merge(Compressor.prototype, {
                 body: make_node(AST_Binary, self, {
                     operator : "||",
                     left     : negated,
-                    right    : self.body.body
+                    right    : statement_to_expression(self.body)
                 })
             }).transform(compressor);
             return make_node(AST_SimpleStatement, self, {
                 body: make_node(AST_Binary, self, {
                     operator : "&&",
                     left     : self.condition,
-                    right    : self.body.body
+                    right    : statement_to_expression(self.body)
                 })
             }).transform(compressor);
         }
@@ -1989,7 +1968,7 @@ merge(Compressor.prototype, {
                 body: make_node(AST_Binary, self, {
                     operator : "||",
                     left     : self.condition,
-                    right    : self.alternative.body
+                    right    : statement_to_expression(self.alternative)
                 })
             }).transform(compressor);
         }
@@ -2372,7 +2351,19 @@ merge(Compressor.prototype, {
                 }
             }
         }
-        return self.evaluate(compressor)[0];
+        if (compressor.option("negate_iife")
+            && compressor.parent() instanceof AST_SimpleStatement
+            && is_iife_call(self)) {
+            return self.negate(compressor, true);
+        }
+        return self;
+
+        function is_iife_call(node) {
+            if (node instanceof AST_Call && !(node instanceof AST_New)) {
+                return node.expression instanceof AST_Function || is_iife_call(node.expression);
+            }
+            return false;
+        }
     });
 
     OPT(AST_New, function(self, compressor){
@@ -2459,6 +2450,10 @@ merge(Compressor.prototype, {
                     // !!foo ==> foo, if we're in boolean context
                     return e.expression;
                 }
+                if (e instanceof AST_Binary) {
+                    var statement = first_in_statement(compressor);
+                    self = (statement ? best_of_statement : best_of)(self, e.negate(compressor, statement));
+                }
                 break;
               case "typeof":
                 // typeof always returns a non-empty string, thus it's
@@ -2472,9 +2467,6 @@ merge(Compressor.prototype, {
                 }
                 return make_node(AST_True, self);
             }
-            if (e instanceof AST_Binary && self.operator == "!") {
-                self = best_of(self, e.negate(compressor));
-            }
         }
         return self.evaluate(compressor)[0];
     });
@@ -2651,11 +2643,12 @@ merge(Compressor.prototype, {
         if (compressor.option("comparisons") && self.is_boolean()) {
             if (!(compressor.parent() instanceof AST_Binary)
                 || compressor.parent() instanceof AST_Assign) {
+                var statement = first_in_statement(compressor);
                 var negated = make_node(AST_UnaryPrefix, self, {
                     operator: "!",
-                    expression: self.negate(compressor)
+                    expression: self.negate(compressor, statement)
                 });
-                self = best_of(self, negated);
+                self = (statement ? best_of_statement : best_of)(self, negated);
             }
             if (compressor.option("unsafe_comps")) {
                 switch (self.operator) {
@@ -2859,8 +2852,9 @@ merge(Compressor.prototype, {
                 return maintain_this_binding(compressor.parent(), self, self.alternative);
             }
         }
-        var negated = cond[0].negate(compressor);
-        if (best_of(cond[0], negated) === negated) {
+        var statement = first_in_statement(compressor);
+        var negated = cond[0].negate(compressor, statement);
+        if ((statement ? best_of_statement : best_of)(cond[0], negated) === negated) {
             self = make_node(AST_Conditional, self, {
                 condition: negated,
                 consequent: self.alternative,
diff --git a/lib/output.js b/lib/output.js
index 50e5aa4..b6f0a87 100644
--- a/lib/output.js
+++ b/lib/output.js
@@ -425,7 +425,6 @@ function OutputStream(options) {
         pos             : function() { return current_pos },
         push_node       : function(node) { stack.push(node) },
         pop_node        : function() { return stack.pop() },
-        stack           : function() { return stack },
         parent          : function(n) {
             return stack[stack.length - 2 - (n || 0)];
         }
@@ -1334,30 +1333,6 @@ function OutputStream(options) {
         }
     };
 
-    // return true if the node at the top of the stack (that means the
-    // innermost node in the current output) is lexically the first in
-    // a statement.
-    function first_in_statement(output) {
-        var a = output.stack(), i = a.length, node = a[--i], p = a[--i];
-        while (i > 0) {
-            if (p instanceof AST_Statement && p.body === node)
-                return true;
-            if ((p instanceof AST_Seq           && p.car === node        ) ||
-                (p instanceof AST_Call          && p.expression === node && !(p instanceof AST_New) ) ||
-                (p instanceof AST_Dot           && p.expression === node ) ||
-                (p instanceof AST_Sub           && p.expression === node ) ||
-                (p instanceof AST_Conditional   && p.condition === node  ) ||
-                (p instanceof AST_Binary        && p.left === node       ) ||
-                (p instanceof AST_UnaryPostfix  && p.expression === node ))
-            {
-                node = p;
-                p = a[--i];
-            } else {
-                return false;
-            }
-        }
-    };
-
     // self should be AST_New.  decide if we want to show parens or not.
     function need_constructor_parens(self, output) {
         // Always print parentheses with arguments
diff --git a/lib/utils.js b/lib/utils.js
index d0a21ac..a0571d6 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -320,3 +320,26 @@ Dictionary.fromObject = function(obj) {
 function HOP(obj, prop) {
     return Object.prototype.hasOwnProperty.call(obj, prop);
 }
+
+// return true if the node at the top of the stack (that means the
+// innermost node in the current output) is lexically the first in
+// a statement.
+function first_in_statement(stack) {
+    var node = stack.parent(-1);
+    for (var i = 0, p; p = stack.parent(i); i++) {
+        if (p instanceof AST_Statement && p.body === node)
+            return true;
+        if ((p instanceof AST_Seq           && p.car === node        ) ||
+            (p instanceof AST_Call          && p.expression === node && !(p instanceof AST_New) ) ||
+            (p instanceof AST_Dot           && p.expression === node ) ||
+            (p instanceof AST_Sub           && p.expression === node ) ||
+            (p instanceof AST_Conditional   && p.condition === node  ) ||
+            (p instanceof AST_Binary        && p.left === node       ) ||
+            (p instanceof AST_UnaryPostfix  && p.expression === node ))
+        {
+            node = p;
+        } else {
+            return false;
+        }
+    }
+}
diff --git a/test/compress/negate-iife.js b/test/compress/negate-iife.js
index 0c11160..312e0f2 100644
--- a/test/compress/negate-iife.js
+++ b/test/compress/negate-iife.js
@@ -10,6 +10,16 @@ negate_iife_1: {
     }
 }
 
+negate_iife_1_off: {
+    options = {
+        negate_iife: false,
+    };
+    input: {
+        (function(){ stuff() })();
+    }
+    expect_exact: '(function(){stuff()})();'
+}
+
 negate_iife_2: {
     options = {
         negate_iife: true
@@ -25,6 +35,20 @@ negate_iife_2: {
 negate_iife_3: {
     options = {
         negate_iife: true,
+        conditionals: true
+    };
+    input: {
+        (function(){ return true })() ? console.log(true) : console.log(false);
+    }
+    expect: {
+        !function(){ return true }() ? console.log(false) : console.log(true);
+    }
+}
+
+negate_iife_3_off: {
+    options = {
+        negate_iife: false,
+        conditionals: true,
     };
     input: {
         (function(){ return true })() ? console.log(true) : console.log(false);
@@ -37,6 +61,7 @@ negate_iife_3: {
 negate_iife_3: {
     options = {
         negate_iife: true,
+        conditionals: true,
         sequences: true
     };
     input: {
@@ -52,6 +77,41 @@ negate_iife_3: {
     }
 }
 
+sequence_off: {
+    options = {
+        negate_iife: false,
+        conditionals: true,
+        sequences: true,
+        passes: 2,
+    };
+    input: {
+        function f() {
+            (function(){ return true })() ? console.log(true) : console.log(false);
+            (function(){
+                console.log("something");
+            })();
+        }
+        function g() {
+            (function(){
+                console.log("something");
+            })();
+            (function(){ return true })() ? console.log(true) : console.log(false);
+        }
+    }
+    expect: {
+        function f() {
+            !function(){ return true }() ? console.log(false) : console.log(true), function(){
+                console.log("something");
+            }();
+        }
+        function g() {
+            (function(){
+                console.log("something");
+            })(), function(){ return true }() ? console.log(true) : console.log(false);
+        }
+    }
+}
+
 negate_iife_4: {
     options = {
         negate_iife: true,
@@ -75,6 +135,29 @@ negate_iife_4: {
     }
 }
 
+negate_iife_4_off: {
+    options = {
+        negate_iife: false,
+        sequences: true,
+        conditionals: true,
+    };
+    input: {
+        if ((function(){ return true })()) {
+            foo(true);
+        } else {
+            bar(false);
+        }
+        (function(){
+            console.log("something");
+        })();
+    }
+    expect: {
+        !function(){ return true }() ? bar(false) : foo(true), function(){
+            console.log("something");
+        }();
+    }
+}
+
 negate_iife_nested: {
     options = {
         negate_iife: true,
@@ -107,6 +190,38 @@ negate_iife_nested: {
     }
 }
 
+negate_iife_nested_off: {
+    options = {
+        negate_iife: false,
+        sequences: true,
+        conditionals: true,
+    };
+    input: {
+        function Foo(f) {
+            this.f = f;
+        }
+        new Foo(function() {
+            (function(x) {
+                (function(y) {
+                    console.log(y);
+                })(x);
+            })(7);
+        }).f();
+    }
+    expect: {
+        function Foo(f) {
+            this.f = f;
+        }
+        new Foo(function() {
+            (function(x) {
+                (function(y) {
+                    console.log(y);
+                })(x);
+            })(7);
+        }).f();
+    }
+}
+
 negate_iife_issue_1073: {
     options = {
         negate_iife: true,
@@ -172,3 +287,36 @@ issue_1254_negate_iife_nested: {
     }
     expect_exact: '!function(){return function(){console.log("test")}}()()()()();'
 }
+
+issue_1288: {
+    options = {
+        negate_iife: true,
+        conditionals: true,
+    };
+    input: {
+        if (w) ;
+        else {
+            (function f() {})();
+        }
+        if (!x) {
+            (function() {
+                x = {};
+            })();
+        }
+        if (y)
+            (function() {})();
+        else
+            (function(z) {
+                return z;
+            })(0);
+    }
+    expect: {
+        w || function f() {}();
+        x || function() {
+            x = {};
+        }();
+        y ? function() {}() : function(z) {
+            return z;
+        }(0);
+    }
+}
diff --git a/test/compress/sequences.js b/test/compress/sequences.js
index 8a3ffe8..d93f523 100644
--- a/test/compress/sequences.js
+++ b/test/compress/sequences.js
@@ -213,3 +213,41 @@ limit_2: {
         i, j, k;
     }
 }
+
+negate_iife_for: {
+    options = {
+        sequences: true,
+        negate_iife: true,
+    };
+    input: {
+        (function() {})();
+        for (i = 0; i < 5; i++) console.log(i);
+
+        (function() {})();
+        for (; i < 5; i++) console.log(i);
+    }
+    expect: {
+        for (!function() {}(), i = 0; i < 5; i++) console.log(i);
+        for (function() {}(); i < 5; i++) console.log(i);
+    }
+}
+
+iife: {
+    options = {
+        sequences: true,
+    };
+    input: {
+        x = 42;
+        (function a() {})();
+        !function b() {}();
+        ~function c() {}();
+        +function d() {}();
+        -function e() {}();
+        void function f() {}();
+        typeof function g() {}();
+    }
+    expect: {
+        x = 42, function a() {}(), function b() {}(), function c() {}(),
+        function d() {}(), function e() {}(), function f() {}(), function g() {}()
+    }
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/uglifyjs.git



More information about the Pkg-javascript-commits mailing list