From 2e4fbdeb08ea5a9b78285218fed8b968bdd32172 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Mon, 13 May 2019 21:58:04 +0800 Subject: [PATCH] enhance `keep_fargs` (#3409) --- LICENSE | 2 +- README.md | 5 +- lib/ast.js | 14 +- lib/compress.js | 51 +- test/compress/arguments.js | 46 ++ test/compress/keep_fargs.js | 1053 +++++++++++++++++++++++++++++++++++ 6 files changed, 1145 insertions(+), 26 deletions(-) create mode 100644 test/compress/keep_fargs.js diff --git a/LICENSE b/LICENSE index 4fdaa8553b..122e8fb97a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ UglifyJS is released under the BSD license: -Copyright 2012-2018 (c) Mihai Bazon +Copyright 2012-2019 (c) Mihai Bazon Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions diff --git a/README.md b/README.md index a4f76229a8..270f20cbda 100644 --- a/README.md +++ b/README.md @@ -664,8 +664,9 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u - `join_vars` (default: `true`) -- join consecutive `var` statements -- `keep_fargs` (default: `true`) -- Prevents the compressor from discarding unused - function arguments. You need this for code which relies on `Function.length`. +- `keep_fargs` (default: `strict`) -- Discard unused function arguments. Code + which relies on `Function.length` will break if this is done indiscriminately, + i.e. when passing `true`. Pass `false` to always retain function arguments. - `keep_fnames` (default: `false`) -- Pass `true` to prevent the compressor from discarding function names. Useful for code relying on diff --git a/lib/ast.js b/lib/ast.js index 1ced76316d..03850d4fde 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -376,7 +376,7 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", { } }, AST_Scope); -var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", { +var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments length_read", { $documentation: "Base class for functions", $propdoc: { name: "[AST_SymbolDeclaration?] the name of this function", @@ -614,6 +614,18 @@ var AST_PropAccess = DEFNODE("PropAccess", "expression property", { $propdoc: { expression: "[AST_Node] the “container” expression", property: "[AST_Node|string] the property to access. For AST_Dot this is always a plain string, while for AST_Sub it's an arbitrary AST_Node" + }, + getProperty: function() { + var p = this.property; + if (p instanceof AST_Constant) { + return p.getValue(); + } + if (p instanceof AST_UnaryPrefix + && p.operator == "void" + && p.expression instanceof AST_Constant) { + return; + } + return p; } }); diff --git a/lib/compress.js b/lib/compress.js index 638fb7c0e7..57df130a27 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -69,7 +69,7 @@ function Compressor(options, false_by_default) { if_return : !false_by_default, inline : !false_by_default, join_vars : !false_by_default, - keep_fargs : true, + keep_fargs : false_by_default || "strict", keep_fnames : false, keep_infinity : false, loops : !false_by_default, @@ -104,6 +104,17 @@ function Compressor(options, false_by_default) { } } if (this.options["inline"] === true) this.options["inline"] = 3; + var keep_fargs = this.options["keep_fargs"]; + this.drop_fargs = keep_fargs == "strict" ? function(lambda) { + if (lambda.length_read) return false; + var name = lambda.name; + if (!name) return true; + if (name.fixed_value() !== lambda) return false; + var def = name.definition(); + if (def.direct_access) return false; + var escaped = def.escaped; + return escaped && escaped.depth != 1; + } : keep_fargs ? return_false : return_true; var pure_funcs = this.options["pure_funcs"]; if (typeof pure_funcs == "function") { this.pure_funcs = pure_funcs; @@ -118,6 +129,8 @@ function Compressor(options, false_by_default) { } else { this.pure_funcs = return_true; } + var sequences = this.options["sequences"]; + this.sequences_limit = sequences == 1 ? 800 : sequences | 0; var top_retain = this.options["top_retain"]; if (top_retain instanceof RegExp) { this.top_retain = function(def) { @@ -141,8 +154,6 @@ function Compressor(options, false_by_default) { funcs: toplevel, vars: toplevel }; - var sequences = this.options["sequences"]; - this.sequences_limit = sequences == 1 ? 800 : sequences | 0; } Compressor.prototype = new TreeTransformer; @@ -272,14 +283,19 @@ merge(Compressor.prototype, { self.transform(tt); }); - function read_property(obj, key) { - key = get_value(key); + function read_property(obj, node) { + var key = node.getProperty(); if (key instanceof AST_Node) return; var value; if (obj instanceof AST_Array) { var elements = obj.elements; if (key == "length") return make_node_from_constant(elements.length, obj); if (typeof key == "number" && key in elements) value = elements[key]; + } else if (obj instanceof AST_Lambda) { + if (key == "length") { + obj.length_read = true; + return make_node_from_constant(obj.argnames.length, obj); + } } else if (obj instanceof AST_Object) { key = "" + key; var props = obj.properties; @@ -326,7 +342,7 @@ merge(Compressor.prototype, { return is_modified(compressor, tw, obj, obj, level + 2); } if (parent instanceof AST_PropAccess && parent.expression === node) { - var prop = read_property(value, parent.property); + var prop = read_property(value, parent); return !immutable && is_modified(compressor, tw, parent, prop, level + 1); } } @@ -490,13 +506,15 @@ merge(Compressor.prototype, { var obj = tw.parent(level + 1); mark_escaped(tw, d, scope, obj, obj, level + 2, depth); } else if (parent instanceof AST_PropAccess && node === parent.expression) { - value = read_property(value, parent.property); + value = read_property(value, parent); mark_escaped(tw, d, scope, parent, value, level + 1, depth + 1); if (value) return; } if (level > 0) return; + if (parent instanceof AST_Call && node === parent.expression) return; if (parent instanceof AST_Sequence && node !== parent.tail_node()) return; if (parent instanceof AST_SimpleStatement) return; + if (parent instanceof AST_Unary) return; d.direct_access = true; } @@ -2217,18 +2235,6 @@ merge(Compressor.prototype, { })); } - function get_value(key) { - if (key instanceof AST_Constant) { - return key.getValue(); - } - if (key instanceof AST_UnaryPrefix - && key.operator == "void" - && key.expression instanceof AST_Constant) { - return; - } - return key; - } - function is_undefined(node, compressor) { return node.is_undefined || node instanceof AST_Undefined @@ -3621,7 +3627,7 @@ merge(Compressor.prototype, { node.name = null; } if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) { - var trim = !compressor.option("keep_fargs"); + var trim = compressor.drop_fargs(node); for (var a = node.argnames, i = a.length; --i >= 0;) { var sym = a[i]; if (!(sym.definition().id in in_use_ids)) { @@ -4068,7 +4074,7 @@ merge(Compressor.prototype, { if (node instanceof AST_PropAccess && node.expression instanceof AST_SymbolRef) { var defs = defs_by_id[node.expression.definition().id]; if (defs) { - var def = defs.get(get_value(node.property)); + var def = defs.get(node.getProperty()); var sym = make_node(AST_SymbolRef, node, { name: def.name, scope: node.expression.scope, @@ -6652,7 +6658,7 @@ merge(Compressor.prototype, { || def.orig.length > 1) { argname = null; } - } else if (!argname && !compressor.option("keep_fargs") && index < fn.argnames.length + 5) { + } else if (!argname && compressor.drop_fargs(fn) && 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), @@ -6665,6 +6671,7 @@ merge(Compressor.prototype, { if (argname && find_if(function(node) { return node.name === argname.name; }, fn.argnames) === argname) { + expr.definition().reassigned = false; var sym = make_node(AST_SymbolRef, self, argname); sym.reference({}); delete argname.__unused; diff --git a/test/compress/arguments.js b/test/compress/arguments.js index fba271c792..eaff8119ff 100644 --- a/test/compress/arguments.js +++ b/test/compress/arguments.js @@ -405,6 +405,52 @@ issue_3273_global_strict_reduce_vars: { ] } +issue_3273_keep_fargs_false: { + options = { + arguments: true, + keep_fargs: false, + reduce_vars: true, + } + input: { + (function() { + "use strict"; + arguments[0]++; + console.log(arguments[0]); + })(0); + } + expect: { + (function(argument_0) { + "use strict"; + argument_0++; + console.log(argument_0); + })(0); + } + expect_stdout: "1" +} + +issue_3273_keep_fargs_strict: { + options = { + arguments: true, + keep_fargs: "strict", + reduce_vars: true, + } + input: { + (function() { + "use strict"; + arguments[0]++; + console.log(arguments[0]); + })(0); + } + expect: { + (function(argument_0) { + "use strict"; + argument_0++; + console.log(argument_0); + })(0); + } + expect_stdout: "1" +} + issue_3282_1: { options = { arguments: true, diff --git a/test/compress/keep_fargs.js b/test/compress/keep_fargs.js new file mode 100644 index 0000000000..b23e96f078 --- /dev/null +++ b/test/compress/keep_fargs.js @@ -0,0 +1,1053 @@ +keep_fargs_false: { + options = { + keep_fargs: false, + unused: true, + } + input: { + console.log(function f(a) { + return f.length; + }(), function g(b) { + return g; + }().length); + function h(c) { + return h.length; + } + function i(d) { + return i; + } + function j(e) {} + console.log(h(), i().length, j.length); + } + expect: { + console.log(function f() { + return f.length; + }(), function g() { + return g; + }().length); + function h() { + return h.length; + } + function i() { + return i; + } + function j() {} + console.log(h(), i().length, j.length); + } +} + +keep_fargs_strict: { + options = { + keep_fargs: "strict", + unused: true, + } + input: { + console.log(function f(a) { + return f.length; + }(), function g(b) { + return g; + }().length); + function h(c) { + return h.length; + } + function i(d) { + return i; + } + function j(e) {} + console.log(h(), i().length, j.length); + } + expect: { + console.log(function f(a) { + return f.length; + }(), function g(b) { + return g; + }().length); + function h(c) { + return h.length; + } + function i(d) { + return i; + } + function j(e) {} + console.log(h(), i().length, j.length); + } + expect_stdout: [ + "1 1", + "1 1 1", + ] +} + +keep_fargs_true: { + options = { + keep_fargs: true, + unused: true, + } + input: { + console.log(function f(a) { + return f.length; + }(), function g(b) { + return g; + }().length); + function h(c) { + return h.length; + } + function i(d) { + return i; + } + function j(e) {} + console.log(h(), i().length, j.length); + } + expect: { + console.log(function f(a) { + return f.length; + }(), function g(b) { + return g; + }().length); + function h(c) { + return h.length; + } + function i(d) { + return i; + } + function j(e) {} + console.log(h(), i().length, j.length); + } + expect_stdout: [ + "1 1", + "1 1 1", + ] +} + +replace_index: { + options = { + arguments: true, + evaluate: true, + keep_fargs: "strict", + properties: true, + } + input: { + var arguments = []; + console.log(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: { + var arguments = []; + console.log(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", + ] +} + +replace_index_strict: { + options = { + arguments: true, + evaluate: true, + keep_fargs: "strict", + 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", + ] +} + +issue_1858: { + options = { + collapse_vars: true, + keep_fargs: "strict", + pure_getters: true, + unused: true, + } + input: { + console.log(function(x) { + var a = {}, b = a.b = x; + return a.b + b; + }(1)); + } + expect: { + console.log(function() { + var a = {}, b = a.b = 1; + return a.b + b; + }()); + } + expect_stdout: "2" +} + +issue_2187_2: { + options = { + collapse_vars: true, + keep_fargs: "strict", + unused: true, + } + input: { + var b = 1; + console.log(function(a) { + return a && ++b; + }(b--)); + } + expect: { + var b = 1; + console.log(function() { + return b-- && ++b; + }()); + } + expect_stdout: "1" +} + +issue_2203_2: { + options = { + collapse_vars: true, + keep_fargs: "strict", + unused: true, + } + input: { + a = "PASS"; + console.log({ + a: "FAIL", + b: function() { + return function(c) { + return c.a; + }((String, (Object, function() { + return this; + }()))); + } + }.b()); + } + expect: { + a = "PASS"; + console.log({ + a: "FAIL", + b: function() { + return function() { + return (String, (Object, function() { + return this; + }())).a; + }(); + } + }.b()); + } + expect_stdout: "PASS" +} + +issue_2298: { + options = { + collapse_vars: true, + keep_fargs: "strict", + passes: 2, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + !function() { + function f() { + var a = undefined; + var undefined = a++; + try { + !function g(b) { + b[1] = "foo"; + }(); + console.log("FAIL"); + } catch (e) { + console.log("PASS"); + } + } + f(); + }(); + } + expect: { + !function() { + (function() { + var a = undefined; + var undefined = a++; + try { + !function() { + (void 0)[1] = "foo"; + }(); + console.log("FAIL"); + } catch (e) { + console.log("PASS"); + } + })(); + }(); + } + expect_stdout: "PASS" +} + +issue_2319_1: { + options = { + collapse_vars: true, + keep_fargs: "strict", + unused: true, + } + input: { + console.log(function(a) { + return a; + }(!function() { + return this; + }())); + } + expect: { + console.log(function() { + return !function() { + return this; + }(); + }()); + } + expect_stdout: "false" +} + +issue_2319_2: { + options = { + collapse_vars: true, + keep_fargs: "strict", + unused: true, + } + input: { + console.log(function(a) { + "use strict"; + return a; + }(!function() { + return this; + }())); + } + expect: { + console.log(function(a) { + "use strict"; + return a; + }(!function() { + return this; + }())); + } + expect_stdout: "false" +} + +issue_2319_3: { + options = { + collapse_vars: true, + keep_fargs: "strict", + unused: true, + } + input: { + "use strict"; + console.log(function(a) { + return a; + }(!function() { + return this; + }())); + } + expect: { + "use strict"; + console.log(function() { + return !function() { + return this; + }(); + }()); + } + expect_stdout: "true" +} + +issue_2425_1: { + options = { + collapse_vars: true, + keep_fargs: "strict", + unused: true, + } + input: { + var a = 8; + (function(b) { + b.toString(); + })(--a, a |= 10); + console.log(a); + } + expect: { + var a = 8; + (function(b) { + b.toString(); + })(--a, a |= 10); + console.log(a); + } + expect_stdout: "15" +} + +issue_2425_2: { + options = { + collapse_vars: true, + keep_fargs: "strict", + unused: true, + } + input: { + var a = 8; + (function(b, c) { + b.toString(); + })(--a, a |= 10); + console.log(a); + } + expect: { + var a = 8; + (function(b) { + b.toString(); + })(--a, a |= 10); + console.log(a); + } + expect_stdout: "15" +} + +issue_2425_3: { + options = { + collapse_vars: true, + keep_fargs: "strict", + unused: true, + } + input: { + var a = 8; + (function(b, b) { + b.toString(); + })(--a, a |= 10); + console.log(a); + } + expect: { + var a = 8; + (function() { + (a |= 10).toString(); + })(--a); + console.log(a); + } + expect_stdout: "15" +} + +issue_2436_13: { + options = { + collapse_vars: true, + keep_fargs: "strict", + passes: 2, + reduce_vars: true, + unused: true, + } + input: { + var a = "PASS"; + (function() { + function f(b) { + (function g(b) { + var b = b && (b.null = "FAIL"); + })(a); + } + f(); + })(); + console.log(a); + } + expect: { + var a = "PASS"; + (function() { + (function() { + (function() { + a && (a.null = "FAIL"); + })(); + })(); + })(); + console.log(a); + } + expect_stdout: "PASS" +} + +issue_2506: { + options = { + collapse_vars: true, + keep_fargs: "strict", + passes: 2, + reduce_vars: true, + unused: true, + } + input: { + var c = 0; + function f0(bar) { + function f1(Infinity_2) { + function f13(NaN) { + if (false <= NaN & this >> 1 >= 0) { + c++; + } + } + var b_2 = f13(NaN, c++); + } + var bar = f1(-3, -1); + } + f0(false); + console.log(c); + } + expect: { + var c = 0; + function f0(bar) { + (function() { + (function() { + if (false <= 0/0 & this >> 1 >= 0) + c++; + })(c++); + })(); + } + f0(false); + console.log(c); + } + expect_stdout: "1" +} + +issue_2226_1: { + options = { + keep_fargs: "strict", + side_effects: true, + unused: true, + } + input: { + function f1() { + var a = b; + a += c; + } + function f2(a) { + a <<= b; + } + function f3(a) { + --a; + } + function f4() { + var a = b; + return a *= c; + } + function f5(a) { + x(a /= b); + } + } + expect: { + function f1() { + b; + c; + } + function f2(a) { + b; + } + function f3(a) { + 0; + } + function f4() { + var a = b; + return a *= c; + } + function f5(a) { + x(a /= b); + } + } +} + +issue_2226_2: { + options = { + collapse_vars: true, + keep_fargs: "strict", + sequences: true, + side_effects: true, + unused: true, + } + input: { + console.log(function(a, b) { + a += b; + return a; + }(1, 2)); + } + expect: { + console.log(function(a) { + return a += 2; + }(1)); + } + expect_stdout: "3" +} + +issue_2226_3: { + options = { + collapse_vars: true, + keep_fargs: "strict", + side_effects: true, + unused: true, + } + input: { + console.log(function(a, b) { + a += b; + return a; + }(1, 2)); + } + expect: { + console.log(function(a) { + return a += 2; + }(1)); + } + expect_stdout: "3" +} + +issue_3192: { + options = { + keep_fargs: "strict", + 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() { + "use strict"; + console.log("foo", arguments[0]); + })("bar"); + } + expect_stdout: [ + "foo foo", + "foo bar", + ] +} + +if_increment: { + options = { + evaluate: true, + keep_fargs: "strict", + reduce_vars: true, + unused: true, + } + input: { + console.log(function(a) { + if (console) + return ++a; + }(0)); + } + expect: { + console.log(function() { + if (console) + return 1; + }()); + } + expect_stdout: "1" +} + +try_increment: { + options = { + evaluate: true, + keep_fargs: "strict", + reduce_vars: true, + unused: true, + } + input: { + console.log(function(a) { + try { + return ++a; + } catch (e) {} + }(0)); + } + expect: { + console.log(function() { + try { + return 1; + } catch (e) {} + }()); + } + expect_stdout: "1" +} + +issue_2630_3: { + options = { + inline: true, + keep_fargs: "strict", + reduce_vars: true, + unused: true, + } + input: { + var x = 2, a = 1; + (function() { + function f1(a) { + f2(); + --x >= 0 && f1({}); + } + f1(a++); + function f2() { + a++; + } + })(); + console.log(a); + } + expect: { + var x = 2, a = 1; + (function() { + (function f1() { + f2(); + --x >= 0 && f1({}); + })(a++); + function f2() { + a++; + } + })(); + console.log(a); + } + expect_stdout: "5" +} + +issue_3364: { + options = { + functions: true, + keep_fargs: "strict", + reduce_vars: true, + toplevel: true, + unused: true, + } + mangle = {} + input: { + var s = 2, a = 100, b = 10, c = 0; + function f(p, e, r) { + try { + for (var i = 1; i-- > 0;) + var a = function(x) { + function g(y) { + y && y[a++]; + } + var x = g(--s >= 0 && f(c++)); + for (var j = 1; --j > 0;); + }(); + } catch (e) { + try { + return; + } catch (z) { + for (var k = 1; --k > 0;) { + for (var l = 1; l > 0; --l) { + var n = function() {}; + for (var k in n) + var o = (n, k); + } + } + } + } + } + var r = f(); + console.log(c); + } + expect: { + var s = 2, c = 0; + (function o() { + try { + for (var r = 1; r-- > 0;) + var n = function() { + (function(r) { + r && r[n++]; + })(--s >= 0 && o(c++)); + for (var r = 1; --r > 0;); + }(); + } catch (r) { + try { + return; + } catch (r) { + for (var a = 1; --a > 0;) + for (var f = 1; f > 0; --f) { + function t() {} + for (var a in t); + } + } + } + })(); + console.log(c); + } + expect_stdout: "2" +} + +defun_label: { + options = { + keep_fargs: "strict", + passes: 2, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + !function() { + function f(a) { + L: { + if (a) break L; + return 1; + } + } + console.log(f(2)); + }(); + } + expect: { + !function() { + console.log(function() { + L: { + if (2) break L; + return 1; + } + }()); + }(); + } + expect_stdout: true +} + +iife_func_side_effects: { + options = { + keep_fargs: "strict", + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + function x() { + console.log("x"); + } + function y() { + console.log("y"); + } + function z() { + console.log("z"); + } + (function(a, b, c) { + function y() { + console.log("FAIL"); + } + return y + b(); + })(x(), function() { + return y(); + }, z()); + } + expect: { + function x() { + console.log("x"); + } + function y() { + console.log("y"); + } + function z() { + console.log("z"); + } + (function(a, b) { + return function() { + console.log("FAIL"); + } + b(); + })(x(), function() { + return y(); + }, z()); + } + expect_stdout: [ + "x", + "z", + "y", + ] +} + +issue_1595_1: { + options = { + evaluate: true, + keep_fargs: "strict", + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function f(a) { + return f(a + 1); + })(2); + } + expect: { + (function f(a) { + return f(a + 1); + })(2); + } +} + +issue_1595_2: { + options = { + evaluate: true, + keep_fargs: "strict", + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function f(a) { + return g(a + 1); + })(2); + } + expect: { + (function(a) { + return g(a + 1); + })(2); + } +} + +issue_1595_3: { + options = { + evaluate: true, + keep_fargs: "strict", + passes: 2, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function f(a) { + return g(a + 1); + })(2); + } + expect: { + (function() { + return g(3); + })(); + } +} + +issue_1595_4: { + options = { + evaluate: true, + keep_fargs: "strict", + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function iife(a, b, c) { + console.log(a, b, c); + if (a) iife(a - 1, b, c); + })(3, 4, 5); + } + expect: { + (function iife(a, b, c) { + console.log(a, b, c); + if (a) iife(a - 1, b, c); + })(3, 4, 5); + } + expect_stdout: true +} + +duplicate_lambda_defun_name_1: { + options = { + keep_fargs: "strict", + reduce_vars: true, + } + input: { + console.log(function f(a) { + function f() {} + return f.length; + }()); + } + expect: { + console.log(function f(a) { + function f() {} + return f.length; + }()); + } + expect_stdout: "0" +} + +duplicate_lambda_defun_name_2: { + options = { + keep_fargs: "strict", + passes: 2, + reduce_vars: true, + unused: true, + } + input: { + console.log(function f(a) { + function f() {} + return f.length; + }()); + } + expect: { + console.log(function() { + return function() {}.length; + }()); + } + expect_stdout: "0" +} + +function_name_mangle: { + options = { + keep_fargs: "strict", + keep_fnames: true, + reduce_vars: true, + unused: true, + } + mangle = {} + input: { + (function() { + function foo(bar) {} + console.log(typeof foo); + })(); + } + expect_exact: "(function(){console.log(typeof function o(){})})();" + expect_stdout: "function" +} + +function_name_mangle_ie8: { + options = { + keep_fargs: "strict", + keep_fnames: true, + reduce_vars: true, + unused: true, + } + mangle = { + ie8: true, + toplevel: true, + } + input: { + (function() { + function foo(bar) {} + console.log(typeof foo); + })(); + } + expect_exact: "(function(){console.log(typeof function o(){})})();" + expect_stdout: "function" +}