diff --git a/lib/compress.js b/lib/compress.js index 7a4c53eb..c54a8f3a 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -784,8 +784,11 @@ Compressor.prototype.compress = function(node) { if (save) fixed = compressor.option("rests") && function() { var value = save(); if (!(value instanceof AST_Array)) return node; + for (var i = 0, len = node.elements.length; i < len; i++) { + if (value.elements[i] instanceof AST_Spread) return node; + } if (!fixed_node) fixed_node = make_node(AST_Array, node); - fixed_node.elements = value.elements.slice(node.elements.length); + fixed_node.elements = value.elements.slice(len); return fixed_node; }; node.rest.walk(scanner); @@ -6662,8 +6665,28 @@ Compressor.prototype.compress = function(node) { unused_fn_names.push(node); } if (!(node instanceof AST_Accessor)) { - if (node.rest) { - var rest = node.rest.transform(trimmer); + var args, spread; + if (parent instanceof AST_Call && parent.expression === node) { + args = parent.args; + for (spread = 0; spread < args.length; spread++) { + if (args[spread] instanceof AST_Spread) break; + } + } + var argnames = node.argnames; + var rest = node.rest; + if (rest) { + if (!args || spread < argnames.length || rest instanceof AST_SymbolFunarg) { + rest = rest.transform(trimmer); + } else { + var trimmed = trim_destructured(rest, make_node(AST_Array, parent, { + elements: args.slice(argnames.length), + }), function(node) { + return node.definition().id in in_use_ids ? node : null; + }, !node.uses_arguments, rest); + rest = trimmed.name; + 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_DestructuredArray) { @@ -6674,37 +6697,43 @@ Compressor.prototype.compress = function(node) { } node.rest = rest; } - var argnames = node.argnames; var trim = compressor.drop_fargs(node, parent) && !node.rest; var default_length = trim ? -1 : node.length(); for (var i = argnames.length; --i >= 0;) { var sym = argnames[i]; - if (!(sym instanceof AST_SymbolFunarg)) { - var arg = sym.transform(trimmer); - if (arg) { + if (sym instanceof AST_SymbolFunarg) { + var def = sym.definition(); + if (def.id in in_use_ids) { + trim = false; + if (indexOf_assign(def, sym) < 0) sym.unused = null; + } else if (trim) { + log(sym, "Dropping unused function argument {name}"); + argnames.pop(); + } else { + sym.unused = true; + } + } else { + var funarg; + if (!args || spread < i) { + funarg = sym.transform(trimmer); + } else { + funarg = trim_destructured(sym, args[i], function(node) { + return node.definition().id in in_use_ids ? node : null; + }, !node.uses_arguments, sym).name; + } + if (funarg) { trim = false; } else if (trim) { - log(sym.name, "Dropping unused default argument {name}"); + log_default(sym, "Dropping unused default argument {name}"); argnames.pop(); } else if (i > default_length) { - log(sym.name, "Dropping unused default argument assignment {name}"); - sym.name.unused = true; + log_default(sym, "Dropping unused default argument assignment {name}"); + if (sym.name instanceof AST_SymbolFunarg) sym.name.unused = true; argnames[i] = sym.name; } else { - log(sym.name, "Dropping unused default argument value {name}"); + log_default(sym, "Dropping unused default argument value {name}"); sym.value = make_node(AST_Number, sym, { value: 0 }); } - continue; - } - var def = sym.definition(); - if (def.id in in_use_ids) { - trim = false; - if (indexOf_assign(def, sym) < 0) sym.unused = null; - } else if (trim) { - log(sym, "Dropping unused function argument {name}"); - argnames.pop(); - } else { - sym.unused = true; } } fns_with_marked_args.push(node); @@ -7049,6 +7078,19 @@ Compressor.prototype.compress = function(node) { AST_Node[sym.definition().references.length > 0 ? "info" : "warn"](text + " [{file}:{line},{col}]", template(sym)); } + function log_default(node, text) { + if (node.name instanceof AST_SymbolFunarg) { + log(node.name, text); + } else { + AST_Node.info(text + " [{file}:{line},{col}]", { + name: node.print_to_string(), + file: node.start.file, + line: node.start.line, + col : node.start.col, + }); + } + } + function template(sym) { return { name: sym.name, @@ -7245,18 +7287,20 @@ Compressor.prototype.compress = function(node) { return node; } - function trim_destructured(node, value, process, drop) { + function trim_destructured(node, value, process, drop, root) { var trimmer = new TreeTransformer(function(node) { if (node instanceof AST_DefaultValue) { - if (compressor.option("default_values") && value && value.is_defined(compressor)) { - node = node.name; - } else { + if (!(compressor.option("default_values") && value && value.is_defined(compressor))) { var save_drop = drop; drop = false; var trimmed = trim_default(trimmer, node); drop = save_drop; if (!trimmed && drop && value) value = value.drop_side_effect_free(compressor); return trimmed; + } else if (node === root) { + root = node = node.name; + } else { + node = node.name; } } if (node instanceof AST_DestructuredArray) { @@ -7319,10 +7363,12 @@ Compressor.prototype.compress = function(node) { if (!node.rest && (value instanceof AST_Array || value && value.is_string(compressor))) switch (elements.length) { case 0: + if (node === root) break; if (drop) value = value.drop_side_effect_free(compressor); return null; case 1: if (!drop) break; + if (node === root) break; var sym = elements[0]; if (sym.has_side_effects(compressor)) break; if (value.has_side_effects(compressor) && sym.match_symbol(function(node) { @@ -7446,11 +7492,13 @@ Compressor.prototype.compress = function(node) { }); if (value && !node.rest) switch (properties.length) { case 0: + if (node === root) break; if (value.may_throw_on_access(compressor, true)) break; if (drop) value = value.drop_side_effect_free(compressor); return null; case 1: if (!drop) break; + if (node === root) break; var prop = properties[0]; if (prop.key instanceof AST_Node) break; if (prop.value.has_side_effects(compressor)) break; diff --git a/test/compress/default-values.js b/test/compress/default-values.js index f43374b7..53ebb194 100644 --- a/test/compress/default-values.js +++ b/test/compress/default-values.js @@ -2038,3 +2038,59 @@ issue_5222: { expect_stdout: "PASS" node_version: ">=6" } + +issue_5246_1: { + options = { + pure_getters: true, + unused: true, + } + input: { + console.log(function({} = 42) { + return "PASS"; + }("foo")); + } + expect: { + console.log(function({} = 0) { + return "PASS"; + }("foo")); + } + expect_stdout: "PASS" + expect_warnings: [ + "INFO: Dropping unused default argument value {}=42 [test/compress/default-values.js:1,29]", + "INFO: Dropping unused default argument value {}=0 [test/compress/default-values.js:1,29]", + ] + node_version: ">=6" +} + +issue_5246_2: { + options = { + unused: true, + } + input: { + (function f(a = "FAIL", [] = 42) { + console.log(a); + })("PASS", []); + } + expect: { + (function(a = "FAIL", []) { + console.log(a); + })("PASS", []); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5246_3: { + options = { + default_values: true, + unused: true, + } + input: { + console.log(function f([ , {} ] = null){}([ , {} ])); + } + expect: { + console.log(function([ {} ]){}([ {} ])); + } + expect_stdout: "undefined" + node_version: ">=6" +} diff --git a/test/compress/rests.js b/test/compress/rests.js index ce27b6eb..33584216 100644 --- a/test/compress/rests.js +++ b/test/compress/rests.js @@ -359,7 +359,7 @@ retain_funarg_destructured_object_2: { expect: { console.log(function({ p: a, ... b }) { return b; - }({ p: "FAIL" }).p || "PASS"); + }({}).p || "PASS"); } expect_stdout: "PASS" node_version: ">=8.3.0" @@ -1104,7 +1104,7 @@ issue_5108: { expect: { console.log(function([]) { return "PASS"; - }([ "PASS", "FAIL" ])); + }([])); } expect_stdout: "PASS" node_version: ">=6" @@ -1205,3 +1205,62 @@ issue_5165_2: { expect_stdout: "PASS" node_version: ">=6" } + +issue_5246_1: { + options = { + reduce_vars: true, + rests: true, + unused: true, + } + input: { + console.log(typeof function([ , ...a ]) { + return this && a; + }([ , function(){} ])[0]); + } + expect: { + console.log(typeof function([]) { + return this && [ function(){} ]; + }([])[0]); + } + expect_stdout: "function" + node_version: ">=6" +} + +issue_5246_2: { + options = { + reduce_vars: true, + rests: true, + toplevel: true, + unused: true, + } + input: { + A = [ , "PASS", "FAIL" ]; + var [ , ...a ] = [ ... A ]; + console.log(a[0]); + } + expect: { + A = [ , "PASS", "FAIL" ]; + var [ , ...a ] = [ ... A ]; + console.log(a[0]); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5246_3: { + options = { + unused: true, + } + input: { + (function f(...[ [ a ] ]) { + console.log(a); + })([ "PASS" ]); + } + expect: { + (function(...[ a ]) { + console.log(a); + })([ "PASS" ][0]); + } + expect_stdout: "PASS" + node_version: ">=6" +} diff --git a/test/compress/yields.js b/test/compress/yields.js index 6e40f3e0..0b90a5f7 100644 --- a/test/compress/yields.js +++ b/test/compress/yields.js @@ -1371,12 +1371,12 @@ issue_5034: { node_version: ">=4" } -issue_5076: { +issue_5076_1: { options = { evaluate: true, hoist_vars: true, - passes: 2, pure_getters: "strict", + sequences: true, side_effects: true, toplevel: true, unused: true, @@ -1393,13 +1393,41 @@ issue_5076: { } expect: { var a; - console.log("PASS"); + console.log("PASS"), a = 42["a"]; } expect_stdout: "PASS" node_version: ">=6" } +issue_5076_2: { + options = { + evaluate: true, + hoist_vars: true, + passes: 2, + pure_getters: "strict", + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + yields: true, + } + input: { + var a; + console.log("PASS"); + var b = function*({ + p: {}, + }) {}({ + p: { a } = 42, + }); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + issue_5177: { options = { properties: true,