enhance `inline` (#3832)
authorAlex Lam S.L <alexlamsl@gmail.com>
Thu, 30 Apr 2020 20:33:46 +0000 (21:33 +0100)
committerGitHub <noreply@github.com>
Thu, 30 Apr 2020 20:33:46 +0000 (04:33 +0800)
lib/compress.js
test/compress/functions.js
test/compress/reduce_vars.js

index c679a8b..15db233 100644 (file)
@@ -225,7 +225,7 @@ merge(Compressor.prototype, {
         // output and performance.
         descend(node, this);
         var opt = node.optimize(this);
-        if (is_scope) {
+        if (is_scope && opt === node) {
             opt.drop_unused(this);
             descend(opt, this);
         }
@@ -3981,6 +3981,38 @@ merge(Compressor.prototype, {
         return self;
     });
 
+    OPT(AST_Function, function(self, compressor) {
+        self.body = tighten_body(self.body, compressor);
+        if (compressor.option("inline")) for (var i = 0; i < self.body.length; i++) {
+            var stat = self.body[i];
+            if (stat instanceof AST_Directive) continue;
+            if (stat instanceof AST_Return) {
+                var call = stat.value;
+                if (!call || call.TYPE != "Call") break;
+                var fn = call.expression;
+                if (fn instanceof AST_SymbolRef) {
+                    fn = fn.fixed_value();
+                }
+                if (!(fn instanceof AST_Lambda)) break;
+                if (fn.uses_arguments) break;
+                if (fn.contains_this()) break;
+                var j = fn.argnames.length;
+                if (j > 0 && compressor.option("inline") < 2) break;
+                if (j > self.argnames.length) break;
+                if (j < self.argnames.length && !compressor.drop_fargs(fn, call)) break;
+                while (--j >= 0) {
+                    var arg = call.args[j];
+                    if (!(arg instanceof AST_SymbolRef)) break;
+                    if (arg.definition() !== self.argnames[j].definition()) break;
+                }
+                if (j >= 0) break;
+                return call.expression;
+            }
+            break;
+        }
+        return self;
+    });
+
     AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
         if (!compressor.option("unused")) return;
         if (compressor.has_directive("use asm")) return;
@@ -6133,7 +6165,7 @@ merge(Compressor.prototype, {
 
         function can_substitute_directly() {
             if (var_assigned) return;
-            if (compressor.option("inline") <= 1 && fn.argnames.length) return;
+            if (compressor.option("inline") < 2 && fn.argnames.length) return;
             if (!fn.variables.all(function(def) {
                 return def.references.length < 2 && def.orig[0] instanceof AST_SymbolFunarg;
             })) return;
index b088ca8..262bb6a 100644 (file)
@@ -2877,7 +2877,7 @@ issue_2437: {
     }
 }
 
-issue_2485: {
+issue_2485_1: {
     options = {
         functions: true,
         reduce_funcs: true,
@@ -2926,6 +2926,54 @@ issue_2485: {
     expect_stdout: "6"
 }
 
+issue_2485_2: {
+    options = {
+        functions: true,
+        inline: true,
+        reduce_funcs: true,
+        reduce_vars: true,
+        unused: true,
+    }
+    input: {
+        var foo = function(bar) {
+            var n = function(a, b) {
+                return a + b;
+            };
+            var sumAll = function(arg) {
+                return arg.reduce(n, 0);
+            };
+            var runSumAll = function(arg) {
+                return sumAll(arg);
+            };
+            bar.baz = function(arg) {
+                var n = runSumAll(arg);
+                return (n.get = 1), n;
+            };
+            return bar;
+        };
+        var bar = foo({});
+        console.log(bar.baz([1, 2, 3]));
+    }
+    expect: {
+        var foo = function(bar) {
+            function n(a, b) {
+                return a + b;
+            }
+            function runSumAll(arg) {
+                return arg.reduce(n, 0);
+            }
+            bar.baz = function(arg) {
+                var n = runSumAll(arg);
+                return (n.get = 1), n;
+            };
+            return bar;
+        };
+        var bar = foo({});
+        console.log(bar.baz([1, 2, 3]));
+    }
+    expect_stdout: "6"
+}
+
 issue_3364: {
     options = {
         functions: true,
@@ -3113,9 +3161,7 @@ issue_3400_1: {
                 return console.log(o[g]), o;
             }
             function e() {
-                return [ 42 ].map(function(v) {
-                    return h(v);
-                });
+                return [ 42 ].map(h);
             }
             return e();
         }()[0].p);
@@ -3130,7 +3176,7 @@ issue_3400_2: {
     options = {
         collapse_vars: true,
         inline: true,
-        passes: 2,
+        passes: 3,
         reduce_funcs: true,
         reduce_vars: true,
         unused: true,
@@ -3158,11 +3204,11 @@ issue_3400_2: {
     }
     expect: {
         void console.log(function g() {
-            return [ 42 ].map(function(v) {
-                return o = {
-                    p: v
-                }, console.log(o[g]), o;
-                var o;
+            return [ 42 ].map(function(u) {
+                var o = {
+                    p: u
+                };
+                return console.log(o[g]), o;
             });
         }()[0].p);
     }
@@ -3568,8 +3614,8 @@ pr_3592_2: {
         var g = [ "PASS" ];
         console.log((z = "PASS", function(problem) {
             return g[problem];
-        }((y = z, problem(y)))));
-        var z, y;
+        }(problem(z))));
+        var z;
     }
     expect_stdout: "PASS"
 }
@@ -3635,9 +3681,7 @@ pr_3595_1: {
         }
         console.log((arg = "PASS", function(problem) {
             return g[problem];
-        }(function(arg) {
-            return problem(arg);
-        }(arg))));
+        }(problem(arg))));
         var arg;
     }
     expect_stdout: "PASS"
@@ -3678,9 +3722,7 @@ pr_3595_2: {
         }
         console.log(function(problem) {
             return g[problem];
-        }(function(arg) {
-            return problem(arg);
-        }("PASS")));
+        }(problem("PASS")));
     }
     expect_stdout: "PASS"
 }
@@ -4108,3 +4150,443 @@ issue_3821_2: {
     }
     expect_stdout: "PASS"
 }
