diff --git a/lib/compress.js b/lib/compress.js index 3745b764..4235617d 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -6934,7 +6934,9 @@ Compressor.prototype.compress = function(node) { } var argnames = node.argnames; var rest = node.rest; + var after = false, before = false; if (rest) { + before = true; if (!args || spread < argnames.length || rest instanceof AST_SymbolFunarg) { rest = rest.transform(trimmer); } else { @@ -6947,8 +6949,7 @@ Compressor.prototype.compress = function(node) { args.length = argnames.length; if (trimmed.value.elements.length) [].push.apply(args, trimmed.value.elements); } - if (rest instanceof AST_Destructured && !rest.rest - && (!node.uses_arguments || tt.has_directive("use strict"))) { + if (rest instanceof AST_Destructured && !rest.rest) { if (rest instanceof AST_DestructuredArray) { if (rest.elements.length == 0) rest = null; } else if (rest.properties.length == 0) { @@ -6956,7 +6957,10 @@ Compressor.prototype.compress = function(node) { } } node.rest = rest; - if (rest) trim = false; + if (rest) { + trim = false; + after = true; + } } var default_length = trim ? -1 : node.length(); for (var i = argnames.length; --i >= 0;) { @@ -6973,29 +6977,43 @@ Compressor.prototype.compress = function(node) { sym.unused = true; } } else { + before = true; var funarg; if (!args || spread < i) { funarg = sym.transform(trimmer); } else { - funarg = trim_destructured(sym, args[i], function(node) { + var trimmed = trim_destructured(sym, args[i], function(node) { return node.definition().id in in_use_ids ? node : null; - }, !node.uses_arguments, sym).name; + }, !node.uses_arguments, sym); + funarg = trimmed.name; + if (trimmed.value) args[i] = trimmed.value; } if (funarg) { trim = false; + argnames[i] = funarg; + if (!after) after = !(funarg instanceof AST_SymbolFunarg); } else if (trim) { log_default(sym, "Dropping unused default argument {name}"); argnames.pop(); } else if (i > default_length) { log_default(sym, "Dropping unused default argument assignment {name}"); - if (sym.name instanceof AST_SymbolFunarg) sym.name.unused = true; + if (sym.name instanceof AST_SymbolFunarg) { + sym.name.unused = true; + } else { + after = true; + } argnames[i] = sym.name; } else { log_default(sym, "Dropping unused default argument value {name}"); + argnames[i] = sym = sym.clone(); sym.value = make_node(AST_Number, sym, { value: 0 }); + after = true; } } } + if (before && !after && node.uses_arguments && !tt.has_directive("use strict")) { + node.rest = make_node(AST_DestructuredArray, node, { elements: [] }); + } fns_with_marked_args.push(node); } } @@ -7555,6 +7573,7 @@ Compressor.prototype.compress = function(node) { var value = node.value.drop_side_effect_free(compressor); if (!value) return null; log(node.name, "Side effects in default value of unused variable {name}"); + node = node.clone(); node.name.unused = null; node.value = value; } @@ -7632,6 +7651,7 @@ Compressor.prototype.compress = function(node) { drop = save_drop; if (values && newValues) { fill_holes(value, newValues); + value = value.clone(); value.elements = newValues; } if (!node.rest && (value instanceof AST_Array @@ -7665,20 +7685,25 @@ Compressor.prototype.compress = function(node) { drop = false; value = value.fixed_value(); } - var prop_keys, prop_map; + var prop_keys, prop_map, values; if (value instanceof AST_Object) { prop_keys = []; prop_map = new Dictionary(); - value.properties.forEach(function(prop, index) { - if (prop instanceof AST_Spread) return prop_map = false; - var key = prop.key; - if (key instanceof AST_Node) key = key.evaluate(compressor, true); - if (key instanceof AST_Node) { + values = value.properties.map(function(prop, index) { + prop = prop.clone(); + if (prop instanceof AST_Spread) { prop_map = false; - } else if (prop_map && !(prop instanceof AST_ObjectSetter)) { - prop_map.set(key, prop); + } else { + var key = prop.key; + if (key instanceof AST_Node) key = key.evaluate(compressor, true); + if (key instanceof AST_Node) { + prop_map = false; + } else if (prop_map && !(prop instanceof AST_ObjectSetter)) { + prop_map.set(key, prop); + } + prop_keys[index] = key; } - prop_keys[index] = key; + return prop; }); } if (node.rest) { @@ -7743,27 +7768,30 @@ Compressor.prototype.compress = function(node) { }); value = save_value; drop = save_drop; - if (drop_keys && prop_keys) value.properties = List(value.properties, function(prop, index) { - if (prop instanceof AST_Spread) return prop; - var key = prop_keys[index]; - if (key instanceof AST_Node) return prop; - if (drop_keys.has(key)) { - var mapped = drop_keys.get(key); - if (!mapped) return prop; - if (mapped === prop) return prop_map.get(key) || List.skip; - } else if (node.rest) { - return prop; - } - var trimmed = prop.value.drop_side_effect_free(compressor); - if (trimmed) { - prop.value = trimmed; - return prop; - } - return retain_key(prop) ? make_node(AST_ObjectKeyVal, prop, { - key: prop.key, - value: make_node(AST_Number, prop, { value: 0 }), - }) : List.skip; - }); + if (drop_keys && prop_keys) { + value = value.clone(); + value.properties = List(values, function(prop, index) { + if (prop instanceof AST_Spread) return prop; + var key = prop_keys[index]; + if (key instanceof AST_Node) return prop; + if (drop_keys.has(key)) { + var mapped = drop_keys.get(key); + if (!mapped) return prop; + if (mapped === prop) return prop_map.get(key) || List.skip; + } else if (node.rest) { + return prop; + } + var trimmed = prop.value.drop_side_effect_free(compressor); + if (trimmed) { + prop.value = trimmed; + return prop; + } + return retain_key(prop) ? make_node(AST_ObjectKeyVal, prop, { + key: prop.key, + value: make_node(AST_Number, prop, { value: 0 }), + }) : List.skip; + }); + } if (value && !node.rest) switch (properties.length) { case 0: if (node === root) break; diff --git a/test/compress/default-values.js b/test/compress/default-values.js index 9878820c..f0859749 100644 --- a/test/compress/default-values.js +++ b/test/compress/default-values.js @@ -2313,3 +2313,85 @@ issue_5444_3: { expect_stdout: "PASS" node_version: ">=6" } + +issue_5448_1: { + options = { + reduce_vars: true, + unused: true, + } + input: { + (function(a = typeof console.log) { + do { + var b = [ ...a ]; + } while (console.log("PASS")); + })(); + } + expect: { + (function(a = console.log) { + do {} while (console.log("PASS")); + })(); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5448_2: { + options = { + keep_fargs: true, + reduce_vars: true, + unused: true, + } + input: { + (function(a = typeof console) { + do { + var b = [ ...a ]; + } while (console.log("PASS")); + })(); + } + expect: { + (function(a = 0) { + do {} while (console.log("PASS")); + })(); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5448_3: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var [ a = typeof console ] = [ void console.log("PASS") ]; + var b = [ ...a ]; + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5448_4: { + options = { + evaluate: true, + pure_getters: "strict", + reduce_vars: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var { p: a = typeof console } = { p: void console.log("PASS") }; + var b = [ ...a ]; + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} diff --git a/test/compress/destructured.js b/test/compress/destructured.js index 847ab87c..2ef7acee 100644 --- a/test/compress/destructured.js +++ b/test/compress/destructured.js @@ -3020,6 +3020,7 @@ issue_5074_method_pure_getters: { issue_5085_1: { options = { evaluate: true, + passes: 2, reduce_vars: true, toplevel: true, unsafe: true, @@ -3032,8 +3033,7 @@ issue_5085_1: { } expect: { var a = "PASS"; - var b = [ 42 ][0]; - b; + 42; console.log(a); } expect_stdout: "PASS" @@ -3043,6 +3043,7 @@ issue_5085_1: { issue_5085_2: { options = { evaluate: true, + passes: 2, reduce_vars: true, side_effects: true, unsafe: true, @@ -3059,7 +3060,7 @@ issue_5085_2: { expect: { var a = "PASS"; (function(b) { - b = [ 42 ][0]; + 0; })(); console.log(a); }