From 83a42716c37eb644c490454d7f2119b1fe8158b1 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 12 Feb 2020 23:46:16 +0000 Subject: [PATCH] fix corner case in `unused` (#3716) --- lib/compress.js | 123 +++++++++++++++++++---------------- test/compress/drop-unused.js | 52 +++++++++++++-- test/compress/functions.js | 2 +- test/compress/keep_fargs.js | 8 +-- 4 files changed, 116 insertions(+), 69 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 55e40ef2f0..8a33b4742f 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -4001,6 +4001,8 @@ merge(Compressor.prototype, { }; // pass 3: we should drop declarations not in_use var unused_fn_names = []; + var calls_to_drop_args = []; + var fns_with_marked_args = []; var tt = new TreeTransformer(function(node, descend, in_list) { var parent = tt.parent(); if (drop_vars) { @@ -4041,6 +4043,7 @@ merge(Compressor.prototype, { } } } + if (node instanceof AST_Call) calls_to_drop_args.push(node); if (scope !== self) return; if (node instanceof AST_Function && node.name && drop_fn_name(node.name.definition())) { unused_fn_names.push(node); @@ -4059,6 +4062,7 @@ merge(Compressor.prototype, { trim = false; } } + fns_with_marked_args.push(node); } if (drop_funcs && node instanceof AST_Defun && node !== self) { var def = node.name.definition(); @@ -4252,6 +4256,9 @@ merge(Compressor.prototype, { unused_fn_names.forEach(function(fn) { fn.name = null; }); + calls_to_drop_args.forEach(function(call) { + drop_unused_call_args(call, compressor, fns_with_marked_args); + }); function log(sym, text, props) { AST_Node[sym.unreferenced() ? "warn" : "info"](text, props); @@ -5448,6 +5455,62 @@ merge(Compressor.prototype, { return make_sequence(node, x); } + function drop_unused_call_args(call, compressor, fns_with_marked_args) { + var exp = call.expression; + var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp; + if (!(fn instanceof AST_Lambda)) return; + if (fn.uses_arguments) return; + if (fn.pinned()) return; + if (fns_with_marked_args && fns_with_marked_args.indexOf(fn) < 0) return; + var args = call.args; + var pos = 0, last = 0; + var drop_fargs = fn === exp && !fn.name && compressor.drop_fargs(fn, call); + var side_effects = []; + for (var i = 0; i < args.length; i++) { + var trim = i >= fn.argnames.length; + if (trim || fn.argnames[i].__unused) { + var node = args[i].drop_side_effect_free(compressor); + if (drop_fargs) { + fn.argnames.splice(i, 1); + args.splice(i, 1); + if (node) side_effects.push(node); + i--; + continue; + } else if (node) { + side_effects.push(node); + args[pos++] = make_sequence(call, side_effects); + side_effects = []; + } else if (!trim) { + if (side_effects.length) { + node = make_sequence(call, side_effects); + side_effects = []; + } else { + node = make_node(AST_Number, args[i], { + value: 0 + }); + } + args[pos++] = node; + continue; + } + } else { + side_effects.push(args[i]); + args[pos++] = make_sequence(call, side_effects); + side_effects = []; + } + last = pos; + } + if (drop_fargs) for (; i < fn.argnames.length; i++) { + if (fn.argnames[i].__unused) fn.argnames.splice(i--, 1); + } + args.length = last; + if (!side_effects.length) return; + var arg = make_sequence(call, side_effects); + args.push(args.length < fn.argnames.length ? make_node(AST_UnaryPrefix, call, { + operator: "void", + expression: arg + }) : arg); + } + OPT(AST_Call, function(self, compressor) { var exp = self.expression; if (compressor.option("sequences")) { @@ -5464,63 +5527,7 @@ merge(Compressor.prototype, { if (seq !== self) return seq.optimize(compressor); } } - var fn = exp; - if (compressor.option("reduce_vars") && fn instanceof AST_SymbolRef) { - fn = fn.fixed_value(); - } - var is_func = fn instanceof AST_Lambda; - if (compressor.option("unused") - && is_func - && !fn.uses_arguments - && !fn.pinned()) { - var pos = 0, last = 0; - var drop_fargs = exp === fn && !fn.name && compressor.drop_fargs(fn, self); - var side_effects = []; - for (var i = 0; i < self.args.length; i++) { - var trim = i >= fn.argnames.length; - if (trim || fn.argnames[i].__unused) { - var node = self.args[i].drop_side_effect_free(compressor); - if (drop_fargs) { - fn.argnames.splice(i, 1); - self.args.splice(i, 1); - if (node) side_effects.push(node); - i--; - continue; - } else if (node) { - side_effects.push(node); - self.args[pos++] = make_sequence(self, side_effects); - side_effects = []; - } else if (!trim) { - if (side_effects.length) { - node = make_sequence(self, side_effects); - side_effects = []; - } else { - node = make_node(AST_Number, self.args[i], { - value: 0 - }); - } - self.args[pos++] = node; - continue; - } - } else { - side_effects.push(self.args[i]); - self.args[pos++] = make_sequence(self, side_effects); - side_effects = []; - } - last = pos; - } - if (drop_fargs) for (; i < fn.argnames.length; i++) { - if (fn.argnames[i].__unused) fn.argnames.splice(i--, 1); - } - self.args.length = last; - if (side_effects.length) { - var arg = make_sequence(self, side_effects); - self.args.push(self.args.length < fn.argnames.length ? make_node(AST_UnaryPrefix, self, { - operator: "void", - expression: arg - }) : arg); - } - } + if (compressor.option("unused")) drop_unused_call_args(self, compressor); if (compressor.option("unsafe")) { if (is_undeclared_ref(exp)) switch (exp.name) { case "Array": @@ -5786,6 +5793,8 @@ merge(Compressor.prototype, { } } } + var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp; + var is_func = fn instanceof AST_Lambda; var stat = is_func && fn.first_statement(); var can_inline = compressor.option("inline") && !self.is_expr_pure(compressor); if (exp === fn && can_inline && stat instanceof AST_Return) { diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index 0691a1800a..2d2f3d435f 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -1191,10 +1191,10 @@ issue_2105_1: { input: { !function(factory) { factory(); - }( function() { + }(function() { return function(fn) { fn()().prop(); - }( function() { + }(function() { function bar() { var quux = function() { console.log("PASS"); @@ -1205,7 +1205,7 @@ issue_2105_1: { return { prop: foo }; } return bar; - } ); + }); }); } expect: { @@ -1235,10 +1235,10 @@ issue_2105_2: { input: { !function(factory) { factory(); - }( function() { + }(function() { return function(fn) { fn()().prop(); - }( function() { + }(function() { function bar() { var quux = function() { console.log("PASS"); @@ -1249,7 +1249,7 @@ issue_2105_2: { return { prop: foo }; } return bar; - } ); + }); }); } expect: { @@ -1258,6 +1258,44 @@ issue_2105_2: { expect_stdout: "PASS" } +issue_2105_3: { + options = { + inline: true, + passes: 2, + reduce_vars: true, + unused: true, + } + input: { + !function(factory) { + factory(); + }(function() { + return function(fn) { + fn()().prop(); + }(function() { + function bar() { + var quux = function() { + console.log("PASS"); + }, foo = function() { + console.log; + quux(); + }; + return { prop: foo }; + } + return bar; + }); + }); + } + expect: { + !void void { + prop: function() { + console.log; + void console.log("PASS"); + } + }.prop(); + } + expect_stdout: "PASS" +} + issue_2226_1: { options = { side_effects: true, @@ -2330,7 +2368,7 @@ function_parameter_ie8: { (function() { (function f() { console.log("PASS"); - })(0); + })(); })(); } expect_stdout: "PASS" diff --git a/test/compress/functions.js b/test/compress/functions.js index ef51af7255..0cf0179219 100644 --- a/test/compress/functions.js +++ b/test/compress/functions.js @@ -1276,7 +1276,7 @@ issue_2630_3: { (function() { (function f1(a) { f2(); - --x >= 0 && f1({}); + --x >= 0 && f1(); })(a++); function f2() { a++; diff --git a/test/compress/keep_fargs.js b/test/compress/keep_fargs.js index a17a8f3f34..85fcb0bfc2 100644 --- a/test/compress/keep_fargs.js +++ b/test/compress/keep_fargs.js @@ -728,7 +728,7 @@ issue_2630_3: { (function() { (function f1() { f2(); - --x >= 0 && f1({}); + --x >= 0 && f1(); })(a++); function f2() { a++; @@ -1369,7 +1369,7 @@ recursive_iife_1: { } expect: { console.log(function f(a, b) { - return b || f("FAIL", "PASS"); + return b || f(0, "PASS"); }()); } expect_stdout: "PASS" @@ -1388,7 +1388,7 @@ recursive_iife_2: { } expect: { console.log(function f(a, b) { - return b || f("FAIL", "PASS"); + return b || f(0, "PASS"); }(0, 0)); } expect_stdout: "PASS" @@ -1416,7 +1416,7 @@ recursive_iife_3: { var a = 1, c = "PASS"; (function() { (function f(b, d, e) { - a-- && f(null, 42, 0); + a-- && f(0, 42, 0); e && (c = "FAIL"); d && d.p; })();