From 65d39a3702643a8dc6a3b797a15470e6715d6f84 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Thu, 14 Jan 2021 16:22:34 +0000 Subject: [PATCH] fix corner cases in `collapse_vars` (#4555) fixes #4554 --- lib/compress.js | 49 ++++++++++++++++++++++++++++------- test/compress/destructured.js | 31 ++++++++++++++++++++++ 2 files changed, 71 insertions(+), 9 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index f0ed6773..fadd7415 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -1715,7 +1715,7 @@ merge(Compressor.prototype, { var funarg = candidate.name instanceof AST_SymbolFunarg; var may_throw = return_false; if (candidate.may_throw(compressor)) { - if (funarg && scope instanceof AST_AsyncFunction) continue; + if (funarg && is_async(scope)) continue; may_throw = in_try ? function(node) { return node.has_side_effects(compressor); } : side_effects_external; @@ -1727,6 +1727,9 @@ merge(Compressor.prototype, { var lhs_local = is_lhs_local(lhs); var rvalue = get_rvalue(candidate); if (!side_effects) side_effects = value_has_side_effects(); + var check_destructured = in_try || !lhs_local ? function(node) { + return node instanceof AST_Destructured; + } : return_false; var replace_all = replace_all_symbols(candidate); var hit = funarg; var abort = false; @@ -1833,7 +1836,6 @@ merge(Compressor.prototype, { } if (node instanceof AST_Debugger) return true; if (node instanceof AST_Defun) return funarg && lhs.name === node.name.name; - if (node instanceof AST_Destructured) return (in_try || !lhs_local) && parent instanceof AST_Assign; if (node instanceof AST_DestructuredKeyVal) return node.key instanceof AST_Node; if (node instanceof AST_DWLoop) return true; if (node instanceof AST_LoopControl) return true; @@ -1931,7 +1933,7 @@ merge(Compressor.prototype, { } if (node instanceof AST_This) return symbol_in_lvalues(node, parent); if (node instanceof AST_VarDef) { - if ((in_try || !lhs_local) && node.name instanceof AST_Destructured) return true; + if (check_destructured(node.name)) return true; return (node.value || parent instanceof AST_Let) && node.name.match_symbol(function(node) { return node instanceof AST_SymbolDeclaration && (lvalues.has(node.name) || side_effects && may_modify(node)); @@ -1940,13 +1942,38 @@ merge(Compressor.prototype, { var sym = is_lhs(node.left, node); if (!sym) return false; if (sym instanceof AST_PropAccess) return true; - if ((in_try || !lhs_local) && sym instanceof AST_Destructured) return true; + if (check_destructured(sym)) return true; return sym.match_symbol(function(node) { return node instanceof AST_SymbolRef && (lvalues.has(node.name) || read_toplevel && compressor.exposed(node.definition())); }, true); } + function may_throw_destructured(node, value) { + if (!value) return !(node instanceof AST_Symbol); + if (node instanceof AST_DefaultValue) { + return value.has_side_effects(compressor) + || node.value.has_side_effects(compressor) + || may_throw_destructured(node.name, is_undefined(value) && node.value); + } + if (node instanceof AST_Destructured) { + if (node.rest && may_throw_destructured(node.rest)) return true; + if (node instanceof AST_DestructuredArray) { + if (!(value instanceof AST_Array || value.is_string(compressor))) return true; + return !all(node.elements, function(element) { + return !may_throw_destructured(element); + }); + } + if (node instanceof AST_DestructuredObject) { + if (!value.is_defined(compressor)) return true; + return !all(node.properties, function(prop) { + if (prop instanceof AST_Node && prop.has_side_effects(compressor)) return false; + return !may_throw_destructured(prop.value); + }); + } + } + } + function extract_args() { var iife, fn = compressor.self(); if (is_function(fn) @@ -1960,7 +1987,7 @@ merge(Compressor.prototype, { })) { var fn_strict = compressor.has_directive("use strict"); if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false; - var has_await = fn instanceof AST_AsyncFunction ? function(node) { + 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); @@ -1984,18 +2011,19 @@ merge(Compressor.prototype, { } }); args = iife.args.slice(); + var len = args.length; var names = Object.create(null); for (var i = fn.argnames.length; --i >= 0;) { var sym = fn.argnames[i]; - var arg = iife.args[i]; + var arg = args[i]; var value; if (sym instanceof AST_DefaultValue) { value = sym.value; sym = sym.name; - args[iife.args.length + i] = value; + args[len + i] = value; } if (sym instanceof AST_Destructured) { - if (!sym.match_symbol(return_false)) continue; + if (!may_throw_destructured(sym, arg)) continue; candidates.length = 0; break; } @@ -2015,7 +2043,7 @@ merge(Compressor.prototype, { value: arg }); candidate.name_index = i; - candidate.arg_index = value ? iife.args.length + i : i; + candidate.arg_index = value ? len + i : i; candidates.unshift([ candidate ]); } } @@ -2273,7 +2301,10 @@ merge(Compressor.prototype, { can_replace = false; var after = stop_after; var if_hit = stop_if_hit; + var stack = scanner.stack; + scanner.stack = [ parent ]; lhs.transform(scanner); + scanner.stack = stack; stop_if_hit = if_hit; stop_after = after; can_replace = replace; diff --git a/test/compress/destructured.js b/test/compress/destructured.js index 029b4427..7cc6f46d 100644 --- a/test/compress/destructured.js +++ b/test/compress/destructured.js @@ -2456,3 +2456,34 @@ issue_4519_2: { expect_stdout: "PASS" node_version: ">=6" } + +issue_4554: { + options = { + collapse_vars: true, + unused: true, + } + input: { + A = "PASS"; + var a = "FAIL"; + try { + (function({}, b) { + return b; + })(void 0, a = A); + } catch (e) { + console.log(a); + } + } + expect: { + A = "PASS"; + var a = "FAIL"; + try { + (function({}, b) { + return b; + })(void 0, a = A); + } catch (e) { + console.log(a); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} -- 2.34.1