diff options
-rw-r--r-- | README.md | 3 | ||||
-rw-r--r-- | lib/compress.js | 28 | ||||
-rw-r--r-- | test/compress/arguments.js | 119 | ||||
-rw-r--r-- | test/compress/hoist_props.js | 12 |
4 files changed, 155 insertions, 7 deletions
@@ -598,6 +598,9 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u ## Compress options +- `arguments` (default: `true`) -- replace `arguments[index]` with function + parameter name whenever possible. + - `booleans` (default: `true`) -- various optimizations for boolean context, for example `!!a ? b : c → a ? b : c` diff --git a/lib/compress.js b/lib/compress.js index 97e4c11d..a63a5c66 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -48,6 +48,7 @@ function Compressor(options, false_by_default) { return new Compressor(options, false_by_default); TreeTransformer.call(this, this.before, this.after); this.options = defaults(options, { + arguments : !false_by_default, booleans : !false_by_default, collapse_vars : !false_by_default, comparisons : !false_by_default, @@ -3551,7 +3552,7 @@ merge(Compressor.prototype, { AST_Scope.DEFMETHOD("make_var_name", function(prefix) { var var_names = this.var_names(); - prefix = prefix.replace(/[^a-z_$]+/ig, "_"); + prefix = prefix.replace(/(?:^[^a-z_$]|[^a-z0-9_$])/ig, "_"); var name = prefix; for (var i = 0; var_names[name]; i++) name = prefix + "$" + i; var_names[name] = true; @@ -5954,6 +5955,31 @@ 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/test/compress/arguments.js b/test/compress/arguments.js new file mode 100644 index 00000000..e8cc690f --- /dev/null +++ b/test/compress/arguments.js @@ -0,0 +1,119 @@ +replace_index: { + options = { + arguments: true, + evaluate: true, + properties: true, + } + input: { + console.log(arguments && arguments[0]); + (function() { + console.log(arguments[1], arguments["1"], arguments["foo"]); + })("bar", 42); + (function(a, b) { + console.log(arguments[1], arguments["1"], arguments["foo"]); + })("bar", 42); + (function(arguments) { + console.log(arguments[1], arguments["1"], arguments["foo"]); + })("bar", 42); + (function() { + var arguments; + console.log(arguments[1], arguments["1"], arguments["foo"]); + })("bar", 42); + } + expect: { + console.log(arguments && arguments[0]); + (function() { + console.log(arguments[1], arguments[1], arguments.foo); + })("bar", 42); + (function(a, b) { + console.log(b, b, arguments.foo); + })("bar", 42); + (function(arguments) { + console.log(arguments[1], arguments[1], arguments.foo); + })("bar", 42); + (function() { + var arguments; + console.log(arguments[1], arguments[1], arguments.foo); + })("bar", 42); + } + expect_stdout: [ + "undefined", + "42 42 undefined", + "42 42 undefined", + "a a undefined", + "42 42 undefined", + ] +} + +replace_index_keep_fargs: { + options = { + arguments: true, + evaluate: true, + keep_fargs: false, + properties: true, + } + input: { + console.log(arguments && arguments[0]); + (function() { + console.log(arguments[1], arguments["1"], arguments["foo"]); + })("bar", 42); + (function(a, b) { + console.log(arguments[1], arguments["1"], arguments["foo"]); + })("bar", 42); + (function(arguments) { + console.log(arguments[1], arguments["1"], arguments["foo"]); + })("bar", 42); + (function() { + var arguments; + console.log(arguments[1], arguments["1"], arguments["foo"]); + })("bar", 42); + } + expect: { + console.log(arguments && arguments[0]); + (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); + (function(arguments) { + console.log(arguments[1], arguments[1], arguments.foo); + })("bar", 42); + (function() { + var arguments; + console.log(arguments[1], arguments[1], arguments.foo); + })("bar", 42); + } + expect_stdout: [ + "undefined", + "42 42 undefined", + "42 42 undefined", + "a a undefined", + "42 42 undefined", + ] +} + +modified: { + options = { + arguments: true, + } + input: { + (function(a, b) { + var c = arguments[0]; + var d = arguments[1]; + a = "foo"; + b++; + console.log(a, b, c, d, arguments[0], arguments[1]); + })("bar", 42); + } + expect: { + (function(a, b) { + var c = a; + var d = b; + a = "foo"; + b++; + console.log(a, b, c, d, a, b); + })("bar", 42); + } + expect_stdout: "foo 43 bar 42 foo 43" +} diff --git a/test/compress/hoist_props.js b/test/compress/hoist_props.js index 03867f78..26887af2 100644 --- a/test/compress/hoist_props.js +++ b/test/compress/hoist_props.js @@ -239,14 +239,14 @@ name_collision_2: { input: { var o = { p: 1, - 0: function(x) { + "+": function(x) { return x; }, - 1: function(x) { + "-": function(x) { return x + 1; } }, o__$0 = 2, o__$1 = 3; - console.log(o.p === o.p, o[0](4), o[1](5), o__$0, o__$1); + console.log(o.p === o.p, o["+"](4), o["-"](5), o__$0, o__$1); } expect: { var o_p = 1, @@ -273,14 +273,14 @@ name_collision_3: { input: { var o = { p: 1, - 0: function(x) { + "+": function(x) { return x; }, - 1: function(x) { + "-": function(x) { return x + 1; } }, o__$0 = 2, o__$1 = 3; - console.log(o.p === o.p, o[0](4), o[1](5)); + console.log(o.p === o.p, o["+"](4), o["-"](5)); } expect: { var o_p = 1, |