[Pkg-javascript-commits] [uglifyjs] 70/228: process code with implicit return statement (#1522)

Jonas Smedegaard dr at jones.dk
Sat Apr 15 14:25:18 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 07accd2fbb78ddbdb427774b3b5287a16fa95b5f
Author: Alex Lam S.L <alexlamsl at gmail.com>
Date:   Fri Mar 3 18:13:07 2017 +0800

    process code with implicit return statement (#1522)
    
    Bookmarklet for instance implicitedly assumes a "completion value" without using `return`.
    The `expression` option now supports such use cases.
    Optimisations on IIFEs also enhanced.
    
    fixes #354
    fixes #543
    fixes #625
    fixes #628
    fixes #640
    closes #1293
---
 README.md                    |   3 +
 lib/compress.js              |  90 +++++++++---
 test/compress/drop-unused.js |   2 +-
 test/compress/evaluate.js    |   8 +-
 test/compress/functions.js   |   8 +-
 test/compress/issue-640.js   | 317 +++++++++++++++++++++++++++++++++++++++++++
 test/compress/negate-iife.js |  87 ++++++++++++
 test/compress/sequences.js   |   2 +-
 8 files changed, 485 insertions(+), 32 deletions(-)

diff --git a/README.md b/README.md
index 628bcde..0b532a8 100644
--- a/README.md
+++ b/README.md
@@ -426,6 +426,9 @@ to set `true`; it's effectively a shortcut for `foo=true`).
   such as `console.info` and/or retain side effects from function arguments
   after dropping the function call then use `pure_funcs` instead.
 
+- `expression` -- default `false`.  Pass `true` to preserve completion values
+  from terminal statements without `return`, e.g. in bookmarklets.
+
 - `keep_fargs` -- default `true`.  Prevents the
   compressor from discarding unused function arguments.  You need this
   for code which relies on `Function.length`.
diff --git a/lib/compress.js b/lib/compress.js
index ec1e717..2cd7912 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -80,6 +80,7 @@ function Compressor(options, false_by_default) {
         screw_ie8     : true,
         drop_console  : false,
         angular       : false,
+        expression    : false,
         warnings      : true,
         global_defs   : {},
         passes        : 1,
@@ -116,12 +117,18 @@ Compressor.prototype = new TreeTransformer;
 merge(Compressor.prototype, {
     option: function(key) { return this.options[key] },
     compress: function(node) {
+        if (this.option("expression")) {
+            node = node.process_expression(true);
+        }
         var passes = +this.options.passes || 1;
         for (var pass = 0; pass < passes && pass < 3; ++pass) {
             if (pass > 0 || this.option("reduce_vars"))
                 node.reset_opt_flags(this, true);
             node = node.transform(this);
         }
+        if (this.option("expression")) {
+            node = node.process_expression(false);
+        }
         return node;
     },
     warn: function(text, props) {
@@ -178,6 +185,42 @@ merge(Compressor.prototype, {
         return this.print_to_string() == node.print_to_string();
     });
 
+    AST_Node.DEFMETHOD("process_expression", function(insert) {
+        var self = this;
+        var tt = new TreeTransformer(function(node) {
+            if (insert && node instanceof AST_SimpleStatement) {
+                return make_node(AST_Return, node, {
+                    value: node.body
+                });
+            }
+            if (!insert && node instanceof AST_Return) {
+                return make_node(AST_SimpleStatement, node, {
+                    body: node.value || make_node(AST_Undefined, node)
+                });
+            }
+            if (node instanceof AST_Lambda && node !== self) {
+                return node;
+            }
+            if (node instanceof AST_Block) {
+                var index = node.body.length - 1;
+                if (index >= 0) {
+                    node.body[index] = node.body[index].transform(tt);
+                }
+            }
+            if (node instanceof AST_If) {
+                node.body = node.body.transform(tt);
+                if (node.alternative) {
+                    node.alternative = node.alternative.transform(tt);
+                }
+            }
+            if (node instanceof AST_With) {
+                node.body = node.body.transform(tt);
+            }
+            return node;
+        });
+        return self.transform(tt);
+    });
+
     AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan){
         var reduce_vars = rescan && compressor.option("reduce_vars");
         var safe_ids = [];
@@ -2030,7 +2073,14 @@ merge(Compressor.prototype, {
         def(AST_Constant, return_null);
         def(AST_This, return_null);
         def(AST_Call, function(compressor, first_in_statement){
-            if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) return this;
+            if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) {
+                if (this.expression instanceof AST_Function) {
+                    var node = this.clone();
+                    node.expression = node.expression.process_expression(false);
+                    return node;
+                }
+                return this;
+            }
             if (this.pure) {
                 compressor.warn("Dropping __PURE__ call [{file}:{line},{col}]", this.start);
                 this.pure.value = this.pure.value.replace(/[@#]__PURE__/g, ' ');
@@ -2522,12 +2572,13 @@ merge(Compressor.prototype, {
     });
 
     OPT(AST_Call, function(self, compressor){
+        var exp = self.expression;
         if (compressor.option("unused")
-            && self.expression instanceof AST_Function
-            && !self.expression.uses_arguments
-            && !self.expression.uses_eval
-            && self.args.length > self.expression.argnames.length) {
-            var end = self.expression.argnames.length;
+            && exp instanceof AST_Function
+            && !exp.uses_arguments
+            && !exp.uses_eval
+            && self.args.length > exp.argnames.length) {
+            var end = exp.argnames.length;
             for (var i = end, len = self.args.length; i < len; i++) {
                 var node = self.args[i].drop_side_effect_free(compressor);
                 if (node) {
@@ -2537,7 +2588,6 @@ merge(Compressor.prototype, {
             self.args.length = end;
         }
         if (compressor.option("unsafe")) {
-            var exp = self.expression;
             if (exp instanceof AST_SymbolRef && exp.undeclared()) {
                 switch (exp.name) {
                   case "Array":
@@ -2711,16 +2761,22 @@ merge(Compressor.prototype, {
                 return best_of(self, node);
             }
         }
-        if (compressor.option("side_effects")) {
-            if (self.expression instanceof AST_Function
-                && self.args.length == 0
-                && !AST_Block.prototype.has_side_effects.call(self.expression, compressor)) {
-                return make_node(AST_Undefined, self).transform(compressor);
+        if (exp instanceof AST_Function) {
+            if (exp.body[0] instanceof AST_Return
+                && exp.body[0].value.is_constant()) {
+                var args = self.args.concat(exp.body[0].value);
+                return AST_Seq.from_array(args).transform(compressor);
+            }
+            if (compressor.option("side_effects")) {
+                if (!AST_Block.prototype.has_side_effects.call(exp, compressor)) {
+                    var args = self.args.concat(make_node(AST_Undefined, self));
+                    return AST_Seq.from_array(args).transform(compressor);
+                }
             }
         }
         if (compressor.option("drop_console")) {
-            if (self.expression instanceof AST_PropAccess) {
-                var name = self.expression.expression;
+            if (exp instanceof AST_PropAccess) {
+                var name = exp.expression;
                 while (name.expression) {
                     name = name.expression;
                 }
@@ -2731,12 +2787,6 @@ merge(Compressor.prototype, {
                 }
             }
         }
-        if (self.args.length == 0
-            && self.expression instanceof AST_Function
-            && self.expression.body[0] instanceof AST_Return
-            && self.expression.body[0].value.is_constant()) {
-            return self.expression.body[0].value;
-        }
         if (compressor.option("negate_iife")
             && compressor.parent() instanceof AST_SimpleStatement
             && is_iife_call(self)) {
diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js
index 4b61318..20dab3b 100644
--- a/test/compress/drop-unused.js
+++ b/test/compress/drop-unused.js
@@ -632,7 +632,7 @@ iife: {
     }
     expect: {
         function f() {
-            ~function() {}(b);
+            b;
         }
     }
 }
diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js
index 26b6e48..6873950 100644
--- a/test/compress/evaluate.js
+++ b/test/compress/evaluate.js
@@ -640,9 +640,7 @@ call_args: {
     expect: {
         const a = 1;
         console.log(1);
-        +function(a) {
-            return 1;
-        }(1);
+        +(1, 1);
     }
 }
 
@@ -663,9 +661,7 @@ call_args_drop_param: {
     expect: {
         const a = 1;
         console.log(1);
-        +function() {
-            return 1;
-        }(b);
+        +(b, 1);
     }
 }
 
diff --git a/test/compress/functions.js b/test/compress/functions.js
index d3d99b5..18505a1 100644
--- a/test/compress/functions.js
+++ b/test/compress/functions.js
@@ -35,9 +35,9 @@ iifes_returning_constants_keep_fargs_true: {
         console.log("okay");
         console.log(123);
         console.log(void 0);
-        console.log(function(x,y,z){return 2}(1,2,3));
-        console.log(function(x,y){return 6}(2,3));
-        console.log(function(x, y){return 6}(2,3,a(),b()));
+        console.log(2);
+        console.log(6);
+        console.log((a(), b(), 6));
     }
 }
 
@@ -71,6 +71,6 @@ iifes_returning_constants_keep_fargs_false: {
         console.log(void 0);
         console.log(2);
         console.log(6);
-        console.log(function(){return 6}(a(),b()));
+        console.log((a(), b(), 6));
     }
 }
diff --git a/test/compress/issue-640.js b/test/compress/issue-640.js
new file mode 100644
index 0000000..dd3f3f2
--- /dev/null
+++ b/test/compress/issue-640.js
@@ -0,0 +1,317 @@
+cond_5: {
+    options = {
+        conditionals: true,
+        expression: true,
+    }
+    input: {
+        if (some_condition()) {
+            if (some_other_condition()) {
+                do_something();
+            } else {
+                alternate();
+            }
+        } else {
+            alternate();
+        }
+
+        if (some_condition()) {
+            if (some_other_condition()) {
+                do_something();
+            }
+        }
+    }
+    expect: {
+        some_condition() && some_other_condition() ? do_something() : alternate();
+        if (some_condition() && some_other_condition()) do_something();
+    }
+}
+
+dead_code_const_annotation_regex: {
+    options = {
+        booleans     : true,
+        conditionals : true,
+        dead_code    : true,
+        evaluate     : true,
+        expression   : true,
+        loops        : true,
+    }
+    input: {
+        var unused;
+        // @constraint this shouldn't be a constant
+        var CONST_FOO_ANN = false;
+        if (CONST_FOO_ANN) {
+            console.log("reachable");
+        }
+    }
+    expect: {
+        var unused;
+        var CONST_FOO_ANN = !1;
+        if (CONST_FOO_ANN) console.log('reachable');
+    }
+}
+
+drop_console_2: {
+    options = {
+        drop_console: true,
+        expression: true,
+    }
+    input: {
+        console.log('foo');
+        console.log.apply(console, arguments);
+    }
+    expect: {
+        // with regular compression these will be stripped out as well
+        void 0;
+        void 0;
+    }
+}
+
+drop_value: {
+    options = {
+        expression: true,
+        side_effects: true,
+    }
+    input: {
+        (1, [2, foo()], 3, {a:1, b:bar()});
+    }
+    expect: {
+        foo(), {a:1, b:bar()};
+    }
+}
+
+wrongly_optimized: {
+    options = {
+        conditionals: true,
+        booleans: true,
+        evaluate: true,
+        expression: true,
+    }
+    input: {
+        function func() {
+            foo();
+        }
+        if (func() || true) {
+            bar();
+        }
+    }
+    expect: {
+        function func() {
+            foo();
+        }
+        // TODO: optimize to `func(), bar()`
+        if (func(), !0) bar();
+    }
+}
+
+negate_iife_1: {
+    options = {
+        expression: true,
+        negate_iife: true,
+    }
+    input: {
+        (function(){ stuff() })();
+    }
+    expect: {
+        (function(){ stuff() })();
+    }
+}
+
+negate_iife_3: {
+    options = {
+        conditionals: true,
+        expression: true,
+        negate_iife: true,
+    }
+    input: {
+        (function(){ return t })() ? console.log(true) : console.log(false);
+    }
+    expect: {
+        (function(){ return t })() ? console.log(true) : console.log(false);
+    }
+}
+
+negate_iife_3_off: {
+    options = {
+        conditionals: true,
+        expression: true,
+        negate_iife: false,
+    }
+    input: {
+        (function(){ return t })() ? console.log(true) : console.log(false);
+    }
+    expect: {
+        (function(){ return t })() ? console.log(true) : console.log(false);
+    }
+}
+
+negate_iife_4: {
+    options = {
+        conditionals: true,
+        expression: true,
+        negate_iife: true,
+        sequences: true,
+    }
+    input: {
+        (function(){ return t })() ? console.log(true) : console.log(false);
+        (function(){
+            console.log("something");
+        })();
+    }
+    expect: {
+        (function(){ return t })() ? console.log(true) : console.log(false), function(){
+            console.log("something");
+        }();
+    }
+}
+
+negate_iife_5: {
+    options = {
+        conditionals: true,
+        expression: true,
+        negate_iife: true,
+        sequences: true,
+    }
+    input: {
+        if ((function(){ return t })()) {
+            foo(true);
+        } else {
+            bar(false);
+        }
+        (function(){
+            console.log("something");
+        })();
+    }
+    expect: {
+        (function(){ return t })() ? foo(true) : bar(false), function(){
+            console.log("something");
+        }();
+    }
+}
+
+negate_iife_5_off: {
+    options = {
+        conditionals: true,
+        expression: true,
+        negate_iife: false,
+        sequences: true,
+    };
+    input: {
+        if ((function(){ return t })()) {
+            foo(true);
+        } else {
+            bar(false);
+        }
+        (function(){
+            console.log("something");
+        })();
+    }
+    expect: {
+        (function(){ return t })() ? foo(true) : bar(false), function(){
+            console.log("something");
+        }();
+    }
+}
+
+issue_1254_negate_iife_true: {
+    options = {
+        expression: true,
+        negate_iife: true,
+    }
+    input: {
+        (function() {
+            return function() {
+                console.log('test')
+            };
+        })()();
+    }
+    expect_exact: '(function(){return function(){console.log("test")}})()();'
+}
+
+issue_1254_negate_iife_nested: {
+    options = {
+        expression: true,
+        negate_iife: true,
+    }
+    input: {
+        (function() {
+            return function() {
+                console.log('test')
+            };
+        })()()()()();
+    }
+    expect_exact: '(function(){return function(){console.log("test")}})()()()()();'
+}
+
+conditional: {
+    options = {
+        expression: true,
+        pure_funcs: [ "pure" ],
+        side_effects: true,
+    }
+    input: {
+        pure(1 | a() ? 2 & b() : 7 ^ c());
+        pure(1 | a() ? 2 & b() : 5);
+        pure(1 | a() ? 4 : 7 ^ c());
+        pure(1 | a() ? 4 : 5);
+        pure(3 ? 2 & b() : 7 ^ c());
+        pure(3 ? 2 & b() : 5);
+        pure(3 ? 4 : 7 ^ c());
+        pure(3 ? 4 : 5);
+    }
+    expect: {
+        1 | a() ? b() : c();
+        1 | a() && b();
+        1 | a() || c();
+        a();
+        3 ? b() : c();
+        3 && b();
+        3 || c();
+        pure(3 ? 4 : 5);
+    }
+}
+
+limit_1: {
+    options = {
+        expression: true,
+        sequences: 3,
+    }
+    input: {
+        a;
+        b;
+        c;
+        d;
+        e;
+        f;
+        g;
+        h;
+        i;
+        j;
+        k;
+    }
+    expect: {
+        // Turned into a single return statement
+        // so it can no longer be split into lines
+        a,b,c,d,e,f,g,h,i,j,k;
+    }
+}
+
+iife: {
+    options = {
+        expression: true,
+        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() {}(), typeof function g() {}();
+    }
+}
diff --git a/test/compress/negate-iife.js b/test/compress/negate-iife.js
index f17ae20..9a0b5a4 100644
--- a/test/compress/negate-iife.js
+++ b/test/compress/negate-iife.js
@@ -32,6 +32,19 @@ negate_iife_2: {
     }
 }
 
+negate_iife_2_side_effects: {
+    options = {
+        negate_iife: true,
+        side_effects: true,
+    }
+    input: {
+        (function(){ return {} })().x = 10; // should not transform this one
+    }
+    expect: {
+        (function(){ return {} })().x = 10;
+    }
+}
+
 negate_iife_3: {
     options = {
         negate_iife: true,
@@ -45,6 +58,34 @@ negate_iife_3: {
     }
 }
 
+negate_iife_3_evaluate: {
+    options = {
+        conditionals: true,
+        evaluate: true,
+        negate_iife: true,
+    }
+    input: {
+        (function(){ return true })() ? console.log(true) : console.log(false);
+    }
+    expect: {
+        console.log(true);
+    }
+}
+
+negate_iife_3_side_effects: {
+    options = {
+        conditionals: true,
+        negate_iife: true,
+        side_effects: true,
+    }
+    input: {
+        (function(){ return t })() ? console.log(true) : console.log(false);
+    }
+    expect: {
+        !function(){ return t }() ? console.log(false) : console.log(true);
+    }
+}
+
 negate_iife_3_off: {
     options = {
         negate_iife: false,
@@ -58,6 +99,20 @@ negate_iife_3_off: {
     }
 }
 
+negate_iife_3_off_evaluate: {
+    options = {
+        conditionals: true,
+        evaluate: true,
+        negate_iife: false,
+    }
+    input: {
+        (function(){ return true })() ? console.log(true) : console.log(false);
+    }
+    expect: {
+        console.log(true);
+    }
+}
+
 negate_iife_4: {
     options = {
         negate_iife: true,
@@ -320,3 +375,35 @@ issue_1288: {
         }(0);
     }
 }
+
+issue_1288_side_effects: {
+    options = {
+        conditionals: true,
+        negate_iife: true,
+        side_effects: true,
+    }
+    input: {
+        if (w) ;
+        else {
+            (function f() {})();
+        }
+        if (!x) {
+            (function() {
+                x = {};
+            })();
+        }
+        if (y)
+            (function() {})();
+        else
+            (function(z) {
+                return z;
+            })(0);
+    }
+    expect: {
+        w;
+        x || function() {
+            x = {};
+        }();
+        y;
+    }
+}
diff --git a/test/compress/sequences.js b/test/compress/sequences.js
index d93f523..7bb274c 100644
--- a/test/compress/sequences.js
+++ b/test/compress/sequences.js
@@ -248,6 +248,6 @@ iife: {
     }
     expect: {
         x = 42, function a() {}(), function b() {}(), function c() {}(),
-        function d() {}(), function e() {}(), function f() {}(), function g() {}()
+        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