+
+substitude: {
+    options = {
+        inline: true,
+        reduce_vars: true,
+        toplevel: true,
+    }
+    input: {
+        var o = {};
+        function f(a) {
+            return a === o ? "PASS" : "FAIL";
+        }
+        [
+            function() {
+                return f;
+            },
+            function() {
+                return function(b) {
+                    return f(b);
+                };
+            },
+            function() {
+                "use strict";
+                return function(c) {
+                    return f(c);
+                };
+            },
+            function() {
+                return function(c) {
+                    "use strict";
+                    return f(c);
+                };
+            },
+            function() {
+                return function(d, e) {
+                    return f(d, e);
+                };
+            },
+        ].forEach(function(g) {
+            console.log(g()(o));
+            console.log(g().call(o, o));
+        });
+    }
+    expect: {
+        var o = {};
+        function f(a) {
+            return a === o ? "PASS" : "FAIL";
+        }
+        [
+            function() {
+                return f;
+            },
+            function() {
+                return f;
+            },
+            function() {
+                "use strict";
+                return f;
+            },
+            function() {
+                return f;
+            },
+            function() {
+                return function(d, e) {
+                    return f(d, e);
+                };
+            },
+        ].forEach(function(g) {
+            console.log(g()(o));
+            console.log(g().call(o, o));
+        });
+    }
+    expect_stdout: [
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+    ]
+}
+
+substitude_arguments: {
+    options = {
+        inline: true,
+        reduce_vars: true,
+        toplevel: true,
+    }
+    input: {
+        var o = {};
+        function f() {
+            return arguments[0] === o ? "PASS" : "FAIL";
+        }
+        [
+            function() {
+                return f;
+            },
+            function() {
+                return function(b) {
+                    return f(b);
+                };
+            },
+            function() {
+                "use strict";
+                return function(c) {
+                    return f(c);
+                };
+            },
+            function() {
+                return function(c) {
+                    "use strict";
+                    return f(c);
+                };
+            },
+            function() {
+                return function(d, e) {
+                    return f(d, e);
+                };
+            },
+        ].forEach(function(g) {
+            console.log(g()(o));
+            console.log(g().call(o, o));
+        });
+    }
+    expect: {
+        var o = {};
+        function f() {
+            return arguments[0] === o ? "PASS" : "FAIL";
+        }
+        [
+            function() {
+                return f;
+            },
+            function() {
+                return function(b) {
+                    return f(b);
+                };
+            },
+            function() {
+                "use strict";
+                return function(c) {
+                    return f(c);
+                };
+            },
+            function() {
+                return function(c) {
+                    "use strict";
+                    return f(c);
+                };
+            },
+            function() {
+                return function(d, e) {
+                    return f(d, e);
+                };
+            },
+        ].forEach(function(g) {
+            console.log(g()(o));
+            console.log(g().call(o, o));
+        });
+    }
+    expect_stdout: [
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+    ]
+}
+
+substitude_drop_fargs: {
+    options = {
+        inline: true,
+        keep_fargs: false,
+        reduce_vars: true,
+        toplevel: true,
+    }
+    input: {
+        var o = {};
+        function f(a) {
+            return a === o ? "PASS" : "FAIL";
+        }
+        [
+            function() {
+                return f;
+            },
+            function() {
+                return function(b) {
+                    return f(b);
+                };
+            },
+            function() {
+                "use strict";
+                return function(c) {
+                    return f(c);
+                };
+            },
+            function() {
+                return function(c) {
+                    "use strict";
+                    return f(c);
+                };
+            },
+            function() {
+                return function(d, e) {
+                    return f(d, e);
+                };
+            },
+        ].forEach(function(g) {
+            console.log(g()(o));
+            console.log(g().call(o, o));
+        });
+    }
+    expect: {
+        var o = {};
+        function f(a) {
+            return a === o ? "PASS" : "FAIL";
+        }
+        [
+            function() {
+                return f;
+            },
+            function() {
+                return f;
+            },
+            function() {
+                "use strict";
+                return f;
+            },
+            function() {
+                return f;
+            },
+            function() {
+                return f;
+            },
+        ].forEach(function(g) {
+            console.log(g()(o));
+            console.log(g().call(o, o));
+        });
+    }
+    expect_stdout: [
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+    ]
+}
+
+substitude_this: {
+    options = {
+        inline: true,
+        reduce_vars: true,
+        toplevel: true,
+    }
+    input: {
+        var o = {};
+        function f(a) {
+            return a === o ? this === o : "FAIL";
+        }
+        [
+            function() {
+                return f;
+            },
+            function() {
+                return function(b) {
+                    return f(b);
+                };
+            },
+            function() {
+                "use strict";
+                return function(c) {
+                    return f(c);
+                };
+            },
+            function() {
+                return function(c) {
+                    "use strict";
+                    return f(c);
+                };
+            },
+            function() {
+                return function(d, e) {
+                    return f(d, e);
+                };
+            },
+        ].forEach(function(g) {
+            console.log(g()(o));
+            console.log(g().call(o, o));
+        });
+    }
+    expect: {
+        var o = {};
+        function f(a) {
+            return a === o ? this === o : "FAIL";
+        }
+        [
+            function() {
+                return f;
+            },
+            function() {
+                return function(b) {
+                    return f(b);
+                };
+            },
+            function() {
+                "use strict";
+                return function(c) {
+                    return f(c);
+                };
+            },
+            function() {
+                return function(c) {
+                    "use strict";
+                    return f(c);
+                };
+            },
+            function() {
+                return function(d, e) {
+                    return f(d, e);
+                };
+            },
+        ].forEach(function(g) {
+            console.log(g()(o));
+            console.log(g().call(o, o));
+        });
+    }
+    expect_stdout: [
+        "false",
+        "true",
+        "false",
+        "false",
+        "false",
+        "false",
+        "false",
+        "false",
+        "false",
+        "false",
+    ]
+}
+
+substitude_use_strict: {
+    options = {
+        inline: true,
+        reduce_vars: true,
+        toplevel: true,
+    }
+    input: {
+        var o = {};
+        function f(a) {
+            "use strict";
+            return a === o ? "PASS" : "FAIL";
+        }
+        [
+            function() {
+                return f;
+            },
+            function() {
+                return function(b) {
+                    return f(b);
+                };
+            },
+            function() {
+                "use strict";
+                return function(c) {
+                    return f(c);
+                };
+            },
+            function() {
+                return function(c) {
+                    "use strict";
+                    return f(c);
+                };
+            },
+            function() {
+                return function(d, e) {
+                    return f(d, e);
+                };
+            },
+        ].forEach(function(g) {
+            console.log(g()(o));
+            console.log(g().call(o, o));
+        });
+    }
+    expect: {
+        var o = {};
+        function f(a) {
+            "use strict";
+            return a === o ? "PASS" : "FAIL";
+        }
+        [
+            function() {
+                return f;
+            },
+            function() {
+                return f;
+            },
+            function() {
+                "use strict";
+                return f;
+            },
+            function() {
+                return f;
+            },
+            function() {
+                return function(d, e) {
+                    return f(d, e);
+                };
+            },
+        ].forEach(function(g) {
+            console.log(g()(o));
+            console.log(g().call(o, o));
+        });
+    }
+    expect_stdout: [
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+        "PASS",
+    ]
+}
index 25108ca..8cc301c 100644 (file)
@@ -4589,7 +4589,7 @@ perf_8: {
     expect_stdout: "348150"
 }
 
