From: Alex Lam S.L Date: Thu, 9 Mar 2017 11:11:05 +0000 (+0800) Subject: fix & improve function argument compression (#1584) X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=b633706ce42576b7e2aa85a96c5691bde87e71ac;p=UglifyJS.git fix & improve function argument compression (#1584) - one-use function call => IIFE should take `eval()` & `arguments` into account - if unused parameter cannot be eliminated, replace it with `0` fixes #1583 --- diff --git a/lib/compress.js b/lib/compress.js index f6b76ec2..3964636f 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -281,6 +281,9 @@ merge(Compressor.prototype, { if (node instanceof AST_Function && (iife = tw.parent()) instanceof AST_Call && iife.expression === node) { + // Virtually turn IIFE parameters into variable definitions: + // (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})() + // So existing transformation rules can work on them. node.argnames.forEach(function(arg, i) { var d = arg.definition(); d.fixed = iife.args[i] || make_node(AST_Undefined, iife); @@ -1810,10 +1813,12 @@ merge(Compressor.prototype, { node.name = null; } if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) { - if (!compressor.option("keep_fargs")) { - for (var a = node.argnames, i = a.length; --i >= 0;) { - var sym = a[i]; - if (!(sym.definition().id in in_use_ids)) { + var trim = !compressor.option("keep_fargs"); + for (var a = node.argnames, i = a.length; --i >= 0;) { + var sym = a[i]; + if (!(sym.definition().id in in_use_ids)) { + sym.__unused = true; + if (trim) { a.pop(); compressor.warn("Dropping unused function argument {name} [{file}:{line},{col}]", { name : sym.name, @@ -1822,7 +1827,9 @@ merge(Compressor.prototype, { col : sym.start.col }); } - else break; + } + else { + trim = false; } } } @@ -2609,6 +2616,9 @@ merge(Compressor.prototype, { exp = def.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) === def.scope) { self.expression = exp; } @@ -2617,16 +2627,26 @@ merge(Compressor.prototype, { if (compressor.option("unused") && exp instanceof AST_Function && !exp.uses_arguments - && !exp.uses_eval - && self.args.length > exp.argnames.length) { - var end = exp.argnames.length; - for (var i = end, len = self.args.length; i < len; i++) { - var node = self.args[i].drop_side_effect_free(compressor); - if (node) { - self.args[end++] = node; + && !exp.uses_eval) { + var pos = 0, last = 0; + for (var i = 0, len = self.args.length; i < len; i++) { + var trim = i >= exp.argnames.length; + if (trim || exp.argnames[i].__unused) { + var node = self.args[i].drop_side_effect_free(compressor); + if (node) { + self.args[pos++] = node; + } else if (!trim) { + self.args[pos++] = make_node(AST_Number, self.args[i], { + value: 0 + }); + continue; + } + } else { + self.args[pos++] = self.args[i]; } + last = pos; } - self.args.length = end; + self.args.length = last; } if (compressor.option("unsafe")) { if (exp instanceof AST_SymbolRef && exp.undeclared()) { diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index 728557a6..9f3bf77d 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -761,3 +761,33 @@ assign_chain: { } } } + +issue_1583: { + options = { + keep_fargs: true, + reduce_vars: true, + unused: true, + } + input: { + function m(t) { + (function(e) { + t = e(); + })(function() { + return (function(a) { + return a; + })(function(a) {}); + }); + } + } + expect: { + function m(t) { + (function(e) { + t = (function() { + return (function(a) { + return a; + })(function(a) {}); + })(); + })(); + } + } +} diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index 10dc9d98..734ce4ed 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -1144,3 +1144,111 @@ double_reference: { } } } + +iife_arguments_1: { + options = { + reduce_vars: true, + unused: true, + } + input: { + (function(x) { + console.log(x() === arguments[0]); + })(function f() { + return f; + }); + } + expect: { + (function(x) { + console.log(x() === arguments[0]); + })(function f() { + return f; + }); + } +} + +iife_arguments_2: { + options = { + reduce_vars: true, + unused: true, + } + input: { + (function() { + var x = function f() { + return f; + }; + console.log(x() === arguments[0]); + })(); + } + expect: { + (function() { + console.log(function f() { + return f; + }() === arguments[0]); + })(); + } +} + +iife_eval_1: { + options = { + reduce_vars: true, + unused: true, + } + input: { + (function(x) { + console.log(x() === eval("x")); + })(function f() { + return f; + }); + } + expect: { + (function(x) { + console.log(x() === eval("x")); + })(function f() { + return f; + }); + } +} + +iife_eval_2: { + options = { + reduce_vars: true, + unused: true, + } + input: { + (function() { + var x = function f() { + return f; + }; + console.log(x() === eval("x")); + })(); + } + expect: { + (function() { + var x = function f() { + return f; + }; + console.log(x() === eval("x")); + })(); + } +} + +iife_func_side_effects: { + options = { + reduce_vars: true, + unused: true, + } + input: { + (function(a, b, c) { + return b(); + })(x(), function() { + return y(); + }, z()); + } + expect: { + (function(a, b, c) { + return function() { + return y(); + }(); + })(x(), 0, z()); + } +}