diff --git a/lib/compress.js b/lib/compress.js index 49f2cd39..587f0888 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -1665,6 +1665,18 @@ Compressor.prototype.compress = function(node) { } } + function find_try(compressor, level, node, scope, may_throw, sync) { + for (var parent; parent = compressor.parent(level++); node = parent) { + if (parent === scope) return false; + if (sync && parent instanceof AST_Lambda) { + if (parent.name || is_async(parent) || is_generator(parent)) return true; + } else if (parent instanceof AST_Try) { + if (parent.bfinally && parent.bfinally !== node) return true; + if (may_throw && parent.bcatch && parent.bcatch !== node) return true; + } + } + } + var identifier_atom = makePredicate("Infinity NaN undefined"); function is_lhs_read_only(lhs, compressor) { if (lhs instanceof AST_ObjectIdentity) return true; @@ -2585,7 +2597,7 @@ Compressor.prototype.compress = function(node) { }); } if (node instanceof AST_DestructuredObject) { - if (!value.is_defined(compressor)) return true; + if (value.may_throw_on_access(compressor)) return true; return !all(node.properties, function(prop) { if (prop.key instanceof AST_Node && reject(prop.key)) return false; return !may_throw_arg(reject, prop.value); @@ -2608,11 +2620,18 @@ Compressor.prototype.compress = function(node) { })) { var fn_strict = fn.in_strict_mode(compressor) && !fn.parent_scope.resolve(true).in_strict_mode(compressor); - var has_await = is_async(fn) ? function(node) { - return node instanceof AST_Symbol && node.name == "await"; - } : function(node) { - return node instanceof AST_Await && !tw.find_parent(AST_Scope); - }; + var check_arg = false, has_await; + if (is_async(fn)) { + check_arg = true; + has_await = function(node) { + return node instanceof AST_Symbol && node.name == "await"; + }; + } else { + check_arg = find_try(compressor, 1, iife, null, true, true); + has_await = function(node) { + return node instanceof AST_Await && !tw.find_parent(AST_Scope); + }; + } var arg_scope = null; var tw = new TreeWalker(function(node, descend) { if (!arg) return true; @@ -2645,18 +2664,21 @@ Compressor.prototype.compress = function(node) { for (var i = fn.argnames.length; --i >= 0;) { var sym = fn.argnames[i]; var arg = args[i]; - var value; + var value = null; if (sym instanceof AST_DefaultValue) { value = sym.value; sym = sym.name; args[len + i] = value; } if (sym instanceof AST_Destructured) { - if (!may_throw_arg(function(node) { + if (check_arg && may_throw_arg(function(node) { return node.has_side_effects(compressor); - }, sym, arg)) continue; - candidates.length = 0; - break; + }, sym, arg)) { + candidates.length = 0; + break; + } + args[len + i] = fn.argnames[i]; + continue; } if (names.has(sym.name)) continue; names.set(sym.name, true); @@ -12656,15 +12678,7 @@ Compressor.prototype.compress = function(node) { self.right = make_node(AST_Null, right); var may_throw = node.may_throw(compressor); self.right = right; - for (var parent; parent = compressor.parent(level++); node = parent) { - if (parent === scope) return false; - if (sync && parent instanceof AST_Lambda) { - if (parent.name || is_async(parent) || is_generator(parent)) return true; - } else if (parent instanceof AST_Try) { - if (parent.bfinally && parent.bfinally !== node) return true; - if (may_throw && parent.bcatch && parent.bcatch !== node) return true; - } - } + return find_try(compressor, level, node, scope, may_throw, sync); } function strip_assignment(def) { diff --git a/test/compress/awaits.js b/test/compress/awaits.js index 5fee9e02..220f6fae 100644 --- a/test/compress/awaits.js +++ b/test/compress/awaits.js @@ -1046,6 +1046,60 @@ collapse_vars_3: { node_version: ">=8" } +collapse_funarg_1: { + options = { + collapse_vars: true, + unused: true, + } + input: { + A = "FAIL"; + var a = "PASS"; + (async function({}, b) { + return b; + })(null, A = a); + console.log(A); + } + expect: { + A = "FAIL"; + var a = "PASS"; + (async function({}, b) { + return b; + })(null, A = a); + console.log(A); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +collapse_funarg_2: { + options = { + collapse_vars: true, + unused: true, + } + input: { + A = "FAIL"; + B = "PASS"; + (async function() { + console.log(function({}, a) { + return a; + }(null, A = B)); + })(); + console.log(A); + } + expect: { + A = "FAIL"; + B = "PASS"; + (async function() { + console.log(function({}, a) { + return a; + }(null, A = B)); + })(); + console.log(A); + } + expect_stdout: "PASS" + node_version: ">=8" +} + collapse_property_lambda: { options = { collapse_vars: true, diff --git a/test/compress/default-values.js b/test/compress/default-values.js index bb1a1133..d11a256e 100644 --- a/test/compress/default-values.js +++ b/test/compress/default-values.js @@ -1170,6 +1170,49 @@ mangle_arrow_2_toplevel: { node_version: ">=6.9.3" } +collapse_preceding_simple_arg: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var a = "foo"; + console.log(function(b, c = "bar") { + return b + c; + }(a, a)); + } + expect: { + var a = "foo"; + console.log(function(b, c = "bar") { + return a + c; + }(0, a)); + } + expect_stdout: "foofoo" + node_version: ">=6" +} + +drop_preceding_simple_arg: { + options = { + collapse_vars: true, + keep_fargs: false, + unused: true, + } + input: { + var a = "foo"; + console.log(function(b, c = "bar") { + return b + c; + }(a, a)); + } + expect: { + var a = "foo"; + console.log(function(c = "bar") { + return a + c; + }(a)); + } + expect_stdout: "foofoo" + node_version: ">=6" +} + issue_4444: { options = { collapse_vars: true, diff --git a/test/compress/destructured.js b/test/compress/destructured.js index 4f615e92..4cef240b 100644 --- a/test/compress/destructured.js +++ b/test/compress/destructured.js @@ -472,6 +472,93 @@ funarg_collapse_vars_3: { node_version: ">=6" } +funarg_collapse_vars_4: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var a = "PASS"; + (function(b, { log: c }) { + c(b); + })(a, console); + } + expect: { + var a = "PASS"; + (function(b, { log: c }) { + c(a); + })(0, console); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +funarg_collapse_vars_5: { + options = { + collapse_vars: true, + unused: true, + } + input: { + A = "FAIL"; + B = "PASS"; + try { + console.log(function({}, a) { + return a; + }(null, A = B)); + } catch (e) {} + console.log(A); + } + expect: { + A = "FAIL"; + B = "PASS"; + try { + console.log(function({}, a) { + return a; + }(null, A = B)); + } catch (e) {} + console.log(A); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +funarg_collapse_vars_6: { + options = { + collapse_vars: true, + unused: true, + } + input: { + A = "FAIL"; + B = "PASS"; + function f() { + console.log(function({}, a) { + return a; + }(null, A = B)); + } + try { + f(); + } catch (e) { + console.log(A); + } + } + expect: { + A = "FAIL"; + B = "PASS"; + function f() { + console.log(function({}, a) { + return a; + }(null, A = B)); + } + try { + f(); + } catch (e) { + console.log(A); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + funarg_reduce_vars_1: { options = { reduce_vars: true,