From: Alex Lam S.L Date: Fri, 15 Dec 2017 05:28:30 +0000 (+0800) Subject: handle `inline` of function arguments (#2590) X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=8f681b1d1721e931852be48720d26ba052eac96c;p=UglifyJS.git handle `inline` of function arguments (#2590) fixes #2476 --- diff --git a/lib/compress.js b/lib/compress.js index 9b0a5350..aaadcd10 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -2997,10 +2997,10 @@ merge(Compressor.prototype, { return self; }); - AST_Scope.DEFMETHOD("make_var_name", function(prefix) { - var var_names = this.var_names; + AST_Scope.DEFMETHOD("var_names", function() { + var var_names = this._var_names; if (!var_names) { - this.var_names = var_names = Object.create(null); + this._var_names = var_names = Object.create(null); this.enclosed.forEach(function(def) { var_names[def.name] = true; }); @@ -3008,6 +3008,11 @@ merge(Compressor.prototype, { var_names[name] = true; }); } + return var_names; + }); + + AST_Scope.DEFMETHOD("make_var_name", function(prefix) { + var var_names = this.var_names(); prefix = prefix.replace(/[^a-z_$]+/ig, "_"); var name = prefix; for (var i = 0; var_names[name]; i++) name = prefix + "$" + i; @@ -3862,7 +3867,7 @@ merge(Compressor.prototype, { } } if (fn instanceof AST_Function) { - var def; + var def, scope, value; if (compressor.option("inline") && !fn.uses_arguments && !fn.uses_eval @@ -3871,24 +3876,13 @@ merge(Compressor.prototype, { : compressor.option("unused") && (def = exp.definition()).references.length == 1 && !recursive_ref(compressor, def)) + && !self.has_pure_annotation(compressor) && !fn.contains_this() - && all(fn.argnames, function(arg) { - return arg.__unused; - }) - && !self.has_pure_annotation(compressor)) { - var value; - if (stat instanceof AST_Return) { - value = stat.value; - } else if (stat instanceof AST_SimpleStatement) { - value = make_node(AST_UnaryPrefix, stat, { - operator: "void", - expression: stat.body - }); - } - if (value) { - var args = self.args.concat(value); - return make_sequence(self, args).optimize(compressor); - } + && (scope = can_flatten_args(fn)) + && (value = flatten_body(stat))) { + var expressions = flatten_args(fn, scope); + expressions.push(value); + return make_sequence(self, expressions).optimize(compressor); } if (compressor.option("side_effects") && all(fn.body, is_empty)) { var args = self.args.concat(make_node(AST_Undefined, self)); @@ -3917,6 +3911,62 @@ merge(Compressor.prototype, { return best_of(compressor, ev, self); } return self; + + function can_flatten_args(fn) { + var scope = compressor.find_parent(AST_Scope); + var safe_to_inject = compressor.toplevel.vars || !(scope instanceof AST_Toplevel); + return all(fn.argnames, function(arg) { + return arg.__unused || safe_to_inject && !scope.var_names()[arg.name]; + }) && scope; + } + + function flatten_args(fn, scope) { + var decls = []; + var expressions = []; + for (var len = fn.argnames.length, i = len; --i >= 0;) { + var name = fn.argnames[i]; + var value = self.args[i]; + if (name.__unused) { + if (value || expressions.length) { + expressions.unshift(value || make_node(AST_Undefined, self)); + } + } else { + decls.unshift(make_node(AST_VarDef, name, { + name: name, + value: null + })); + var sym = make_node(AST_SymbolRef, name, name); + name.definition().references.push(sym); + expressions.unshift(make_node(AST_Assign, self, { + operator: "=", + left: sym, + right: value || make_node(AST_Undefined, self) + })); + } + } + for (i = len, len = self.args.length; i < len; i++) { + expressions.push(self.args[i]); + } + if (decls.length) { + for (i = 0; compressor.parent(i) !== scope;) i++; + i = scope.body.indexOf(compressor.parent(i - 1)) + 1; + scope.body.splice(i, 0, make_node(AST_Var, fn, { + definitions: decls + })); + } + 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/collapse_vars.js b/test/compress/collapse_vars.js index 39d6b641..1a487981 100644 --- a/test/compress/collapse_vars.js +++ b/test/compress/collapse_vars.js @@ -3317,15 +3317,14 @@ issue_2436_4: { }(o)); } expect: { - console.log(function(c) { - return { - x: c.a, - y: c.b, - }; - }({ + console.log({ + x: (c = { a: 1, b: 2, - })); + }).a, + y: c.b, + }); + var c; } expect_stdout: true } @@ -3448,12 +3447,11 @@ issue_2436_8: { }(o)); } expect: { - console.log(function(c) { - return { - x: c.a, - y: c.b, - }; - }(o)); + console.log({ + x: (c = o).a, + y: c.b, + }); + var c; } expect_stdout: true } @@ -3478,12 +3476,11 @@ issue_2436_9: { } expect: { var o = console; - console.log(function(c) { - return { - x: c.a, - y: c.b, - }; - }(o)); + console.log({ + x: (c = o).a, + y: c.b, + }); + var c; } expect_stdout: true } @@ -3523,13 +3520,12 @@ issue_2436_10: { o = { b: 3 }; return n; } - console.log(function(c) { - return [ - c.a, - f(c.b), - c.b, - ]; - }(o).join(" ")); + console.log((c = o, [ + c.a, + f(c.b), + c.b, + ]).join(" ")); + var c; } expect_stdout: "1 2 2" } diff --git a/test/compress/functions.js b/test/compress/functions.js index c4281d5c..a36509af 100644 --- a/test/compress/functions.js +++ b/test/compress/functions.js @@ -578,11 +578,10 @@ issue_2531_1: { } expect: { function outer() { - return function(value) { - return function() { - return value; - }; - }("Hello"); + return value = "Hello", function() { + return value; + }; + var value; } console.log("Greeting:", outer()()); } @@ -593,9 +592,10 @@ issue_2531_2: { options = { evaluate: true, inline: true, - passes: 2, + passes: 3, reduce_funcs: true, reduce_vars: true, + side_effects: true, unused: true, } input: { @@ -627,9 +627,10 @@ issue_2531_3: { options = { evaluate: true, inline: true, - passes: 2, + passes: 3, reduce_funcs: true, reduce_vars: true, + side_effects: true, toplevel: true, unused: true, } @@ -747,3 +748,27 @@ inline_loop_4: { }; } } + +issue_2476: { + options = { + inline: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function foo(x, y, z) { + return x < y ? x * y + z : x * z - y; + } + for (var sum = 0, i = 0; i < 10; i++) + sum += foo(i, i + 1, 3 * i); + console.log(sum); + } + expect: { + for (var sum = 0, i = 0; i < 10; i++) + sum += (x = i, y = i + 1, z = 3 * i, x < y ? x * y + z : x * z - y); + var x, y, z; + console.log(sum); + } + expect_stdout: "465" +} diff --git a/test/compress/hoist_props.js b/test/compress/hoist_props.js index a46033d5..012a3fca 100644 --- a/test/compress/hoist_props.js +++ b/test/compress/hoist_props.js @@ -55,9 +55,8 @@ issue_2377_2: { console.log(obj.foo, obj.cube(3)); } expect: { - console.log(1, function(x) { - return x * x * x; - }(3)); + console.log(1, (x = 3, x * x * x)); + var x; } expect_stdout: "1 27" } @@ -67,9 +66,10 @@ issue_2377_3: { evaluate: true, inline: true, hoist_props: true, - passes: 3, + passes: 4, reduce_funcs: true, reduce_vars: true, + side_effects: true, toplevel: true, unused: true, } diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index ff93079d..9ed9c974 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -1396,7 +1396,7 @@ defun_inline_3: { options = { evaluate: true, inline: true, - passes: 2, + passes: 3, reduce_funcs: true, reduce_vars: true, side_effects: true, @@ -2250,12 +2250,11 @@ redefine_farg_2: { console.log(f([]), g([]), h([])); } expect: { - console.log(function(a) { - return typeof a; - }([]), "number",function(a, b) { + console.log((a = [], typeof a), "number",function(a, b) { a = b; return typeof a; }([])); + var a; } expect_stdout: "object number undefined" } @@ -2266,7 +2265,7 @@ redefine_farg_3: { evaluate: true, inline: true, keep_fargs: false, - passes: 2, + passes: 3, reduce_funcs: true, reduce_vars: true, sequences: true, @@ -3107,6 +3106,7 @@ obj_var_2: { obj_arg_1: { options = { + collapse_vars: true, evaluate: true, inline: true, passes: 2, @@ -3138,9 +3138,10 @@ obj_arg_1: { obj_arg_2: { options = { + collapse_vars: true, evaluate: true, inline: true, - passes: 2, + passes: 3, properties: true, reduce_funcs: true, reduce_vars: true, diff --git a/test/mocha/glob.js b/test/mocha/glob.js index eb5d477d..b6f1e049 100644 --- a/test/mocha/glob.js +++ b/test/mocha/glob.js @@ -26,7 +26,7 @@ describe("bin/uglifyjs with input file globs", function() { }); }); it("bin/uglifyjs with multiple input file globs.", function(done) { - var command = uglifyjscmd + ' "test/input/issue-1242/???.es5" "test/input/issue-1242/*.js" -mc toplevel,passes=2'; + var command = uglifyjscmd + ' "test/input/issue-1242/???.es5" "test/input/issue-1242/*.js" -mc toplevel,passes=3'; exec(command, function(err, stdout) { if (err) throw err;