-issue_2485: {
+issue_2485_1: {
     options = {
         reduce_funcs: true,
         reduce_vars: true,
@@ -4637,6 +4637,53 @@ issue_2485: {
     expect_stdout: "6"
 }
 
+issue_2485_2: {
+    options = {
+        inline: true,
+        reduce_funcs: true,
+        reduce_vars: true,
+        unused: true,
+    }
+    input: {
+        var foo = function(bar) {
+            var n = function(a, b) {
+                return a + b;
+            };
+            var sumAll = function(arg) {
+                return arg.reduce(n, 0);
+            };
+            var runSumAll = function(arg) {
+                return sumAll(arg);
+            };
+            bar.baz = function(arg) {
+                var n = runSumAll(arg);
+                return (n.get = 1), n;
+            };
+            return bar;
+        };
+        var bar = foo({});
+        console.log(bar.baz([1, 2, 3]));
+    }
+    expect: {
+        var foo = function(bar) {
+            var n = function(a, b) {
+                return a + b;
+            };
+            var runSumAll = function(arg) {
+                    return arg.reduce(n, 0);
+            };
+            bar.baz = function(arg) {
+                var n = runSumAll(arg);
+                return (n.get = 1), n;
+            };
+            return bar;
+        };
+        var bar = foo({});
+        console.log(bar.baz([1, 2, 3]));
+    }
+    expect_stdout: "6"
+}
+
 issue_2455: {
     options = {
         reduce_vars: true,