From c7a3e09407fbed928ccd874609c63c31b27f052a Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 26 Aug 2020 02:32:55 +0100 Subject: [PATCH] enhance `loops` & `unused` (#4074) - extend `ufuzz` generation of for-in loops --- lib/compress.js | 42 ++++++++++++++++++++++++++---------------- test/compress/loops.js | 32 +++++++++++++++++++++++++++++++- test/ufuzz/index.js | 15 ++++++++++----- 3 files changed, 67 insertions(+), 22 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 47445665..213cead3 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -4456,7 +4456,7 @@ merge(Compressor.prototype, { if (drop_funcs && node !== self && node instanceof AST_Defun) { var def = node.name.definition(); if (!(def.id in in_use_ids)) { - log(node.name, "Dropping unused function {name} [{file}:{line},{col}]", template(node.name)); + log(node.name, "Dropping unused function {name}"); def.eliminated++; return in_list ? List.skip : make_node(AST_EmptyStatement, node); } @@ -4471,7 +4471,7 @@ merge(Compressor.prototype, { if (!(sym.definition().id in in_use_ids)) { sym.__unused = true; if (trim) { - log(sym, "Dropping unused function argument {name} [{file}:{line},{col}]", template(sym)); + log(sym, "Dropping unused function argument {name}"); a.pop(); } } else { @@ -4574,7 +4574,7 @@ merge(Compressor.prototype, { AST_Node.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name)); side_effects.push(value); } else { - log(def.name, "Dropping unused variable {name} [{file}:{line},{col}]", template(def.name)); + log(def.name, "Dropping unused variable {name}"); } sym.eliminated++; } @@ -4669,19 +4669,22 @@ merge(Compressor.prototype, { return !block ? node : in_list ? List.splice(block.body) : block; } else if (node instanceof AST_ForIn) { if (!drop_vars || !compressor.option("loops")) return; - if (!(node.init instanceof AST_Definitions)) return; - var sym = node.init.definitions[0].name; - if (sym.definition().id in in_use_ids) return; if (!is_empty(node.body)) return; - log(sym, "Dropping unused loop variable {name} [{file}:{line},{col}]", template(sym)); - var value = node.object.drop_side_effect_free(compressor); - if (value) { - AST_Node.warn("Side effects in object of for-in loop [{file}:{line},{col}]", template(sym)); - return make_node(AST_SimpleStatement, node, { - body: value - }); + var sym = node.init; + if (sym instanceof AST_Definitions) { + sym = sym.definitions[0].name; + } else while (sym instanceof AST_PropAccess) { + sym = sym.expression.tail_node(); } - return in_list ? List.skip : make_node(AST_EmptyStatement, node); + var def = sym.definition(); + if (!def || def.id in in_use_ids) return; + log(sym, "Dropping unused loop variable {name}"); + var value = node.object.drop_side_effect_free(compressor); + if (!value) return in_list ? List.skip : make_node(AST_EmptyStatement, node); + AST_Node.warn("Side effects in object of for-in loop [{file}:{line},{col}]", value.start); + return make_node(AST_SimpleStatement, node, { + body: value + }); } else if (node instanceof AST_Sequence) { if (node.expressions.length == 1) return node.expressions[0]; } @@ -4701,8 +4704,8 @@ merge(Compressor.prototype, { drop_unused_call_args(call, compressor, fns_with_marked_args); }); - function log(sym, text, props) { - AST_Node[sym.definition().references.length > 0 ? "info" : "warn"](text, props); + function log(sym, text) { + AST_Node[sym.definition().references.length > 0 ? "info" : "warn"](text + " [{file}:{line},{col}]", template(sym)); } function template(sym) { @@ -4790,6 +4793,13 @@ merge(Compressor.prototype, { if (track_assigns(node_def, sym) && is_lhs(sym, node) !== sym) add_assigns(node_def, sym); return true; } + if (node instanceof AST_ForIn) { + if (!compressor.option("loops")) return; + if (!is_empty(node.body)) return; + if (node.init.has_side_effects(compressor)) return; + node.object.walk(tw); + return true; + } if (node instanceof AST_SymbolRef) { node_def = node.definition(); if (!(node_def.id in in_use_ids)) { diff --git a/test/compress/loops.js b/test/compress/loops.js index aef1b90b..4a276b9e 100644 --- a/test/compress/loops.js +++ b/test/compress/loops.js @@ -756,7 +756,37 @@ empty_for_in_side_effects: { expect_warnings: [ "WARN: Dropping unused variable b [test/compress/loops.js:4,16]", "INFO: Dropping unused loop variable a [test/compress/loops.js:1,17]", - "WARN: Side effects in object of for-in loop [test/compress/loops.js:1,17]", + "WARN: Side effects in object of for-in loop [test/compress/loops.js:2,17]", + ] +} + +empty_for_in_prop_init: { + options = { + loops: true, + pure_getters: "strict", + unused: true, + } + input: { + console.log(function f() { + var a = "bar"; + for ((a, f)[a] in console.log("foo")); + return a; + }()); + } + expect: { + console.log(function() { + var a = "bar"; + console.log("foo"); + return a; + }()); + } + expect_stdout: [ + "foo", + "bar", + ] + expect_warnings: [ + "INFO: Dropping unused loop variable f [test/compress/loops.js:3,21]", + "WARN: Side effects in object of for-in loop [test/compress/loops.js:3,30]", ] } diff --git a/test/ufuzz/index.js b/test/ufuzz/index.js index 9e8ca8dc..60f86667 100644 --- a/test/ufuzz/index.js +++ b/test/ufuzz/index.js @@ -496,11 +496,16 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn var label = createLabel(canBreak, canContinue); canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK); canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE); - var optElementVar = ""; - if (rng(5) > 1) { - optElementVar = "c = 1 + c; var " + createVarName(MANDATORY) + " = expr" + loop + "[key" + loop + "]; "; - } - return "{var expr" + loop + " = " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "; " + label.target + " for (var key" + loop + " in expr" + loop + ") {" + optElementVar + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "}}"; + var key = rng(10) ? "key" + loop : getVarName(); + return [ + "{var expr" + loop + " = " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "; ", + label.target + " for (", + /^key/.test(key) ? "var " : "", + key + " in expr" + loop + ") {", + rng(5) > 1 ? "c = 1 + c; var " + createVarName(MANDATORY) + " = expr" + loop + "[" + key + "]; " : "", + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth), + "}}", + ].join(""); case STMT_SEMI: return use_strict && rng(20) === 0 ? '"use strict";' : ";"; case STMT_EXPR: -- 2.34.1