aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Lam S.L <alexlamsl@gmail.com>2019-05-13 21:58:04 +0800
committerGitHub <noreply@github.com>2019-05-13 21:58:04 +0800
commit2e4fbdeb08ea5a9b78285218fed8b968bdd32172 (patch)
tree269c1d6502ea53fbce4c4c849d1a32cd2d8db123
parent3bc7cc82bb442d4d07e918b2972b5189ef53c652 (diff)
downloadtracifyjs-2e4fbdeb08ea5a9b78285218fed8b968bdd32172.tar.gz
tracifyjs-2e4fbdeb08ea5a9b78285218fed8b968bdd32172.zip
enhance `keep_fargs` (#3409)
-rw-r--r--LICENSE2
-rw-r--r--README.md5
-rw-r--r--lib/ast.js14
-rw-r--r--lib/compress.js51
-rw-r--r--test/compress/arguments.js46
-rw-r--r--test/compress/keep_fargs.js1053
6 files changed, 1145 insertions, 26 deletions
diff --git a/LICENSE b/LICENSE
index 4fdaa855..122e8fb9 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
UglifyJS is released under the BSD license:
-Copyright 2012-2018 (c) Mihai Bazon <mihai.bazon@gmail.com>
+Copyright 2012-2019 (c) Mihai Bazon <mihai.bazon@gmail.com>
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 a4f76229..270f20cb 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 1ced7631..03850d4f 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 638fb7c0..57df130a 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 fba271c7..eaff8119 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 00000000..b23e96f0
--- /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"
+}