fix corner cases with class (#4723)

fixes #4720
fixes #4721
fixes #4722
This commit is contained in:
Alex Lam S.L
2021-03-02 19:32:58 +00:00
committed by GitHub
parent adcafce048
commit 955411e065
3 changed files with 148 additions and 8 deletions

View File

@@ -874,6 +874,7 @@ var AST_ClassMethod = DEFNODE("ClassMethod", null, {
$documentation: "A `class` method", $documentation: "A `class` method",
_validate: function() { _validate: function() {
if (!(this.value instanceof AST_LambdaExpression)) throw new Error("value must be AST_LambdaExpression"); 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"); if (this.value.name != null) throw new Error("name of class method's lambda must be null");
}, },
}, AST_ClassProperty); }, AST_ClassProperty);

View File

@@ -409,6 +409,10 @@ merge(Compressor.prototype, {
return node instanceof AST_Class || node instanceof AST_Lambda; 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) { function is_arguments(def) {
return def.name == "arguments" && def.scope.uses_arguments; return def.name == "arguments" && def.scope.uses_arguments;
} }
@@ -4815,7 +4819,7 @@ merge(Compressor.prototype, {
var base = this.extends; var base = this.extends;
if (base) { if (base) {
if (base instanceof AST_SymbolRef) base = base.fixed_value(); 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); return any(this.properties, compressor);
}); });
@@ -5037,7 +5041,7 @@ merge(Compressor.prototype, {
}); });
def(AST_Class, function(scope) { def(AST_Class, function(scope) {
var base = this.extends; 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); return all_constant(this.properties, scope);
}); });
def(AST_ClassProperty, function(scope) { def(AST_ClassProperty, function(scope) {
@@ -7310,7 +7314,7 @@ merge(Compressor.prototype, {
var base = this.extends; var base = this.extends;
if (base) { if (base) {
if (base instanceof AST_SymbolRef) base = base.fixed_value(); 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); if (!base) exprs.unshift(this.extends);
} }
exprs = trim(exprs, compressor, first_in_statement); exprs = trim(exprs, compressor, first_in_statement);
@@ -7320,10 +7324,6 @@ merge(Compressor.prototype, {
if (!base && !values) return null; if (!base && !values) return null;
exprs = []; exprs = [];
} }
if (base) exprs.unshift(make_node(AST_ClassExpression, this, {
extends: this.extends,
properties: [],
}));
if (values) { if (values) {
var fn = make_node(AST_Arrow, this, { var fn = make_node(AST_Arrow, this, {
argnames: [], argnames: [],
@@ -7336,7 +7336,19 @@ merge(Compressor.prototype, {
expression: fn, 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) { def(AST_Conditional, function(compressor) {
var consequent = this.consequent.drop_side_effect_free(compressor); var consequent = this.consequent.drop_side_effect_free(compressor);
@@ -10267,6 +10279,8 @@ merge(Compressor.prototype, {
single_use = false; single_use = false;
} else if (fixed.has_side_effects(compressor)) { } else if (fixed.has_side_effects(compressor)) {
single_use = false; single_use = false;
} else if (compressor.option("ie8") && fixed instanceof AST_Class) {
single_use = false;
} }
if (single_use) fixed.parent_scope = self.scope; if (single_use) fixed.parent_scope = self.scope;
} else if (!fixed || !fixed.is_constant_expression() || fixed.drop_side_effect_free(compressor)) { } else if (!fixed || !fixed.is_constant_expression() || fixed.drop_side_effect_free(compressor)) {

View File

@@ -1047,3 +1047,128 @@ issue_4705: {
expect_stdout: "PASS" expect_stdout: "PASS"
node_version: ">=12" 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"
}