From b6f250f5c96be2143df0091a967e84c3af5b45ba Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sun, 12 Jun 2022 14:24:42 +0100 Subject: [PATCH] enhance `unused` (#5511) --- lib/compress.js | 58 ++++++++++++++++++++----- test/compress/classes.js | 82 ++++++++++++++++++++++++++++++++++++ test/compress/drop-unused.js | 70 ++++++++++++++++++++++++++++++ test/compress/exports.js | 40 ++++++++++++++++++ 4 files changed, 239 insertions(+), 11 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index fbb6aa2a7dd..2494d1d55f9 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -6634,6 +6634,7 @@ Compressor.prototype.compress = function(node) { var for_ins = Object.create(null); var in_use = []; var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use + var lambda_ids = Object.create(null); var value_read = Object.create(null); var value_modified = Object.create(null); var var_defs = Object.create(null); @@ -6669,9 +6670,13 @@ Compressor.prototype.compress = function(node) { in_use_ids[def.id] = true; in_use.push(def); } - if (node.extends) node.extends.walk(tw); var used = tw.parent() instanceof AST_ExportDefault; - if (used) export_defaults[def.id] = true; + if (used) { + export_defaults[def.id] = true; + } else if (drop && !(def.id in lambda_ids)) { + lambda_ids[def.id] = 1; + } + if (node.extends) node.extends.walk(tw); var values = []; node.properties.forEach(function(prop) { if (prop.key instanceof AST_Node) prop.key.walk(tw); @@ -6691,16 +6696,18 @@ Compressor.prototype.compress = function(node) { } if (node instanceof AST_LambdaDefinition) { var def = node.name.definition(); - if ((!drop_funcs || def.exported) && !(def.id in in_use_ids)) { + var drop = drop_funcs && !def.exported; + if (!drop && !(def.id in in_use_ids)) { in_use_ids[def.id] = true; in_use.push(def); } initializations.add(def.id, node); if (tw.parent() instanceof AST_ExportDefault) { export_defaults[def.id] = true; - } else { - return true; + return; } + if (drop && !(def.id in lambda_ids)) lambda_ids[def.id] = 1; + return true; } if (node instanceof AST_Definitions) { node.definitions.forEach(function(defn) { @@ -6730,6 +6737,7 @@ Compressor.prototype.compress = function(node) { } assignments.add(def.id, defn); } + unmark_lambda(def); return true; }, tw); if (side_effects) value.walk(tw); @@ -6921,6 +6929,14 @@ Compressor.prototype.compress = function(node) { }); } } + if (node instanceof AST_Binary && node.operator == "instanceof") { + var sym = node.right; + if (!(sym instanceof AST_SymbolRef)) return; + if (sym.definition().id in in_use_ids) return; + var lhs = node.left.drop_side_effect_free(compressor); + var value = make_node(AST_False, node).optimize(compressor); + return lhs ? make_sequence(node, [ lhs, value ]) : value; + } if (node instanceof AST_Call) { calls_to_drop_args.push(node); node.args = node.args.map(function(arg) { @@ -7463,6 +7479,14 @@ Compressor.prototype.compress = function(node) { return nodes && nodes.indexOf(node); } + function unmark_lambda(def) { + if (lambda_ids[def.id] > 1 && !(def.id in in_use_ids)) { + in_use_ids[def.id] = true; + in_use.push(def); + } + lambda_ids[def.id] = 0; + } + function verify_safe_usage(def, read, modified) { if (def.id in in_use_ids) return; if (read && modified) { @@ -7514,17 +7538,18 @@ Compressor.prototype.compress = function(node) { var def = node.expression.definition(); if (def.scope.resolve() === self) assignments.add(def.id, node); } - var node_def, props = [], sym = assign_as_unused(node, props); - if (sym && ((node_def = sym.definition()).scope.resolve() === self - || self.variables.get(sym.name) === node_def) - && !(is_arguments(node_def) && !all(self.argnames, function(argname) { + var props = [], sym = assign_as_unused(node, props); + if (sym) { + var node_def = sym.definition(); + if (node_def.scope.resolve() !== self && self.variables.get(sym.name) !== node_def) return; + if (is_arguments(node_def) && !all(self.argnames, function(argname) { return !argname.match_symbol(function(node) { if (node instanceof AST_SymbolFunarg) { var def = node.definition(); return def.references.length > def.replaced; } }, true); - }))) { + })) return; if (node.write_only === "p" && node.right.may_throw_on_access(compressor, true)) return; var assign = props.assign; if (assign) { @@ -7554,6 +7579,17 @@ Compressor.prototype.compress = function(node) { } } if (track_assigns(node_def, sym) && is_lhs(sym, node) !== sym) add_assigns(node_def, sym); + unmark_lambda(node_def); + return true; + } + if (node instanceof AST_Binary) { + if (node.operator != "instanceof") return; + var sym = node.right; + if (!(sym instanceof AST_SymbolRef)) return; + var id = sym.definition().id; + if (!lambda_ids[id]) return; + node.left.walk(tw); + lambda_ids[id]++; return true; } if (node instanceof AST_ForIn) { @@ -7575,7 +7611,7 @@ Compressor.prototype.compress = function(node) { return true; } if (node instanceof AST_SymbolRef) { - node_def = node.definition(); + var node_def = node.definition(); if (!(node_def.id in in_use_ids)) { in_use_ids[node_def.id] = true; in_use.push(node_def); diff --git a/test/compress/classes.js b/test/compress/classes.js index 6695ccde7dc..e0ce3e10872 100644 --- a/test/compress/classes.js +++ b/test/compress/classes.js @@ -1283,6 +1283,88 @@ instanceof_lambda: { node_version: ">=4" } +drop_instanceof: { + options = { + booleans: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + class A {} + console.log({} instanceof A, Math instanceof A); + } + expect: { + "use strict"; + console.log(!1, (Math, !1)); + } + expect_stdout: "false false" + node_version: ">=4" +} + +keep_instanceof_1: { + options = { + toplevel: true, + unused: true, + } + input: { + "use strict"; + class A {} + var A; + console.log({} instanceof A, Math instanceof A); + } + expect: { + "use strict"; + class A {} + var A; + console.log({} instanceof A, Math instanceof A); + } + expect_stdout: SyntaxError("Identifier has already been declared") + node_version: ">=4" +} + +keep_instanceof_2: { + options = { + toplevel: true, + unused: true, + } + input: { + "use strict"; + var A = Object; + class A {} + console.log({} instanceof A, Math instanceof A); + } + expect: { + "use strict"; + var A = Object; + class A {} + console.log({} instanceof A, Math instanceof A); + } + expect_stdout: SyntaxError("Identifier has already been declared") + node_version: ">=4" +} + +keep_instanceof_3: { + options = { + toplevel: true, + unused: true, + } + input: { + "use strict"; + class A {} + A = Object; + console.log({} instanceof A, Math instanceof A); + } + expect: { + "use strict"; + class A {} + A = Object; + console.log({} instanceof A, Math instanceof A); + } + expect_stdout: "true true" + node_version: ">=4" +} + issue_805_1: { options = { inline: true, diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index b3e7bb2f89c..21e5c4d6d3c 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -671,6 +671,76 @@ iife: { } } +drop_instanceof: { + options = { + booleans: true, + toplevel: true, + unused: true, + } + input: { + function f() {} + console.log({} instanceof f, Math instanceof f); + } + expect: { + console.log(!1, (Math, !1)); + } + expect_stdout: "false false" +} + +keep_instanceof_1: { + options = { + toplevel: true, + unused: true, + } + input: { + function f() {} + var f; + console.log({} instanceof f, Math instanceof f); + } + expect: { + function f() {} + var f; + console.log({} instanceof f, Math instanceof f); + } + expect_stdout: "false false" +} + +keep_instanceof_2: { + options = { + toplevel: true, + unused: true, + } + input: { + function f() {} + var f = Object; + console.log({} instanceof f, Math instanceof f); + } + expect: { + function f() {} + var f = Object; + console.log({} instanceof f, Math instanceof f); + } + expect_stdout: "true true" +} + +keep_instanceof_3: { + options = { + toplevel: true, + unused: true, + } + input: { + f = Object; + function f() {} + console.log({} instanceof f, Math instanceof f); + } + expect: { + f = Object; + function f() {} + console.log({} instanceof f, Math instanceof f); + } + expect_stdout: "true true" +} + issue_1539: { options = { collapse_vars: true, diff --git a/test/compress/exports.js b/test/compress/exports.js index 2b5e6aabd46..abcd13b08a6 100644 --- a/test/compress/exports.js +++ b/test/compress/exports.js @@ -417,6 +417,46 @@ hoist_funs: { expect_exact: "export function f(){}export default async function*g(){}" } +instanceof_default_class: { + options = { + toplevel: true, + unused: true, + } + input: { + export default class A { + f(a) { + return a instanceof A; + } + } + } + expect: { + export default class A { + f(a) { + return a instanceof A; + } + } + } +} + +instanceof_default_function: { + options = { + toplevel: true, + unused: true, + } + input: { + export default function f() { + if (!(this instanceof f)) + throw new Error("must instantiate"); + } + } + expect: { + export default function f() { + if (!(this instanceof f)) + throw new Error("must instantiate"); + } + } +} + issue_4742_join_vars_1: { options = { join_vars: true,