From: Alex Lam S.L Date: Tue, 2 Mar 2021 19:32:58 +0000 (+0000) Subject: fix corner cases with `class` (#4723) X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=955411e0657cbaea3226537b2738307d10754b02;p=UglifyJS.git fix corner cases with `class` (#4723) fixes #4720 fixes #4721 fixes #4722 --- diff --git a/lib/ast.js b/lib/ast.js index 1aee1044..b035b5c7 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -874,6 +874,7 @@ var AST_ClassMethod = DEFNODE("ClassMethod", null, { $documentation: "A `class` method", _validate: function() { if (!(this.value instanceof AST_LambdaExpression)) throw new Error("value must be AST_LambdaExpression"); + if (is_arrow(this.value)) throw new Error("value cannot be AST_Arrow or AST_AsyncArrow"); if (this.value.name != null) throw new Error("name of class method's lambda must be null"); }, }, AST_ClassProperty); diff --git a/lib/compress.js b/lib/compress.js index 7f642797..55ff50be 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -409,6 +409,10 @@ merge(Compressor.prototype, { return node instanceof AST_Class || node instanceof AST_Lambda; } + function safe_for_extends(node) { + return node instanceof AST_Class || node instanceof AST_Defun || node instanceof AST_Function; + } + function is_arguments(def) { return def.name == "arguments" && def.scope.uses_arguments; } @@ -4815,7 +4819,7 @@ merge(Compressor.prototype, { var base = this.extends; if (base) { if (base instanceof AST_SymbolRef) base = base.fixed_value(); - if (!is_lambda(base) || is_arrow(base)) return true; + if (!safe_for_extends(base)) return true; } return any(this.properties, compressor); }); @@ -5037,7 +5041,7 @@ merge(Compressor.prototype, { }); def(AST_Class, function(scope) { var base = this.extends; - if (base && (!is_lambda(base) || is_arrow(base))) return false; + if (base && !safe_for_extends(base)) return false; return all_constant(this.properties, scope); }); def(AST_ClassProperty, function(scope) { @@ -7310,7 +7314,7 @@ merge(Compressor.prototype, { var base = this.extends; if (base) { if (base instanceof AST_SymbolRef) base = base.fixed_value(); - base = !is_lambda(base) || is_arrow(base); + base = !safe_for_extends(base); if (!base) exprs.unshift(this.extends); } exprs = trim(exprs, compressor, first_in_statement); @@ -7320,10 +7324,6 @@ merge(Compressor.prototype, { if (!base && !values) return null; exprs = []; } - if (base) exprs.unshift(make_node(AST_ClassExpression, this, { - extends: this.extends, - properties: [], - })); if (values) { var fn = make_node(AST_Arrow, this, { argnames: [], @@ -7336,7 +7336,19 @@ merge(Compressor.prototype, { expression: fn, })); } - return make_sequence(this, exprs); + exprs = exprs.length ? make_sequence(this, exprs) : null; + if (!base) return exprs; + var node = make_node(AST_ClassExpression, this, this); + node.name = null; + node.properties = []; + if (exprs) node.properties.push(make_node(AST_ClassMethod, this, { + key: exprs, + value: make_node(AST_Function, this, { + argnames: [], + body: [], + }).init_vars(node), + })); + return node; }); def(AST_Conditional, function(compressor) { var consequent = this.consequent.drop_side_effect_free(compressor); @@ -10267,6 +10279,8 @@ merge(Compressor.prototype, { single_use = false; } else if (fixed.has_side_effects(compressor)) { single_use = false; + } else if (compressor.option("ie8") && fixed instanceof AST_Class) { + single_use = false; } if (single_use) fixed.parent_scope = self.scope; } else if (!fixed || !fixed.is_constant_expression() || fixed.drop_side_effect_free(compressor)) { diff --git a/test/compress/classes.js b/test/compress/classes.js index 279cff65..f073acd4 100644 --- a/test/compress/classes.js +++ b/test/compress/classes.js @@ -1047,3 +1047,128 @@ issue_4705: { expect_stdout: "PASS" node_version: ">=12" } + +issue_4720: { + options = { + ie8: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + class A { + static p = function f() {}; + } + console.log(typeof A.p, typeof f); + } + expect: { + class A { + static p = function f() {}; + } + console.log(typeof A.p, typeof f); + } + expect_stdout: "function undefined" + node_version: ">=12" +} + +issue_4721: { + options = { + side_effects: true, + } + input: { + "use strict"; + var a = "foo"; + try { + (class extends 42 { + [a = "bar"]() {} + }) + } catch (e) { + console.log(a); + } + } + expect: { + "use strict"; + var a = "foo"; + try { + (class extends 42 { + [a = "bar"]() {} + }); + } catch (e) { + console.log(a); + } + } + expect_stdout: true + node_version: ">=4" +} + +issue_4722_1: { + options = { + side_effects: true, + } + input: { + "use strict"; + try { + (class extends function*() {} {}); + } catch (e) { + console.log("PASS"); + } + } + expect: { + "use strict"; + try { + (class extends function*() {} {}); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4722_2: { + options = { + side_effects: true, + } + input: { + "use strict"; + try { + (class extends async function() {} {}); + } catch (e) { + console.log("PASS"); + } + } + expect: { + "use strict"; + try { + (class extends async function() {} {}); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_4722_3: { + options = { + side_effects: true, + } + input: { + "use strict"; + try { + (class extends async function*() {} {}); + } catch (e) { + console.log("PASS"); + } + } + expect: { + "use strict"; + try { + (class extends async function*() {} {}); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=10" +}