From 68b2dadc58d649f3a917b602a66804ad50aae2ae Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Tue, 2 Mar 2021 04:55:09 +0800 Subject: [PATCH] enhance `side_effects` & `unused` (#4711) --- .github/workflows/ci.yml | 2 +- lib/compress.js | 35 ++++++++++++------- test/compress/classes.js | 74 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 96 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2cc18d88..60db779c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ jobs: test: strategy: matrix: - node: [ '0.8', '0.10', '0.12', '4', '6', '8', '10', '12', latest ] + node: [ '0.8', '0.10', '0.12', '4', '6', '8', '10', '12', '14', latest ] os: [ ubuntu-latest, windows-latest ] script: [ compress, mocha, release/benchmark, release/jetstream ] exclude: diff --git a/lib/compress.js b/lib/compress.js index fb6d16f9..6b9d7e7c 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -420,12 +420,14 @@ merge(Compressor.prototype, { } while (sym = sym.parent_scope); } - function can_drop_symbol(ref, keep_lambda) { + function can_drop_symbol(ref, compressor, keep_lambda) { var def = ref.definition(); if (ref.in_arg && is_funarg(def)) return false; return all(def.orig, function(sym) { - return !(sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLet - || keep_lambda && sym instanceof AST_SymbolLambda); + if (sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLet) { + return compressor && can_varify(compressor, sym); + } + return !(keep_lambda && sym instanceof AST_SymbolLambda); }); } @@ -599,6 +601,7 @@ merge(Compressor.prototype, { function is_immutable(value) { if (!value) return false; return value.is_constant() + || value instanceof AST_Class || value instanceof AST_Lambda || value instanceof AST_ObjectIdentity; } @@ -3585,6 +3588,7 @@ merge(Compressor.prototype, { return false; } }); + def(AST_Class, return_false); def(AST_Conditional, function(compressor) { return this.consequent._dot_throw(compressor) || this.alternative._dot_throw(compressor); }); @@ -3593,7 +3597,7 @@ merge(Compressor.prototype, { if (!is_strict(compressor)) return false; var exp = this.expression; if (exp instanceof AST_SymbolRef) exp = exp.fixed_value(); - return !(exp instanceof AST_Lambda && this.property == "prototype"); + return !(this.property == "prototype" && (exp instanceof AST_Class || exp instanceof AST_Lambda)); }); def(AST_Lambda, return_false); def(AST_Null, return_true); @@ -4869,7 +4873,7 @@ merge(Compressor.prototype, { }); def(AST_SymbolDeclaration, return_false); def(AST_SymbolRef, function(compressor) { - return !this.is_declared(compressor) || !can_drop_symbol(this); + return !this.is_declared(compressor) || !can_drop_symbol(this, compressor); }); def(AST_Template, function(compressor) { return this.tag && !is_raw_tag(compressor, this.tag) || any(this.expressions, compressor); @@ -5698,7 +5702,7 @@ merge(Compressor.prototype, { var def = sym.definition(); if (export_defaults[def.id]) return; if (compressor.exposed(def)) return; - if (!can_drop_symbol(sym, nested)) return; + if (!can_drop_symbol(sym, compressor, nested)) return; return sym; }; var assign_in_use = Object.create(null); @@ -7418,7 +7422,7 @@ merge(Compressor.prototype, { return make_sequence(this, [ expression, property ]); }); def(AST_SymbolRef, function(compressor) { - return this.is_declared(compressor) && can_drop_symbol(this) ? null : this; + return this.is_declared(compressor) && can_drop_symbol(this, compressor) ? null : this; }); def(AST_Template, function(compressor, first_in_statement) { if (this.tag && !is_raw_tag(compressor, this.tag)) return this; @@ -7432,7 +7436,9 @@ merge(Compressor.prototype, { this.write_only = !exp.has_side_effects(compressor); return this; } - if (this.operator == "typeof" && exp instanceof AST_SymbolRef && can_drop_symbol(exp)) return null; + if (this.operator == "typeof" && exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor)) { + return null; + } var node = exp.drop_side_effect_free(compressor, first_in_statement); if (first_in_statement && node && is_iife_call(node)) { if (node === exp && this.operator == "!") return this; @@ -8237,13 +8243,16 @@ merge(Compressor.prototype, { }); } + function can_varify(compressor, sym) { + if (!sym.fixed_value()) return false; + var def = sym.definition(); + return is_safe_lexical(def) && !may_overlap(compressor, def); + } + function varify(self, compressor) { return compressor.option("varify") && all(self.definitions, function(defn) { return !defn.name.match_symbol(function(node) { - if (node instanceof AST_SymbolDeclaration) { - var def = node.definition(); - return !node.fixed_value() || !is_safe_lexical(def) || may_overlap(compressor, def); - } + if (node instanceof AST_SymbolDeclaration) return !can_varify(compressor, node); }, true); }) ? to_var(self) : self; } @@ -9370,7 +9379,7 @@ merge(Compressor.prototype, { // always true in booleans AST_Node.warn("Boolean expression always true [{file}:{line},{col}]", self.start); var exprs = [ make_node(AST_True, self) ]; - if (!(exp instanceof AST_SymbolRef && can_drop_symbol(exp))) exprs.unshift(exp); + if (!(exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor))) exprs.unshift(exp); return make_sequence(self, exprs).optimize(compressor); } } diff --git a/test/compress/classes.js b/test/compress/classes.js index dea0d5b0..f94731fb 100644 --- a/test/compress/classes.js +++ b/test/compress/classes.js @@ -125,7 +125,7 @@ private_methods: { } expect_exact: "(new class A{static*#f(){yield 3*A.#p}async #g(){for(var a of A.#f())return a*await 2}static get #p(){return 7}get q(){return this.#g()}}).q.then(console.log);" expect_stdout: "42" - node_version: ">=14" + node_version: ">=14.6" } await: { @@ -573,6 +573,78 @@ computed_key_generator: { node_version: ">=4" } +issue_805_1: { + options = { + inline: true, + passes: 2, + pure_getters: "strict", + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + "use strict"; + (function(a) { + var unused = class {}; + unused.prototype[a()] = 42; + (unused.prototype.bar = function() { + console.log("bar"); + })(); + return unused; + })(function() { + console.log("foo"); + return "foo"; + }); + } + expect: { + "use strict"; + console.log("foo"), + console.log("bar"); + } + expect_stdout: [ + "foo", + "bar", + ] + node_version: ">=4" +} + +issue_805_2: { + options = { + inline: true, + passes: 2, + pure_getters: "strict", + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + "use strict"; + (function(a) { + class unused {} + unused.prototype[a()] = 42; + (unused.prototype.bar = function() { + console.log("bar"); + })(); + return unused; + })(function() { + console.log("foo"); + return "foo"; + }); + } + expect: { + "use strict"; + console.log("foo"), + console.log("bar"); + } + expect_stdout: [ + "foo", + "bar", + ] + node_version: ">=4" +} + issue_4681: { options = { unused: true, -- 2.34.1