[Pkg-javascript-commits] [uglifyjs] 385/491: inline functions with `AST_Var` (#2688)

Jonas Smedegaard dr at jones.dk
Wed Feb 14 19:51:55 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 7d3cddf9d624d169c6667a52e8d6f313d1b30159
Author: Alex Lam S.L <alexlamsl at gmail.com>
Date:   Wed Jan 3 01:54:44 2018 +0800

    inline functions with `AST_Var` (#2688)
---
 README.md                  |   8 +-
 lib/compress.js            | 174 ++++++++++++++++++++++++----------
 test/compress/functions.js | 231 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 360 insertions(+), 53 deletions(-)

diff --git a/README.md b/README.md
index 43793dc..d7c47d7 100644
--- a/README.md
+++ b/README.md
@@ -640,7 +640,13 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
 
 - `if_return` (default: `true`) -- optimizations for if/return and if/continue
 
-- `inline` (default: `true`) -- embed simple functions
+- `inline` (default: `true`) -- inline calls to function with simple/`return` statement:
+  - `false` -- same as `0`
+  - `0` -- disabled inlining
+  - `1` -- inline simple functions
+  - `2` -- inline functions with arguments
+  - `3` -- inline functions with arguments and variables
+  - `true` -- same as `3`
 
 - `join_vars` (default: `true`) -- join consecutive `var` statements
 
diff --git a/lib/compress.js b/lib/compress.js
index 3221268..06873df 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -99,6 +99,7 @@ function Compressor(options, false_by_default) {
             });
         }
     }
