From f05d4f7af3659a481b06449584fa63e3772322e2 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Thu, 20 Apr 2017 13:06:14 +0800 Subject: [PATCH] improve `unused` (#1832) - extract leading value with side-effects out of `var` statement - reduce scanning of `AST_Definitions` from 3 passes to just once --- lib/compress.js | 166 +++++++++++++++------------------ test/compress/collapse_vars.js | 6 +- test/compress/drop-unused.js | 3 +- test/compress/reduce_vars.js | 12 +-- 4 files changed, 82 insertions(+), 105 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 7fa2b52b..772c6227 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -1955,12 +1955,7 @@ merge(Compressor.prototype, { sym.__unused = true; if (trim) { a.pop(); - compressor[sym.unreferenced() ? "warn" : "info"]("Dropping unused function argument {name} [{file}:{line},{col}]", { - name : sym.name, - file : sym.start.file, - line : sym.start.line, - col : sym.start.col - }); + compressor[sym.unreferenced() ? "warn" : "info"]("Dropping unused function argument {name} [{file}:{line},{col}]", template(sym)); } } else { @@ -1970,115 +1965,93 @@ merge(Compressor.prototype, { } if (drop_funcs && node instanceof AST_Defun && node !== self) { if (!(node.name.definition().id in in_use_ids)) { - compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", { - name : node.name.name, - file : node.name.start.file, - line : node.name.start.line, - col : node.name.start.col - }); + compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name)); return make_node(AST_EmptyStatement, node); } return node; } if (drop_vars && node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn)) { - var def = node.definitions.filter(function(def){ - var w = { - name : def.name.name, - file : def.name.start.file, - line : def.name.start.line, - col : def.name.start.col - }; + // place uninitialized names at the start + var body = [], head = [], tail = []; + // for unused names whose initialization has + // side effects, we can cascade the init. code + // into the next one, or next statement. + var side_effects = []; + node.definitions.forEach(function(def) { if (def.value) def.value = def.value.transform(tt); var sym = def.name.definition(); if (sym.id in in_use_ids) { if (def.name instanceof AST_SymbolVar) { var var_defs = var_defs_by_id.get(sym.id); if (var_defs.length > 1 && !def.value) { - compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", w); + compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name)); var_defs.splice(var_defs.indexOf(def), 1); - return false; + return; } } - return true; - } - if (sym.orig[0] instanceof AST_SymbolCatch) { - def.value = def.value && def.value.drop_side_effect_free(compressor); - return true; - } - if (def.value && (def._unused_side_effects = def.value.drop_side_effect_free(compressor))) { - compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w); - return true; + if (def.value) { + if (side_effects.length > 0) { + if (tail.length > 0) { + merge_sequence(side_effects, def.value); + def.value = make_sequence(def.value, side_effects); + } else { + body.push(make_node(AST_SimpleStatement, node, { + body: make_sequence(node, side_effects) + })); + } + side_effects = []; + } + tail.push(def); + } else { + head.push(def); + } + } else if (sym.orig[0] instanceof AST_SymbolCatch) { + var value = def.value && def.value.drop_side_effect_free(compressor); + if (value) merge_sequence(side_effects, value); + def.value = null; + head.push(def); + } else { + var value = def.value && def.value.drop_side_effect_free(compressor); + if (value) { + compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name)); + merge_sequence(side_effects, value); + } else { + compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name)); + } } - compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", w); - return false; }); - if (def.length == 1 - && def[0].value - && !def[0]._unused_side_effects - && def[0].name instanceof AST_SymbolVar) { - var var_defs = var_defs_by_id.get(def[0].name.definition().id); + if (head.length == 0 && tail.length == 1 && tail[0].name instanceof AST_SymbolVar) { + var var_defs = var_defs_by_id.get(tail[0].name.definition().id); if (var_defs.length > 1) { - compressor.warn("Converting duplicated definition of variable {name} to assignment [{file}:{line},{col}]", { - name : def[0].name.name, - file : def[0].name.start.file, - line : def[0].name.start.line, - col : def[0].name.start.col - }); - var_defs.splice(var_defs.indexOf(def[0]), 1); - return make_node(AST_SimpleStatement, node, { - body: make_node(AST_Assign, def[0], { - operator: "=", - left: make_node(AST_SymbolRef, def[0].name, def[0].name), - right: def[0].value - }) - }); + var def = tail.pop(); + compressor.warn("Converting duplicated definition of variable {name} to assignment [{file}:{line},{col}]", template(def.name)); + var_defs.splice(var_defs.indexOf(def), 1); + side_effects.unshift(make_node(AST_Assign, def, { + operator: "=", + left: make_node(AST_SymbolRef, def.name, def.name), + right: def.value + })); } } - // place uninitialized names at the start - def = mergeSort(def, function(a, b){ - if (!a.value && b.value) return -1; - if (!b.value && a.value) return 1; - return 0; - }); - // for unused names whose initialization has - // side effects, we can cascade the init. code - // into the next one, or next statement. - var side_effects = []; - for (var i = 0; i < def.length;) { - var x = def[i]; - if (x._unused_side_effects) { - merge_sequence(side_effects, x._unused_side_effects); - def.splice(i, 1); - } else { - if (side_effects.length > 0) { - merge_sequence(side_effects, x.value); - x.value = make_sequence(x.value, side_effects); - side_effects = []; - } - ++i; - } + if (head.length > 0 || tail.length > 0) { + node.definitions = head.concat(tail); + body.push(node); } if (side_effects.length > 0) { - side_effects = make_node(AST_BlockStatement, node, { - body: [ make_node(AST_SimpleStatement, node, { - body: make_sequence(node, side_effects) - }) ] - }); - } else { - side_effects = null; - } - if (def.length == 0 && !side_effects) { - return make_node(AST_EmptyStatement, node); - } - if (def.length == 0) { - return in_list ? MAP.splice(side_effects.body) : side_effects; + body.push(make_node(AST_SimpleStatement, node, { + body: make_sequence(node, side_effects) + })); } - node.definitions = def; - if (side_effects) { - side_effects.body.unshift(node); - return in_list ? MAP.splice(side_effects.body) : side_effects; + switch (body.length) { + case 0: + return in_list ? MAP.skip : make_node(AST_EmptyStatement, node); + case 1: + return body[0]; + default: + return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, { + body: body + }); } - return node; } if (drop_vars && assign_as_unused && node instanceof AST_Assign @@ -2119,6 +2092,15 @@ merge(Compressor.prototype, { } if (node instanceof AST_Scope && node !== self) return node; + + function template(sym) { + return { + name : sym.name, + file : sym.start.file, + line : sym.start.line, + col : sym.start.col + }; + } } ); self.transform(tt); diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js index 94515a6f..a4c1f9e6 100644 --- a/test/compress/collapse_vars.js +++ b/test/compress/collapse_vars.js @@ -701,10 +701,10 @@ collapse_vars_lvalues_drop_assign: { function f2(x) { var z = x, a = ++z; return z += a; } function f3(x) { var a = (x -= 3); return x + a; } function f4(x) { var a = (x -= 3); return x + a; } - function f5(x) { var v = (e1(), e2()), c = v = --x; return x - c; } + function f5(x) { e1(); var v = e2(), c = v = --x; return x - c; } function f6(x) { e1(), e2(); return --x - x; } - function f7(x) { var c = (e1(), e2() - x); return x - c; } - function f8(x) { var v = (e1(), e2()); return x - (v - x); } + function f7(x) { e1(); var c = e2() - x; return x - c; } + function f8(x) { e1(); var v = e2(); return x - (v - x); } function f9(x) { e1(); return e2() - x - x; } } } diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index 8f0aa0bf..7456f676 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -935,7 +935,8 @@ issue_1715_3: { try { console; } catch (a) { - var a = x(); + var a; + x(); } } f(); diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index c5f26904..57e23891 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -53,9 +53,7 @@ reduce_vars: { console.log(-3); eval("console.log(a);"); })(eval); - (function() { - return "yes"; - })(); + "yes"; console.log(2); } expect_stdout: true @@ -1699,9 +1697,7 @@ redefine_arguments_2: { console.log(function() { var arguments; return typeof arguments; - }(), function() { - return"number"; - }(), function(x) { + }(), "number", function(x) { var arguments = x; return typeof arguments; }()); @@ -1810,9 +1806,7 @@ redefine_farg_2: { console.log(function(a) { var a; return typeof a; - }([]), function() { - return "number"; - }(),function(a, b) { + }([]), "number",function(a, b) { var a = b; return typeof a; }([])); -- 2.34.1