[Pkg-javascript-commits] [uglifyjs] 125/491: implement function inlining (#2053)

Jonas Smedegaard dr at jones.dk
Wed Feb 14 19:51:28 UTC 2018


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

js pushed a commit to annotated tag debian/3.3.10-1
in repository uglifyjs.

commit 3493a182b2c94e4011b2b12d61376273b985d29a
Author: Alex Lam S.L <alexlamsl at gmail.com>
Date:   Tue Jun 6 05:49:53 2017 +0800

    implement function inlining (#2053)
    
    - empty body
    - single `AST_Return`
    - single `AST_SimpleStatement`
    - avoid `/*#__PURE__*/`
    
    Miscellaneous
    - enhance single-use function substitution
    
    fixes #281
---
 README.md                      |   2 +
 lib/compress.js                |  88 ++++++---
 test/compress/collapse_vars.js |   2 +-
 test/compress/evaluate.js      |   2 +
 test/compress/functions.js     |   4 +
 test/compress/issue-1787.js    |   1 +
 test/compress/issue-281.js     | 433 +++++++++++++++++++++++++++++++++++++++++
 test/compress/negate-iife.js   |   6 +-
 test/compress/reduce_vars.js   |  15 +-
 test/mocha/glob.js             |   2 +-
 test/mocha/minify.js           |   2 +-
 11 files changed, 528 insertions(+), 29 deletions(-)

diff --git a/README.md b/README.md
index 95d5446..039bc63 100644
--- a/README.md
+++ b/README.md
@@ -616,6 +616,8 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
 
 - `if_return` -- optimizations for if/return and if/continue
 
+- `inline` -- embed simple functions
+
 - `join_vars` -- join consecutive `var` statements
 
 - `cascade` -- small optimization for sequences, transform `x, x` into `x`
diff --git a/lib/compress.js b/lib/compress.js
index effa6b1..969acd6 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -63,6 +63,7 @@ function Compressor(options, false_by_default) {
         hoist_vars    : false,
         ie8           : false,
         if_return     : !false_by_default,
+        inline        : !false_by_default,
         join_vars     : !false_by_default,
         keep_fargs    : true,
         keep_fnames   : false,
@@ -2943,24 +2944,9 @@ merge(Compressor.prototype, {
 
     OPT(AST_Call, function(self, compressor){
         var exp = self.expression;
-        if (compressor.option("reduce_vars")
-            && exp instanceof AST_SymbolRef) {
-            var def = exp.definition();
+        if (compressor.option("reduce_vars") && exp instanceof AST_SymbolRef) {
             var fixed = exp.fixed_value();
-            if (fixed instanceof AST_Defun) {
-                def.fixed = fixed = make_node(AST_Function, fixed, fixed).clone(true);
-            }
-            if (fixed instanceof AST_Function) {
-                exp = fixed;
-                if (compressor.option("unused")
-                    && def.references.length == 1
-                    && !(def.scope.uses_arguments
-                        && def.orig[0] instanceof AST_SymbolFunarg)
-                    && !def.scope.uses_eval
-                    && compressor.find_parent(AST_Scope) === exp.parent_scope) {
-                    self.expression = exp;
-                }
-            }
+            if (fixed instanceof AST_Function) exp = fixed;
         }
         if (compressor.option("unused")
             && exp instanceof AST_Function
@@ -3171,13 +3157,61 @@ merge(Compressor.prototype, {
             }
         }
         if (exp instanceof AST_Function) {
-            if (exp.body[0] instanceof AST_Return) {
-                var value = exp.body[0].value;
+            var stat = exp.body[0];
+            if (compressor.option("inline") && stat instanceof AST_Return) {
+                var value = stat && stat.value;
                 if (!value || value.is_constant_expression()) {
                     var args = self.args.concat(value || make_node(AST_Undefined, self));
                     return make_sequence(self, args).transform(compressor);
                 }
             }
+            if (compressor.option("inline")
+                && !exp.name
+                && exp.body.length == 1
+                && !exp.uses_arguments
+                && !exp.uses_eval
+                && !self.has_pure_annotation(compressor)) {
+                var body;
+                if (stat instanceof AST_Return) {
+                    body = stat.value.clone(true);
+                } else if (stat instanceof AST_SimpleStatement) {
+                    body = [];
+                    merge_sequence(body, stat.body.clone(true));
+                    merge_sequence(body, make_node(AST_Undefined, self));
+                    body = make_sequence(self, body);
+                }
+                if (body) {
+                    var fn = exp.clone();
+                    fn.argnames = [];
+                    fn.body = [
+                        make_node(AST_Var, self, {
+                            definitions: exp.argnames.map(function(sym, i) {
+                                return make_node(AST_VarDef, sym, {
+                                    name: sym,
+                                    value: self.args[i] || make_node(AST_Undefined, self)
+                                });
+                            })
+                        })
+                    ];
+                    if (self.args.length > exp.argnames.length) {
+                        fn.body.push(make_node(AST_SimpleStatement, self, {
+                            body: make_sequence(self, self.args.slice(exp.argnames.length))
+                        }));
+                    }
+                    fn.body.push(make_node(AST_Return, self, {
+                        value: body
+                    }));
+                    body = fn.transform(compressor).body;
+                    if (body.length == 0) return make_node(AST_Undefined, self);
+                    if (body.length == 1 && body[0] instanceof AST_Return) {
+                        if (!body[0].value) return make_node(AST_Undefined, self);
+                        body = best_of(compressor, body[0].value, self);
+                    } else {
+                        body = self;
+                    }
+                    if (body !== self) return body;
+                }
+            }
             if (compressor.option("side_effects") && all(exp.body, is_empty)) {
                 var args = self.args.concat(make_node(AST_Undefined, self));
                 return make_sequence(self, args).transform(compressor);
@@ -3836,12 +3870,22 @@ merge(Compressor.prototype, {
                 return make_node(AST_Infinity, self).optimize(compressor);
             }
         }
-        if (compressor.option("evaluate")
-            && compressor.option("reduce_vars")
+        if (compressor.option("reduce_vars")
             && is_lhs(self, compressor.parent()) !== self) {
             var d = self.definition();
             var fixed = self.fixed_value();
-            if (fixed) {
+            if (fixed instanceof AST_Defun) {
+                d.fixed = fixed = make_node(AST_Function, fixed, fixed).clone(true);
+            }
+            if (compressor.option("unused")
+                && fixed instanceof AST_Function
+                && d.references.length == 1
+                && !(d.scope.uses_arguments && d.orig[0] instanceof AST_SymbolFunarg)
+                && !d.scope.uses_eval
+                && compressor.find_parent(AST_Scope) === fixed.parent_scope) {
+                return fixed;
+            }
+            if (compressor.option("evaluate") && fixed) {
                 if (d.should_replace === undefined) {
                     var init = fixed.evaluate(compressor);
                     if (init !== fixed && (compressor.option("unsafe_regexp") || !(init instanceof RegExp))) {
diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js
index 90d7ac9..158d1b4 100644
--- a/test/compress/collapse_vars.js
+++ b/test/compress/collapse_vars.js
@@ -1146,7 +1146,7 @@ collapse_vars_constants: {
         function f3(x) {
             var b = x.prop;
             sideeffect1();
-            return b + -9;
+            return b + (function() { return -9; })();
         }
     }
 }
diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js
index 99245d0..27d08d4 100644
--- a/test/compress/evaluate.js
+++ b/test/compress/evaluate.js
@@ -644,6 +644,7 @@ unsafe_prototype_function: {
 call_args: {
     options = {
         evaluate: true,
+        inline: true,
         reduce_vars: true,
         toplevel: true,
     }
@@ -665,6 +666,7 @@ call_args: {
 call_args_drop_param: {
     options = {
         evaluate: true,
+        inline: true,
         keep_fargs: false,
         reduce_vars: true,
         toplevel: true,
diff --git a/test/compress/functions.js b/test/compress/functions.js
index a9ca23f..180bb11 100644
--- a/test/compress/functions.js
+++ b/test/compress/functions.js
@@ -21,6 +21,7 @@ iifes_returning_constants_keep_fargs_true: {
         join_vars     : true,
         reduce_vars   : true,
         cascade       : true,
+        inline        : true,
     }
     input: {
         (function(){ return -1.23; }());
@@ -56,6 +57,7 @@ iifes_returning_constants_keep_fargs_false: {
         join_vars     : true,
         reduce_vars   : true,
         cascade       : true,
+        inline        : true,
     }
     input: {
         (function(){ return -1.23; }());
@@ -82,6 +84,7 @@ issue_485_crashing_1530: {
         conditionals: true,
         dead_code: true,
         evaluate: true,
+        inline: true,
     }
     input: {
         (function(a) {
@@ -154,6 +157,7 @@ function_returning_constant_literal: {
         evaluate: true,
         cascade: true,
         unused: true,
+        inline: true,
     }
     input: {
         function greeter() {
diff --git a/test/compress/issue-1787.js b/test/compress/issue-1787.js
index 02fa0f9..2b5372b 100644
--- a/test/compress/issue-1787.js
+++ b/test/compress/issue-1787.js
@@ -1,6 +1,7 @@
 unary_prefix: {
     options = {
         evaluate: true,
+        inline: true,
         reduce_vars: true,
         unused: true,
     }
diff --git a/test/compress/issue-281.js b/test/compress/issue-281.js
new file mode 100644
index 0000000..6b1b4b4
--- /dev/null
+++ b/test/compress/issue-281.js
@@ -0,0 +1,433 @@
+collapse_vars_constants: {
+    options = {
+        collapse_vars: true,
+        evaluate: true,
+        inline: true,
+        reduce_vars: true,
+        unused: true,
+    }
+    input: {
+        function f1(x) {
+            var a = 4, b = x.prop, c = 5, d = sideeffect1(), e = sideeffect2();
+            return b + (function() { return d - a * e - c; })();
+        }
+        function f2(x) {
+            var a = 4, b = x.prop, c = 5, not_used = sideeffect1(), e = sideeffect2();
+            return b + (function() { return -a * e - c; })();
+        }
+    }
+    expect: {
+        function f1(x) {
+            var b = x.prop, d = sideeffect1(), e = sideeffect2();
+            return b + (d - 4 * e - 5);
+        }
+        function f2(x) {
+            var b = x.prop;
+            sideeffect1();
+            return b + (-4 * sideeffect2() - 5);
+        }
+    }
+}
+
+modified: {
+    options = {
+        collapse_vars: true,
+        inline: true,
+        unused: true,
+    }
+    input: {
+        function f5(b) {
+            var a = function() {
+                return b;
+            }();
+            return b++ + a;
+        }
+        console.log(f5(1));
+    }
+    expect: {
+        function f5(b) {
+            var a = b;
+            return b++ + a;
+        }
+        console.log(f5(1));
+    }
+    expect_stdout: "2"
+}
+
+ref_scope: {
+    options = {
+        collapse_vars: true,
+        inline: true,
+        unused: true,
+    }
+    input: {
+        console.log(function() {
+            var a = 1, b = 2, c = 3;
+            var a = c++, b = b /= a;
+            return function() {
+                return a;
+            }() + b;
+        }());
+    }
+    expect: {
+        console.log(function() {
+            var a = 1, b = 2, c = 3;
+            b = b /= a = c++;
+            return a + b;
+        }());
+    }
+    expect_stdout: true
+}
+
+safe_undefined: {
+    options = {
+        conditionals: true,
+        if_return: true,
+        inline: true,
+        unsafe: false,
+        unused: true,
+    }
+    mangle = {}
+    input: {
+        var a, c;
+        console.log(function(undefined) {
+            return function() {
+                if (a)
+                    return b;
+                if (c)
+                    return d;
+            };
+        }(1)());
+    }
+    expect: {
+        var a, c;
+        console.log(a ? b : c ? d : void 0);
+    }
+    expect_stdout: true
+}
+
+negate_iife_3: {
+    options = {
+        conditionals: true,
+        expression: true,
+        inline: true,
+        negate_iife: true,
+    }
+    input: {
+        (function(){ return t })() ? console.log(true) : console.log(false);
+    }
+    expect: {
+        t ? console.log(true) : console.log(false);
+    }
+}
+
+negate_iife_3_off: {
+    options = {
+        conditionals: true,
+        expression: true,
+        inline: true,
+        negate_iife: false,
+    }
+    input: {
+        (function(){ return t })() ? console.log(true) : console.log(false);
+    }
+    expect: {
+        t ? console.log(true) : console.log(false);
+    }
+}
+
+negate_iife_4: {
+    options = {
+        conditionals: true,
+        expression: true,
+        inline: true,
+        negate_iife: true,
+        sequences: true,
+    }
+    input: {
+        (function(){ return t })() ? console.log(true) : console.log(false);
+        (function(){
+            console.log("something");
+        })();
+    }
+    expect: {
+        t ? console.log(true) : console.log(false), console.log("something"), void 0;
+    }
+}
+
+negate_iife_5: {
+    options = {
+        conditionals: true,
+        expression: true,
+        inline: true,
+        negate_iife: true,
+        sequences: true,
+    }
+    input: {
+        if ((function(){ return t })()) {
+            foo(true);
+        } else {
+            bar(false);
+        }
+        (function(){
+            console.log("something");
+        })();
+    }
+    expect: {
+        t ? foo(true) : bar(false), console.log("something"), void 0;
+    }
+}
+
+negate_iife_5_off: {
+    options = {
+        conditionals: true,
+        expression: true,
+        inline: true,
+        negate_iife: false,
+        sequences: true,
+    };
+    input: {
+        if ((function(){ return t })()) {
+            foo(true);
+        } else {
+            bar(false);
+        }
+        (function(){
+            console.log("something");
+        })();
+    }
+    expect: {
+        t ? foo(true) : bar(false), console.log("something"), void 0;
+    }
+}
+
+issue_1254_negate_iife_true: {
+    options = {
+        expression: true,
+        inline: true,
+        negate_iife: true,
+    }
+    input: {
+        (function() {
+            return function() {
+                console.log('test')
+            };
+        })()();
+    }
+    expect_exact: 'console.log("test"),void 0;'
+    expect_stdout: true
+}
+
+issue_1254_negate_iife_nested: {
+    options = {
+        expression: true,
+        inline: true,
+        negate_iife: true,
+    }
+    input: {
+        (function() {
+            return function() {
+                console.log('test')
+            };
+        })()()()()();
+    }
+    expect_exact: '(console.log("test"),void 0)()()();'
+}
+
+negate_iife_issue_1073: {
+    options = {
+        conditionals: true,
+        evaluate: true,
+        inline: true,
+        negate_iife: true,
+        reduce_vars: true,
+        sequences: true,
+        unused: true,
+    };
+    input: {
+        new (function(a) {
+            return function Foo() {
+                this.x = a;
+                console.log(this);
+            };
+        }(7))();
+    }
+    expect: {
+        new function() {
+            this.x = 7,
+            console.log(this);
+        }();
+    }
+    expect_stdout: true
+}
+
+issue_1288_side_effects: {
+    options = {
+        conditionals: true,
+        evaluate: true,
+        inline: true,
+        negate_iife: true,
+        reduce_vars: true,
+        side_effects: true,
+        unused: true,
+    };
+    input: {
+        if (w) ;
+        else {
+            (function f() {})();
+        }
+        if (!x) {
+            (function() {
+                x = {};
+            })();
+        }
+        if (y)
+            (function() {})();
+        else
+            (function(z) {
+                return z;
+            })(0);
+    }
+    expect: {
+        w;
+        x || (x = {});
+        y;
+    }
+}
+
+inner_var_for_in_1: {
+    options = {
+        evaluate: true,
+        inline: true,
+        reduce_vars: true,
+    }
+    input: {
+        function f() {
+            var a = 1, b = 2;
+            for (b in (function() {
+                return x(a, b, c);
+            })()) {
+                var c = 3, d = 4;
+                x(a, b, c, d);
+            }
+            x(a, b, c, d);
+        }
+    }
+    expect: {
+        function f() {
+            var a = 1, b = 2;
+            for (b in x(1, b, c)) {
+                var c = 3, d = 4;
+                x(1, b, c, d);
+            }
+            x(1, b, c, d);
+        }
+    }
+}
+
+issue_1595_3: {
+    options = {
+        evaluate: true,
+        inline: true,
+        passes: 2,
+        reduce_vars: true,
+        unused: true,
+    }
+    input: {
+        (function f(a) {
+            return g(a + 1);
+        })(2);
+    }
+    expect: {
+        g(3);
+    }
+}
+
+issue_1758: {
+    options = {
+        inline: true,
+        sequences: true,
+        side_effects: true,
+    }
+    input: {
+        console.log(function(c) {
+            var undefined = 42;
+            return function() {
+                c--;
+                c--, c.toString();
+                return;
+            }();
+        }());
+    }
+    expect: {
+        console.log(function(c) {
+            var undefined = 42;
+            return c--, c--, void c.toString();
+        }());
+    }
+    expect_stdout: "undefined"
+}
+wrap_iife: {
+    options = {
+        inline: true,
+        negate_iife: false,
+    }
+    beautify = {
+        wrap_iife: true,
+    }
+    input: {
+        (function() {
+            return function() {
+                console.log('test')
+            };
+        })()();
+    }
+    expect_exact: 'console.log("test"),void 0;'
+}
+
+wrap_iife_in_expression: {
+    options = {
+        inline: true,
+        negate_iife: false,
+    }
+    beautify = {
+        wrap_iife: true,
+    }
+    input: {
+        foo = (function () {
+            return bar();
+        })();
+    }
+    expect_exact: 'foo=bar();'
+}
+
+wrap_iife_in_return_call: {
+    options = {
+        inline: true,
+        negate_iife: false,
+    }
+    beautify = {
+        wrap_iife: true,
+    }
+    input: {
+        (function() {
+            return (function() {
+                console.log('test')
+            })();
+        })()();
+    }
+    expect_exact: '(console.log("test"),void 0)();'
+}
+
+pure_annotation: {
+    options = {
+        inline: true,
+        side_effects: true,
+    }
+    input: {
+        /*@__PURE__*/(function() {
+            console.log("hello");
+        }());
+    }
+    expect_exact: ""
+}
diff --git a/test/compress/negate-iife.js b/test/compress/negate-iife.js
index 514a15c..66d2427 100644
--- a/test/compress/negate-iife.js
+++ b/test/compress/negate-iife.js
@@ -22,7 +22,8 @@ negate_iife_1_off: {
 
 negate_iife_2: {
     options = {
-        negate_iife: true
+        inline: true,
+        negate_iife: true,
     };
     input: {
         (function(){ return {} })().x = 10;
@@ -32,6 +33,7 @@ negate_iife_2: {
 
 negate_iife_2_side_effects: {
     options = {
+        inline: true,
         negate_iife: true,
         side_effects: true,
     }
@@ -58,6 +60,7 @@ negate_iife_3_evaluate: {
     options = {
         conditionals: true,
         evaluate: true,
+        inline: true,
         negate_iife: true,
     }
     input: {
@@ -100,6 +103,7 @@ negate_iife_3_off_evaluate: {
     options = {
         conditionals: true,
         evaluate: true,
+        inline: true,
         negate_iife: false,
     }
     input: {
diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js
index 078de82..cef2983 100644
--- a/test/compress/reduce_vars.js
+++ b/test/compress/reduce_vars.js
@@ -2,6 +2,7 @@ reduce_vars: {
     options = {
         conditionals  : true,
         evaluate      : true,
+        inline        : true,
         global_defs   : {
             C : 0
         },
@@ -1032,6 +1033,7 @@ defun_inline_2: {
 defun_inline_3: {
     options = {
         evaluate: true,
+        inline: true,
         passes: 2,
         reduce_vars: true,
         side_effects: true,
@@ -1054,6 +1056,7 @@ defun_inline_3: {
 
 defun_call: {
     options = {
+        inline: true,
         reduce_vars: true,
         unused: true,
     }
@@ -1080,6 +1083,7 @@ defun_call: {
 
 defun_redefine: {
     options = {
+        inline: true,
         reduce_vars: true,
         unused: true,
     }
@@ -1112,6 +1116,7 @@ defun_redefine: {
 
 func_inline: {
     options = {
+        inline: true,
         reduce_vars: true,
         unused: true,
     }
@@ -1138,6 +1143,7 @@ func_inline: {
 
 func_modified: {
     options = {
+        inline: true,
         reduce_vars: true,
         unused: true,
     }
@@ -1340,10 +1346,9 @@ iife_func_side_effects: {
             console.log("z");
         }
         (function(a, b, c) {
-            function y() {
+            return function() {
                 console.log("FAIL");
-            }
-            return y + b();
+            } + b();
         })(x(), function() {
             return y();
         }, z());
@@ -1716,6 +1721,7 @@ redefine_arguments_1: {
 redefine_arguments_2: {
     options = {
         evaluate: true,
+        inline: true,
         keep_fargs: false,
         reduce_vars: true,
         side_effects: true,
@@ -1752,6 +1758,7 @@ redefine_arguments_2: {
 redefine_arguments_3: {
     options = {
         evaluate: true,
+        inline: true,
         keep_fargs: false,
         passes: 3,
         reduce_vars: true,
@@ -1828,6 +1835,7 @@ redefine_farg_1: {
 redefine_farg_2: {
     options = {
         evaluate: true,
+        inline: true,
         keep_fargs: false,
         reduce_vars: true,
         side_effects: true,
@@ -1864,6 +1872,7 @@ redefine_farg_2: {
 redefine_farg_3: {
     options = {
         evaluate: true,
+        inline: true,
         keep_fargs: false,
         passes: 3,
         reduce_vars: true,
diff --git a/test/mocha/glob.js b/test/mocha/glob.js
index 56d3f82..8567ecb 100644
--- a/test/mocha/glob.js
+++ b/test/mocha/glob.js
@@ -31,7 +31,7 @@ describe("bin/uglifyjs with input file globs", function() {
         exec(command, function(err, stdout) {
             if (err) throw err;
 
-            assert.strictEqual(stdout, 'var print=console.log.bind(console),a=function(n){return 3*n}(3),b=function(n){return n/2}(12);print("qux",a,b),function(n){print("Foo:",2*n)}(11);\n');
+            assert.strictEqual(stdout, 'var print=console.log.bind(console);print("qux",9,6),print("Foo:",2*11);\n');
             done();
         });
     });
diff --git a/test/mocha/minify.js b/test/mocha/minify.js
index 77b798e..519b725 100644
--- a/test/mocha/minify.js
+++ b/test/mocha/minify.js
@@ -106,7 +106,7 @@ describe("minify", function() {
                         content: "inline"
                     }
                 });
-                assert.strictEqual(result.code, "var bar=function(){function foo(bar){return bar}return foo}();");
+                assert.strictEqual(result.code, "var bar=function(){return function(bar){return bar}}();");
                 assert.strictEqual(warnings.length, 1);
                 assert.strictEqual(warnings[0], "inline source map not found");
             } finally {

-- 
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