+    if (this.options["inline"] === true) this.options["inline"] = 3;
     var pure_funcs = this.options["pure_funcs"];
     if (typeof pure_funcs == "function") {
         this.pure_funcs = pure_funcs;
@@ -4030,7 +4031,7 @@ merge(Compressor.prototype, {
             if (compressor.option("inline")
                 && !fn.uses_arguments
                 && !fn.uses_eval
-                && fn.body.length == 1
+                && (value = can_flatten_body(stat))
                 && (exp === fn ? !fn.name
                     : compressor.option("unused")
                         && (def = exp.definition()).references.length == 1
@@ -4038,11 +4039,8 @@ merge(Compressor.prototype, {
                         && fn.is_constant_expression(exp.scope))
                 && !self.pure
                 && !fn.contains_this()
-                && can_flatten_args(fn)
-                && (value = flatten_body(stat))) {
-                var expressions = flatten_args(fn);
-                expressions.push(value.clone(true));
-                return make_sequence(self, expressions).optimize(compressor);
+                && can_inject_symbols()) {
+                return make_sequence(self, flatten_fn()).optimize(compressor);
             }
             if (compressor.option("side_effects") && all(fn.body, is_empty)) {
                 var args = self.args.concat(make_node(AST_Undefined, self));
@@ -4072,19 +4070,44 @@ merge(Compressor.prototype, {
         }
         return self;
 
-        function can_flatten_args(fn) {
-            var catches = Object.create(null);
-            do {
-                scope = compressor.parent(++level);
-                if (scope instanceof AST_Catch) {
-                    catches[scope.argname.name] = true;
-                } else if (scope instanceof AST_IterationStatement) {
-                    in_loop = [];
-                } else if (scope instanceof AST_SymbolRef) {
-                    if (scope.fixed_value() instanceof AST_Scope) return false;
+        function return_value(stat) {
+            if (!stat) return make_node(AST_Undefined, self);
+            if (stat instanceof AST_Return) {
+                if (!stat.value) return make_node(AST_Undefined, self);
+                return stat.value.clone(true);
+            }
+            if (stat instanceof AST_SimpleStatement) {
+                return make_node(AST_UnaryPrefix, stat, {
+                    operator: "void",
+                    expression: stat.body.clone(true)
+                });
+            }
+        }
+
+        function can_flatten_body(stat) {
+            var len = fn.body.length;
+            if (compressor.option("inline") < 3) {
+                return len == 1 && return_value(stat);
+            }
+            stat = null;
+            for (var i = 0; i < len; i++) {
+                var line = fn.body[i];
+                if (line instanceof AST_Definitions) {
+                    if (stat && !all(line.definitions, function(var_def) {
+                        return !var_def.value;
+                    })) {
+                        return false;
+                    }
+                } else if (stat) {
+                    return false;
+                } else {
+                    stat = line;
                 }
-            } while (!(scope instanceof AST_Scope));
-            var safe_to_inject = compressor.toplevel.vars || !(scope instanceof AST_Toplevel);
+            }
+            return return_value(stat);
+        }
+
+        function can_inject_args(catches, defs, safe_to_inject) {
             for (var i = 0, len = fn.argnames.length; i < len; i++) {
                 var arg = fn.argnames[i];
                 if (arg.__unused) continue;
@@ -4096,43 +4119,101 @@ merge(Compressor.prototype, {
                 }
                 if (in_loop) in_loop.push(arg.definition());
             }
-            return !in_loop || in_loop.length == 0 || !is_reachable(stat, in_loop);
+            return true;
         }
 
-        function flatten_args(fn) {
-            var decls = [];
-            var expressions = [];
+        function can_inject_vars(catches, safe_to_inject) {
+            var len = fn.body.length;
+            for (var i = 0; i < len; i++) {
+                var stat = fn.body[i];
+                if (!(stat instanceof AST_Definitions)) continue;
+                if (!safe_to_inject) return false;
+                for (var j = stat.definitions.length; --j >= 0;) {
+                    var name = stat.definitions[j].name;
+                    if (catches[name.name]
+                        || identifier_atom(name.name)
+                        || scope.var_names()[name.name]) {
+                        return false;
+                    }
+                }
+            }
+            return true;
+        }
+
+        function can_inject_symbols() {
+            var catches = Object.create(null);
+            do {
+                scope = compressor.parent(++level);
+                if (scope instanceof AST_Catch) {
+                    catches[scope.argname.name] = true;
+                } else if (scope instanceof AST_IterationStatement) {
+                    in_loop = [];
+                } else if (scope instanceof AST_SymbolRef) {
+                    if (scope.fixed_value() instanceof AST_Scope) return false;
+                }
+            } while (!(scope instanceof AST_Scope));
+            var safe_to_inject = !(scope instanceof AST_Toplevel) || compressor.toplevel.vars;
+            var inline = compressor.option("inline");
+            if (!can_inject_vars(catches, !in_loop && inline >= 3 && safe_to_inject)) return false;
+            if (!can_inject_args(catches, in_loop, inline >= 2 && safe_to_inject)) return false;
+            return !in_loop || in_loop.length == 0 || !is_reachable(fn, in_loop);
+        }
+
+        function append_var(decls, expressions, name, value) {
+            var def = name.definition();
+            scope.var_names()[name.name] = true;
+            scope.variables.set(name.name, def);
+            scope.enclosed.push(def);
+            decls.push(make_node(AST_VarDef, name, {
+                name: name,
+                value: null
+            }));
+            var sym = make_node(AST_SymbolRef, name, name);
+            def.references.push(sym);
+            if (value) expressions.push(make_node(AST_Assign, self, {
+                operator: "=",
+                left: sym,
+                right: value
+            }));
+        }
+
+        function flatten_args(decls, expressions) {
             for (var len = fn.argnames.length, i = len; --i >= 0;) {
                 var name = fn.argnames[i];
                 var value = self.args[i];
                 if (name.__unused || scope.var_names()[name.name]) {
-                    if (value) {
-                        expressions.unshift(value);
-                    }
+                    if (value) expressions.push(value);
                 } else {
-                    var def = name.definition();
-                    scope.var_names()[name.name] = true;
-                    scope.variables.set(name.name, def);
-                    scope.enclosed.push(def);
                     var symbol = make_node(AST_SymbolVar, name, name);
-                    def.orig.push(symbol);
-                    decls.unshift(make_node(AST_VarDef, name, {
-                        name: symbol,
-                        value: null
-                    }));
-                    var sym = make_node(AST_SymbolRef, name, name);
-                    def.references.push(sym);
+                    name.definition().orig.push(symbol);
                     if (!value && in_loop) value = make_node(AST_Undefined, self);
-                    if (value) expressions.unshift(make_node(AST_Assign, self, {
-                        operator: "=",
-                        left: sym,
-                        right: value
-                    }));
+                    append_var(decls, expressions, symbol, value);
                 }
             }
+            decls.reverse();
+            expressions.reverse();
             for (i = len, len = self.args.length; i < len; i++) {
                 expressions.push(self.args[i]);
             }
+        }
+
+        function flatten_body(decls, expressions) {
+            for (i = 0, len = fn.body.length; i < len; i++) {
+                var stat = fn.body[i];
+                if (stat instanceof AST_Definitions) {
+                    stat.definitions.forEach(function(var_def) {
+                        append_var(decls, expressions, var_def.name, var_def.value);
+                    });
+                }
+            }
+            expressions.push(value);
+        }
+
+        function flatten_fn() {
+            var decls = [];
+            var expressions = [];
+            flatten_args(decls, expressions);
+            flatten_body(decls, expressions);
             if (decls.length) {
                 i = scope.body.indexOf(compressor.parent(level - 1)) + 1;
                 scope.body.splice(i, 0, make_node(AST_Var, fn, {
@@ -4141,17 +4222,6 @@ merge(Compressor.prototype, {
             }
             return expressions;
         }
-
-        function flatten_body(stat) {
-            if (stat instanceof AST_Return) {
-                return stat.value;
-            } else if (stat instanceof AST_SimpleStatement) {
-                return make_node(AST_UnaryPrefix, stat, {
-                    operator: "void",
-                    expression: stat.body
-                });
-            }
-        }
     });
 
     OPT(AST_New, function(self, compressor){
diff --git a/test/compress/functions.js b/test/compress/functions.js
index dd98ba6..f38977b 100644
--- a/test/compress/functions.js
+++ b/test/compress/functions.js
@@ -1694,3 +1694,234 @@ loop_init_arg: {
     }
     expect_stdout: "PASS"
 }
+
+inline_false: {
+    options = {
+        inline: false,
+        side_effects: true,
+        toplevel: true,
+    }
+    input: {
+        (function() {
+            console.log(1);
+        })();
+        (function(a) {
+            console.log(a);
+        })(2);
+        (function(b) {
+            var c = b;
+            console.log(c);
+        })(3);
+    }
+    expect: {
+        (function() {
+            console.log(1);
+        })();
+        (function(a) {
+            console.log(a);
+        })(2);
+        (function(b) {
+            var c = b;
+            console.log(c);
+        })(3);
+    }
+    expect_stdout: [
+        "1",
+        "2",
+        "3",
+    ]
+}
+
+inline_0: {
+    options = {
+        inline: 0,
+        side_effects: true,
+        toplevel: true,
+    }
+    input: {
+        (function() {
+            console.log(1);
+        })();
+        (function(a) {
+            console.log(a);
+        })(2);
+        (function(b) {
+            var c = b;
+            console.log(c);
+        })(3);
+    }
+    expect: {
+        (function() {
+            console.log(1);
+        })();
+        (function(a) {
+            console.log(a);
+        })(2);
+        (function(b) {
+            var c = b;
+            console.log(c);
+        })(3);
+    }
+    expect_stdout: [
+        "1",
+        "2",
+        "3",
+    ]
+}
+
+inline_1: {
+    options = {
+        inline: 1,
+        side_effects: true,
+        toplevel: true,
+    }
+    input: {
+        (function() {
+            console.log(1);
+        })();
+        (function(a) {
+            console.log(a);
+        })(2);
+        (function(b) {
+            var c = b;
+            console.log(c);
+        })(3);
+    }
+    expect: {
+        console.log(1);
+        (function(a) {
+            console.log(a);
+        })(2);
+        (function(b) {
+            var c = b;
+            console.log(c);
+        })(3);
+    }
+    expect_stdout: [
+        "1",
+        "2",
+        "3",
+    ]
+}
+
+inline_2: {
+    options = {
+        inline: 2,
+        side_effects: true,
+        toplevel: true,
+    }
+    input: {
+        (function() {
+            console.log(1);
+        })();
+        (function(a) {
+            console.log(a);
+        })(2);
+        (function(b) {
+            var c = b;
+            console.log(c);
+        })(3);
+    }
+    expect: {
+        console.log(1);
+        a = 2, console.log(a);
+        var a;
+        (function(b) {
+            var c = b;
+            console.log(c);
+        })(3);
+    }
+    expect_stdout: [
+        "1",
+        "2",
+        "3",
+    ]
+}
+
+inline_3: {
+    options = {
+        inline: 3,
+        side_effects: true,
+        toplevel: true,
+    }
+    input: {
+        (function() {
+            console.log(1);
+        })();
+        (function(a) {
+            console.log(a);
+        })(2);
+        (function(b) {
+            var c = b;
+            console.log(c);
+        })(3);
+    }
+    expect: {
+        console.log(1);
+        a = 2, console.log(a);
+        var a;
+        b = 3, c = b, console.log(c);
+        var b, c;
+    }
+    expect_stdout: [
+        "1",
+        "2",
+        "3",
+    ]
+}
+
+inline_true: {
+    options = {
+        inline: true,
+        side_effects: true,
+        toplevel: true,
+    }
+    input: {
+        (function() {
+            console.log(1);
+        })();
+        (function(a) {
+            console.log(a);
+        })(2);
+        (function(b) {
+            var c = b;
+            console.log(c);
+        })(3);
+    }
+    expect: {
+        console.log(1);
+        a = 2, console.log(a);
+        var a;
+        b = 3, c = b, console.log(c);
+        var b, c;
+    }
+    expect_stdout: [
+        "1",
+        "2",
+        "3",
+    ]
+}
+
+use_before_init_in_loop: {
+    options = {
+        inline: true,
+        toplevel: true,
+    }
+    input: {
+        var a = "PASS";
+        for (var b = 2; --b >= 0;) (function() {
+            var c = function() {
+                return 1;
+            }(c && (a = "FAIL"));
+        })();
+        console.log(a);
+    }
+    expect: {
+        var a = "PASS";
+        for (var b = 2; --b >= 0;) (function() {
+            var c = (c && (a = "FAIL"), 1);
+        })();
+        console.log(a);
+    }
+    expect_stdout: "PASS"
+}

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