aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Lam S.L <alexlamsl@gmail.com>2021-03-02 19:32:58 +0000
committerGitHub <noreply@github.com>2021-03-03 03:32:58 +0800
commit955411e0657cbaea3226537b2738307d10754b02 (patch)
tree6580fa1e3502f744d271978c3ef412cfe73521c5
parentadcafce04852bb4730e3e699dfdd656dc018cd0f (diff)
downloadtracifyjs-955411e0657cbaea3226537b2738307d10754b02.tar.gz
tracifyjs-955411e0657cbaea3226537b2738307d10754b02.zip
fix corner cases with `class` (#4723)
fixes #4720 fixes #4721 fixes #4722
-rw-r--r--lib/ast.js1
-rw-r--r--lib/compress.js30
-rw-r--r--test/compress/classes.js125
3 files changed, 148 insertions, 8 deletions
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"
+}