From: Alex Lam S.L Date: Thu, 21 Jun 2018 06:10:37 +0000 (+0800) Subject: enhance `arguments` (#3193) X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=766a4147d4891a093b550e958268c48104330b52;p=UglifyJS.git enhance `arguments` (#3193) fixes #3192 --- diff --git a/lib/compress.js b/lib/compress.js index 56c8fc47..0268e07f 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -518,14 +518,15 @@ merge(Compressor.prototype, { var sym = node.left; if (!(sym instanceof AST_SymbolRef)) return; var d = sym.definition(); + var safe = safe_to_assign(tw, d, sym.scope, node.right); + d.assignments++; + if (!safe) return; var fixed = d.fixed; if (!fixed && node.operator != "=") return; - if (!safe_to_assign(tw, d, sym.scope, node.right)) return; var eq = node.operator == "="; var value = eq ? node.right : node; if (is_modified(compressor, tw, node, value, 0)) return; d.references.push(sym); - d.assignments++; if (!eq) d.chained = true; d.fixed = eq ? function() { return node.right; @@ -657,7 +658,7 @@ merge(Compressor.prototype, { // So existing transformation rules can work on them. node.argnames.forEach(function(arg, i) { var d = arg.definition(); - if (!node.uses_arguments && d.fixed === undefined) { + if (d.fixed === undefined && (!node.uses_arguments || tw.has_directive("use strict"))) { d.fixed = function() { return iife.args[i] || make_node(AST_Undefined, iife); }; @@ -760,11 +761,12 @@ merge(Compressor.prototype, { var exp = node.expression; if (!(exp instanceof AST_SymbolRef)) return; var d = exp.definition(); + var safe = safe_to_assign(tw, d, exp.scope, true); + d.assignments++; + if (!safe) return; var fixed = d.fixed; if (!fixed) return; - if (!safe_to_assign(tw, d, exp.scope, true)) return; d.references.push(exp); - d.assignments++; d.chained = true; d.fixed = function() { return make_node(AST_Binary, node, { @@ -3289,6 +3291,15 @@ merge(Compressor.prototype, { // this scope (not in nested scopes). var scope = this; var tw = new TreeWalker(function(node, descend) { + if (node instanceof AST_Lambda && node.uses_arguments && !tw.has_directive("use strict")) { + node.argnames.forEach(function(argname) { + var def = argname.definition(); + if (!(def.id in in_use_ids)) { + in_use_ids[def.id] = true; + in_use.push(def); + } + }); + } if (node === self) return; if (node instanceof AST_Defun) { var node_def = node.name.definition(); @@ -3376,8 +3387,7 @@ merge(Compressor.prototype, { // any declarations with same name will overshadow // name of this anonymous function and can therefore // never be used anywhere - if (!(def.id in in_use_ids) || def.orig.length > 1) - node.name = null; + if (!(def.id in in_use_ids) || def.orig.length > 1) node.name = null; } if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) { var trim = !compressor.option("keep_fargs"); @@ -3389,8 +3399,7 @@ merge(Compressor.prototype, { a.pop(); compressor[sym.unreferenced() ? "warn" : "info"]("Dropping unused function argument {name} [{file}:{line},{col}]", template(sym)); } - } - else { + } else { trim = false; } } @@ -6208,6 +6217,39 @@ merge(Compressor.prototype, { } } } + var fn; + if (compressor.option("arguments") + && expr instanceof AST_SymbolRef + && expr.name == "arguments" + && expr.definition().orig.length == 1 + && (fn = expr.scope) instanceof AST_Lambda + && prop instanceof AST_Number) { + var index = prop.getValue(); + var argname = fn.argnames[index]; + if (argname && compressor.has_directive("use strict")) { + var def = argname.definition(); + if (!compressor.option("reduce_vars") || def.assignments || def.orig.length > 1) { + argname = null; + } + } else if (!argname && !compressor.option("keep_fargs") && index < fn.argnames.length + 5) { + while (index >= fn.argnames.length) { + argname = make_node(AST_SymbolFunarg, fn, { + name: fn.make_var_name("argument_" + fn.argnames.length), + scope: fn + }); + fn.argnames.push(argname); + fn.enclosed.push(fn.def_variable(argname)); + } + } + if (argname && find_if(function(node) { + return node.name === argname.name; + }, fn.argnames) === argname) { + var sym = make_node(AST_SymbolRef, self, argname); + sym.reference({}); + delete argname.__unused; + return sym; + } + } if (is_lhs(self, compressor.parent())) return self; if (key !== prop) { var sub = self.flatten_object(property, compressor); @@ -6251,31 +6293,6 @@ merge(Compressor.prototype, { }); } } - var fn; - if (compressor.option("arguments") - && expr instanceof AST_SymbolRef - && expr.name == "arguments" - && expr.definition().orig.length == 1 - && (fn = expr.scope) instanceof AST_Lambda - && prop instanceof AST_Number) { - var index = prop.getValue(); - var argname = fn.argnames[index]; - if (!argname && !compressor.option("keep_fargs")) { - while (index >= fn.argnames.length) { - argname = make_node(AST_SymbolFunarg, fn, { - name: fn.make_var_name("argument_" + fn.argnames.length), - scope: fn - }); - fn.argnames.push(argname); - fn.enclosed.push(fn.def_variable(argname)); - } - } - if (argname) { - var sym = make_node(AST_SymbolRef, self, argname); - sym.reference({}); - return sym; - } - } var ev = self.evaluate(compressor); if (ev !== self) { ev = make_node_from_constant(ev, self).optimize(compressor); diff --git a/lib/utils.js b/lib/utils.js index ca4b2d4b..7a51fb80 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -52,9 +52,7 @@ function member(name, array) { } function find_if(func, array) { - for (var i = 0, n = array.length; i < n; ++i) { - if (func(array[i])) return array[i]; - } + for (var i = array.length; --i >= 0;) if (func(array[i])) return array[i]; } function repeat_string(str, i) { diff --git a/test/compress/arguments.js b/test/compress/arguments.js index e8cc690f..04857441 100644 --- a/test/compress/arguments.js +++ b/test/compress/arguments.js @@ -5,7 +5,8 @@ replace_index: { properties: true, } input: { - console.log(arguments && arguments[0]); + var arguments = []; + console.log(arguments[0]); (function() { console.log(arguments[1], arguments["1"], arguments["foo"]); })("bar", 42); @@ -21,7 +22,8 @@ replace_index: { })("bar", 42); } expect: { - console.log(arguments && arguments[0]); + var arguments = []; + console.log(arguments[0]); (function() { console.log(arguments[1], arguments[1], arguments.foo); })("bar", 42); @@ -45,6 +47,37 @@ replace_index: { ] } +replace_index_strict: { + options = { + arguments: true, + evaluate: true, + properties: true, + reduce_vars: true, + } + input: { + "use strict"; + (function() { + console.log(arguments[1], arguments["1"], arguments["foo"]); + })("bar", 42); + (function(a, b) { + console.log(arguments[1], arguments["1"], arguments["foo"]); + })("bar", 42); + } + expect: { + "use strict"; + (function() { + console.log(arguments[1], arguments[1], arguments.foo); + })("bar", 42); + (function(a, b) { + console.log(b, b, arguments.foo); + })("bar", 42); + } + expect_stdout: [ + "42 42 undefined", + "42 42 undefined", + ] +} + replace_index_keep_fargs: { options = { arguments: true, @@ -53,7 +86,8 @@ replace_index_keep_fargs: { properties: true, } input: { - console.log(arguments && arguments[0]); + var arguments = []; + console.log(arguments[0]); (function() { console.log(arguments[1], arguments["1"], arguments["foo"]); })("bar", 42); @@ -69,7 +103,8 @@ replace_index_keep_fargs: { })("bar", 42); } expect: { - console.log(arguments && arguments[0]); + var arguments = []; + console.log(arguments[0]); (function(argument_0, argument_1) { console.log(argument_1, argument_1, arguments.foo); })("bar", 42); @@ -93,6 +128,38 @@ replace_index_keep_fargs: { ] } +replace_index_keep_fargs_strict: { + options = { + arguments: true, + evaluate: true, + keep_fargs: false, + properties: true, + reduce_vars: true, + } + input: { + "use strict"; + (function() { + console.log(arguments[1], arguments["1"], arguments["foo"]); + })("bar", 42); + (function(a, b) { + console.log(arguments[1], arguments["1"], arguments["foo"]); + })("bar", 42); + } + expect: { + "use strict"; + (function(argument_0, argument_1) { + console.log(argument_1, argument_1, arguments.foo); + })("bar", 42); + (function(a, b) { + console.log(b, b, arguments.foo); + })("bar", 42); + } + expect_stdout: [ + "42 42 undefined", + "42 42 undefined", + ] +} + modified: { options = { arguments: true, @@ -101,8 +168,10 @@ modified: { (function(a, b) { var c = arguments[0]; var d = arguments[1]; - a = "foo"; + var a = "foo"; b++; + arguments[0] = "moo"; + arguments[1] *= 2; console.log(a, b, c, d, arguments[0], arguments[1]); })("bar", 42); } @@ -110,10 +179,61 @@ modified: { (function(a, b) { var c = a; var d = b; - a = "foo"; + var a = "foo"; b++; + a = "moo"; + b *= 2; console.log(a, b, c, d, a, b); })("bar", 42); } - expect_stdout: "foo 43 bar 42 foo 43" + expect_stdout: "moo 86 bar 42 moo 86" +} + +modified_strict: { + options = { + arguments: true, + reduce_vars: true, + } + input: { + "use strict"; + (function(a, b) { + var c = arguments[0]; + var d = arguments[1]; + var a = "foo"; + b++; + arguments[0] = "moo"; + arguments[1] *= 2; + console.log(a, b, c, d, arguments[0], arguments[1]); + })("bar", 42); + } + expect: { + "use strict"; + (function(a, b) { + var c = arguments[0]; + var d = arguments[1]; + var a = "foo"; + b++; + arguments[0] = "moo"; + arguments[1] *= 2; + console.log(a, b, c, d, arguments[0], arguments[1]); + })("bar", 42); + } + expect_stdout: "foo 43 bar 42 moo 84" +} + +duplicate_argname: { + options = { + arguments: true, + } + input: { + (function(a, b, a) { + console.log(a, b, arguments[0], arguments[1], arguments[2]); + })("foo", 42, "bar"); + } + expect: { + (function(a, b, a) { + console.log(a, b, arguments[0], b, a); + })("foo", 42, "bar"); + } + expect_stdout: "bar 42 foo 42 bar" } diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index 59990b58..e4daa4fc 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -1926,3 +1926,31 @@ issue_3146_4: { } expect_stdout: "PASS" } + +issue_3192: { + options = { + unused: true, + } + input: { + (function(a) { + console.log(a = "foo", arguments[0]); + })("bar"); + (function(a) { + "use strict"; + console.log(a = "foo", arguments[0]); + })("bar"); + } + expect: { + (function(a) { + console.log(a = "foo", arguments[0]); + })("bar"); + (function(a) { + "use strict"; + console.log("foo", arguments[0]); + })("bar"); + } + expect_stdout: [ + "foo foo", + "foo bar", + ] +}