enhance `inline` (#3760)
authorAlex Lam S.L <alexlamsl@gmail.com>
Sun, 5 Apr 2020 02:42:23 +0000 (03:42 +0100)
committerGitHub <noreply@github.com>
Sun, 5 Apr 2020 02:42:23 +0000 (10:42 +0800)
lib/compress.js
test/compress/functions.js
test/input/rename/input.js
test/mocha/cli.js
test/ufuzz/index.js

index 72690fe..f03e0ee 100644 (file)
@@ -5813,12 +5813,24 @@ merge(Compressor.prototype, {
         var is_func = fn instanceof AST_Lambda;
         var stat = is_func && fn.first_statement();
         var can_inline = compressor.option("inline") && !self.is_expr_pure(compressor);
-        if (exp === fn && can_inline && stat instanceof AST_Return) {
+        if (can_inline && stat instanceof AST_Return) {
             var value = stat.value;
-            if (!value || value.is_constant_expression()) {
+            if (exp === fn && (!value || value.is_constant_expression())) {
                 var args = self.args.concat(value || make_node(AST_Undefined, self));
                 return make_sequence(self, args).optimize(compressor);
             }
+            var funarg, pos;
+            if (value instanceof AST_SymbolRef
+                && (funarg = resolve_funarg(value.definition().orig))
+                && (pos = fn.argnames.indexOf(funarg)) >= 0
+                && (pos >= self.args.length - 1 || all(self.args.slice(pos), function(funarg) {
+                    return !funarg.has_side_effects(compressor);
+                }))) {
+                var args = self.args.slice();
+                args.push(args.splice(pos, 1)[0] || make_node(AST_Undefined, self));
+                var node = make_sequence(self, args).optimize(compressor);
+                return maintain_this_binding(compressor, compressor.parent(), compressor.self(), node);
+            }
         }
         if (is_func) {
             var def, value, scope, in_loop, level = -1;
@@ -5837,7 +5849,8 @@ merge(Compressor.prototype, {
                 && can_inject_symbols()) {
                 fn._squeezed = true;
                 if (exp !== fn) fn.parent_scope = exp.scope;
-                return make_sequence(self, flatten_fn()).optimize(compressor);
+                var node = make_sequence(self, flatten_fn()).optimize(compressor);
+                return maintain_this_binding(compressor, compressor.parent(), compressor.self(), node);
             }
             if (compressor.option("side_effects")
                 && all(fn.body, is_empty)
@@ -5864,6 +5877,14 @@ merge(Compressor.prototype, {
         }
         return try_evaluate(compressor, self);
 
+        function resolve_funarg(orig) {
+            var funarg;
+            for (var i = 0; orig[i] instanceof AST_SymbolFunarg; i++) {
+                funarg = orig[i];
+            }
+            return funarg;
+        }
+
         function return_value(stat) {
             if (!stat) return make_node(AST_Undefined, self);
             if (stat instanceof AST_Return) {
index 0cf0179..b1a44ff 100644 (file)
@@ -342,11 +342,7 @@ inner_ref: {
         }(2));
     }
     expect: {
-        console.log(function(a) {
-            return a;
-        }(1), function(a) {
-            return a;
-        }());
+        console.log(1, void 0);
     }
     expect_stdout: "1 undefined"
 }
@@ -1577,7 +1573,23 @@ issue_2663_3: {
     ]
 }
 
-duplicate_argnames: {
+duplicate_argnames_1: {
+    options = {
+        inline: true,
+        side_effects: true,
+    }
+    input: {
+        console.log(function(a, a, a) {
+            return a;
+        }("FAIL", 42, "PASS"));
+    }
+    expect: {
+        console.log("PASS");
+    }
+    expect_stdout: "PASS"
+}
+
+duplicate_argnames_2: {
     options = {
         inline: true,
         reduce_vars: true,
@@ -1857,10 +1869,9 @@ use_before_init_in_loop: {
     expect_stdout: "PASS"
 }
 
-duplicate_arg_var: {
+duplicate_arg_var_1: {
     options = {
         inline: true,
-        toplevel: true,
     }
     input: {
         console.log(function(b) {
@@ -1869,7 +1880,24 @@ duplicate_arg_var: {
         }("PASS"));
     }
     expect: {
-        console.log((b = "PASS", b));
+        console.log("PASS");
+    }
+    expect_stdout: "PASS"
+}
+
+duplicate_arg_var_2: {
+    options = {
+        inline: true,
+        toplevel: true,
+    }
+    input: {
+        console.log(function(b) {
+            return b + "SS";
+            var b;
+        }("PA"));
+    }
+    expect: {
+        console.log((b = "PA", b + "SS"));
         var b;
     }
     expect_stdout: "PASS"
@@ -3785,3 +3813,87 @@ issue_3679_3: {
     }
     expect_stdout: "PASS"
 }
+
+preceding_side_effects: {
+    options = {
+        inline: true,
+    }
+    input: {
+        console.log(function(a, b, c) {
+            return b;
+        }(console, "PASS", 42));
+    }
+    expect: {
+        console.log((console, 42, "PASS"));
+    }
+    expect_stdout: "PASS"
+}
+
+trailing_side_effects: {
+    options = {
+        inline: true,
+    }
+    input: {
+        console.log(function(a, b, c) {
+            return b;
+        }(42, "PASS", console));
+    }
+    expect: {
+        console.log(function(a, b, c) {
+            return b;
+        }(42, "PASS", console));
+    }
+    expect_stdout: "PASS"
+}
+
+preserve_binding_1: {
+    options = {
+        inline: true,
+    }
+    input: {
+        var o = {
+            f: function() {
+                return this === o ? "FAIL" : "PASS";
+            },
+        };
+        console.log(function(a) {
+            return a;
+        }(o.f)());
+    }
+    expect: {
+        var o = {
+            f: function() {
+                return this === o ? "FAIL" : "PASS";
+            },
+        };
+        console.log((0, o.f)());
+    }
+    expect_stdout: "PASS"
+}
+
+preserve_binding_2: {
+    options = {
+        collapse_vars: true,
+        inline: true,
+        unused: true,
+    }
+    input: {
+        var o = {
+            f: function() {
+                return this === o ? "FAIL" : "PASS";
+            },
+        };
+        console.log(function(a) {
+            return a;
+        }(o.f)());
+    }
+    expect: {
+        var o = {
+            f: function() {
+                return this === o ? "FAIL" : "PASS";
+            },
+        };
+        console.log((0, o.f)());
+    }
+    expect_stdout: "PASS"
+}
index ef6daed..3008433 100644 (file)
@@ -1,6 +1,6 @@
 function f(x) {
     return g(x);
     function g(x) {
-        return x;
+        return x + 1;
     }
 }
index 0347797..2e68fbf 100644 (file)
@@ -674,7 +674,7 @@ describe("bin/uglifyjs", function() {
         var command = uglifyjscmd + " test/input/rename/input.js --rename";
         exec(command, function(err, stdout, stderr) {
             if (err) throw err;
-            assert.strictEqual(stdout, "function f(a){return b(a);function b(c){return c}}\n");
+            assert.strictEqual(stdout, "function f(a){return b(a);function b(c){return c+1}}\n");
             done();
         });
     });
@@ -682,7 +682,7 @@ describe("bin/uglifyjs", function() {
         var command = uglifyjscmd + " test/input/rename/input.js -mc passes=2 --no-rename";
         exec(command, function(err, stdout, stderr) {
             if (err) throw err;
-            assert.strictEqual(stdout, "function f(n){return function(n){return n}(n)}\n");
+            assert.strictEqual(stdout, "function f(n){return function(n){return n+1}(n)}\n");
             done();
         });
     });
@@ -690,7 +690,7 @@ describe("bin/uglifyjs", function() {
         var command = uglifyjscmd + " test/input/rename/input.js -mc passes=2";
         exec(command, function(err, stdout, stderr) {
             if (err) throw err;
-            assert.strictEqual(stdout, "function f(n){return n}\n");
+            assert.strictEqual(stdout, "function f(n){return n+1}\n");
             done();
         });
     });
@@ -698,7 +698,7 @@ describe("bin/uglifyjs", function() {
         var command = uglifyjscmd + " test/input/rename/input.js -c passes=2";
         exec(command, function(err, stdout, stderr) {
             if (err) throw err;
-            assert.strictEqual(stdout, "function f(x){return function(x){return x}(x)}\n");
+            assert.strictEqual(stdout, "function f(x){return function(x){return x+1}(x)}\n");
             done();
         });
     });
index f1fce6a..76a782e 100644 (file)
@@ -704,6 +704,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
             break;
         }
         VAR_NAMES.length = nameLenBefore;
+        if (canThrow && rng(8) == 0 && !/^new /.test(s[0])) s[s.length - 1] += "()";
         return filterDirective(s).join("\n");
       case p++:
       case p++: