Skip to content

Commit 8a3ae44

Browse files
authored
fix(es/decorators): Fix bugs of 2022-03 implementation (#9145)
**Description:** I extracted some changes from #8970
1 parent ce45417 commit 8a3ae44

File tree

32 files changed

+489
-360
lines changed

32 files changed

+489
-360
lines changed

crates/swc/src/config/mod.rs

+14-28
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ use swc_ecma_minifier::option::terser::TerserTopLevelOptions;
4242
#[allow(deprecated)]
4343
pub use swc_ecma_parser::JscTarget;
4444
use swc_ecma_parser::{parse_file_as_expr, Syntax, TsSyntax};
45+
pub use swc_ecma_transforms::proposals::DecoratorVersion;
4546
use swc_ecma_transforms::{
4647
feature::FeatureFlag,
4748
hygiene, modules,
@@ -676,6 +677,18 @@ impl Options {
676677
{
677678
Box::new(plugin_transforms)
678679
} else {
680+
let decorator_pass: Box<dyn Fold> =
681+
match transform.decorator_version.unwrap_or_default() {
682+
DecoratorVersion::V202112 => Box::new(decorators(decorators::Config {
683+
legacy: transform.legacy_decorator.into_bool(),
684+
emit_metadata: transform.decorator_metadata.into_bool(),
685+
use_define_for_class_fields: !assumptions.set_public_class_fields,
686+
})),
687+
DecoratorVersion::V202203 => Box::new(
688+
swc_ecma_transforms::proposals::decorator_2022_03::decorator_2022_03(),
689+
),
690+
};
691+
679692
Box::new(chain!(
680693
lint_to_fold(swc_ecma_lints::rules::all(LintParams {
681694
program: &program,
@@ -686,23 +699,7 @@ impl Options {
686699
source_map: cm.clone(),
687700
})),
688701
// Decorators may use type information
689-
Optional::new(
690-
match transform.decorator_version.unwrap_or_default() {
691-
DecoratorVersion::V202112 => {
692-
Either::Left(decorators(decorators::Config {
693-
legacy: transform.legacy_decorator.into_bool(),
694-
emit_metadata: transform.decorator_metadata.into_bool(),
695-
use_define_for_class_fields: !assumptions.set_public_class_fields,
696-
}))
697-
}
698-
DecoratorVersion::V202203 => {
699-
Either::Right(
700-
swc_ecma_transforms::proposals::decorator_2022_03::decorator_2022_03(),
701-
)
702-
}
703-
},
704-
syntax.decorators()
705-
),
702+
Optional::new(decorator_pass, syntax.decorators()),
706703
Optional::new(
707704
explicit_resource_management(),
708705
syntax.explicit_resource_management()
@@ -1446,17 +1443,6 @@ pub struct TransformConfig {
14461443
pub decorator_version: Option<DecoratorVersion>,
14471444
}
14481445

1449-
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
1450-
#[serde(deny_unknown_fields, rename_all = "camelCase")]
1451-
pub enum DecoratorVersion {
1452-
#[default]
1453-
#[serde(rename = "2021-12")]
1454-
V202112,
1455-
1456-
#[serde(rename = "2022-03")]
1457-
V202203,
1458-
}
1459-
14601446
#[derive(Debug, Default, Clone, Serialize, Deserialize, Merge)]
14611447
#[serde(deny_unknown_fields, rename_all = "camelCase")]
14621448
pub struct HiddenTransformConfig {

crates/swc_ecma_transforms_proposal/src/decorator_2022_03.rs

+35-8
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ use swc_ecma_utils::{
1313
use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith};
1414

1515
pub fn decorator_2022_03() -> impl VisitMut + Fold {
16-
as_folder(Decorator202203::default())
16+
as_folder(Decorator2022_03::default())
1717
}
1818

1919
#[derive(Default)]
20-
struct Decorator202203 {
20+
struct Decorator2022_03 {
2121
/// Variables without initializer.
2222
extra_vars: Vec<VarDeclarator>,
2323

@@ -35,6 +35,8 @@ struct Decorator202203 {
3535

3636
#[derive(Default)]
3737
struct ClassState {
38+
private_id_index: u32,
39+
3840
static_lhs: Vec<Ident>,
3941
proto_lhs: Vec<Ident>,
4042

@@ -55,7 +57,7 @@ struct ClassState {
5557
super_class: Option<Ident>,
5658
}
5759

58-
impl Decorator202203 {
60+
impl Decorator2022_03 {
5961
fn preserve_side_effect_of_decorators(
6062
&mut self,
6163
decorators: Vec<Decorator>,
@@ -463,6 +465,7 @@ impl Decorator202203 {
463465
let has_static_member = body.iter().any(|m| match m {
464466
ClassMember::Method(m) => m.is_static,
465467
ClassMember::PrivateMethod(m) => m.is_static,
468+
ClassMember::AutoAccessor(m) => m.is_static,
466469
ClassMember::ClassProp(ClassProp { is_static, .. })
467470
| ClassMember::PrivateProp(PrivateProp { is_static, .. }) => *is_static,
468471
ClassMember::StaticBlock(_) => true,
@@ -527,7 +530,9 @@ impl Decorator202203 {
527530

528531
for m in body.iter_mut() {
529532
match m {
530-
ClassMember::ClassProp(..) | ClassMember::PrivateProp(..) => {
533+
ClassMember::ClassProp(..)
534+
| ClassMember::PrivateProp(..)
535+
| ClassMember::AutoAccessor(..) => {
531536
replace_ident(m, c.ident.to_id(), &new_class_name);
532537
}
533538

@@ -568,6 +573,13 @@ impl Decorator202203 {
568573
p.is_static = false;
569574
}
570575
}
576+
577+
ClassMember::AutoAccessor(p) => {
578+
if p.is_static {
579+
should_move = true;
580+
p.is_static = false;
581+
}
582+
}
571583
_ => (),
572584
}
573585

@@ -753,7 +765,7 @@ impl Decorator202203 {
753765
}
754766
}
755767

756-
impl VisitMut for Decorator202203 {
768+
impl VisitMut for Decorator2022_03 {
757769
noop_visit_mut_type!();
758770

759771
fn visit_mut_class(&mut self, n: &mut Class) {
@@ -932,6 +944,8 @@ impl VisitMut for Decorator202203 {
932944
for mut m in members.take() {
933945
match m {
934946
ClassMember::AutoAccessor(mut accessor) => {
947+
accessor.value.visit_mut_with(self);
948+
935949
let name;
936950
let init;
937951
let field_name_like: JsWord;
@@ -947,9 +961,14 @@ impl VisitMut for Decorator202203 {
947961
init = private_ident!(format!("_init_{}", k.id.sym));
948962
field_name_like = format!("__{}", k.id.sym).into();
949963

964+
self.state.private_id_index += 1;
950965
PrivateName {
951966
span: k.span,
952-
id: Ident::new(format!("__{}", k.id.sym).into(), k.id.span),
967+
id: Ident::new(
968+
format!("__{}_{}", k.id.sym, self.state.private_id_index)
969+
.into(),
970+
k.id.span,
971+
),
953972
}
954973
}
955974
Key::Public(k) => {
@@ -958,10 +977,16 @@ impl VisitMut for Decorator202203 {
958977
.replacen("init", "private", 1)
959978
.into();
960979

980+
self.state.private_id_index += 1;
981+
961982
PrivateName {
962983
span: init.span.with_ctxt(SyntaxContext::empty()),
963984
id: Ident::new(
964-
field_name_like.clone(),
985+
format!(
986+
"{field_name_like}_{}",
987+
self.state.private_id_index
988+
)
989+
.into(),
965990
init.span.with_ctxt(SyntaxContext::empty()),
966991
),
967992
}
@@ -1310,7 +1335,9 @@ impl VisitMut for Decorator202203 {
13101335

13111336
for mut m in new.take() {
13121337
match m {
1313-
ClassMember::Method(..) | ClassMember::PrivateMethod(..) => {}
1338+
ClassMember::Method(..)
1339+
| ClassMember::PrivateMethod(..)
1340+
| ClassMember::AutoAccessor(..) => {}
13141341

13151342
_ => {
13161343
if !m.span().is_dummy() {

crates/swc_ecma_transforms_proposal/src/lib.rs

+13
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
11
#![deny(clippy::all)]
22
#![allow(clippy::vec_box)]
33

4+
use serde::{Deserialize, Serialize};
5+
46
pub use self::{
57
decorators::decorators, export_default_from::export_default_from,
68
import_assertions::import_assertions,
79
};
810

11+
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
12+
#[serde(deny_unknown_fields, rename_all = "camelCase")]
13+
pub enum DecoratorVersion {
14+
#[default]
15+
#[serde(rename = "2021-12")]
16+
V202112,
17+
18+
#[serde(rename = "2022-03")]
19+
V202203,
20+
}
21+
922
pub mod decorator_2022_03;
1023
pub mod decorators;
1124
pub mod explicit_resource_management;

crates/swc_ecma_transforms_proposal/tests/.gitignore

-1
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
(() => {
2+
let old;
3+
let block;
4+
class Bar {
5+
}
6+
const dec = (cls, ctx) => {
7+
old = cls;
8+
return Bar;
9+
};
10+
const Foo =
11+
@dec
12+
class Foo {
13+
static { block = Foo; }
14+
method() { return Foo; }
15+
static method() { return Foo; }
16+
field = Foo;
17+
static field = Foo;
18+
get getter() { return Foo; }
19+
static get getter() { return Foo; }
20+
set setter(x) { x.foo = Foo; }
21+
static set setter(x) { x.foo = Foo; }
22+
accessor accessor = Foo;
23+
static accessor accessor = Foo;
24+
};
25+
const foo = new old;
26+
let obj;
27+
assertEq(() => Foo !== old, true);
28+
assertEq(() => Foo, Bar);
29+
assertEq(() => block, Bar);
30+
assertEq(() => Foo.field, Bar);
31+
assertEq(() => foo.field, Bar);
32+
assertEq(() => old.getter, Bar);
33+
assertEq(() => foo.getter, Bar);
34+
assertEq(() => (obj = { foo: null }, old.setter = obj, obj.foo), Bar);
35+
assertEq(() => (obj = { foo: null }, foo.setter = obj, obj.foo), Bar);
36+
// The specification for accessors is potentially wrong at the moment: https://github.com/tc39/proposal-decorators/issues/529
37+
// assertEq(() => old.accessor, Bar)
38+
// assertEq(() => foo.accessor, Bar)
39+
})();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
(() => {
2+
let old;
3+
let block;
4+
class Bar {
5+
}
6+
const dec = (cls, ctx) => {
7+
old = cls;
8+
return Bar;
9+
};
10+
@dec
11+
class Foo {
12+
static { block = Foo; }
13+
method() { return Foo; }
14+
static method() { return Foo; }
15+
field = Foo;
16+
static field = Foo;
17+
get getter() { return Foo; }
18+
static get getter() { return Foo; }
19+
set setter(x) { x.foo = Foo; }
20+
static set setter(x) { x.foo = Foo; }
21+
accessor accessor = Foo;
22+
static accessor accessor = Foo;
23+
}
24+
const foo = new old;
25+
let obj;
26+
assertEq(() => Foo !== old, true);
27+
assertEq(() => Foo, Bar);
28+
assertEq(() => block, Bar);
29+
assertEq(() => Foo.field, Bar);
30+
assertEq(() => foo.field, Bar);
31+
assertEq(() => old.getter, Bar);
32+
assertEq(() => foo.getter, Bar);
33+
assertEq(() => (obj = { foo: null }, old.setter = obj, obj.foo), Bar);
34+
assertEq(() => (obj = { foo: null }, foo.setter = obj, obj.foo), Bar);
35+
// The specification for accessors is potentially wrong at the moment: https://github.com/tc39/proposal-decorators/issues/529
36+
// assertEq(() => old.accessor, Bar)
37+
// assertEq(() => foo.accessor, Bar)
38+
})();

crates/swc_ecma_transforms_proposal/tests/decorator-evanw-split/Decorator-metadata-class-expression.js

+19
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,23 @@
6565
assertEq(() => Object.getPrototypeOf(foo), null);
6666
assertEq(() => order(bar), '0,1,2,3,11,12,13,14,4,5,6,7,15,16,17,18,8,19,9,20,10,21');
6767
assertEq(() => Object.getPrototypeOf(bar), foo);
68+
// Test an undecorated class
69+
const FooNoDec = class {
70+
};
71+
const BarNoDec = class extends FooNoDec {
72+
};
73+
assertEq(() => FooNoDec[Symbol.metadata], null);
74+
assertEq(() => BarNoDec[Symbol.metadata], null);
75+
// Test a class with no class decorator
76+
const FooOneDec = class {
77+
@dec
78+
x;
79+
};
80+
const BarOneDec = class extends FooOneDec {
81+
@dec
82+
y;
83+
};
84+
assertEq(() => JSON.stringify(FooOneDec[Symbol.metadata]), JSON.stringify({ x: 22 }));
85+
assertEq(() => JSON.stringify(BarOneDec[Symbol.metadata]), JSON.stringify({ y: 23 }));
86+
assertEq(() => Object.getPrototypeOf(BarOneDec[Symbol.metadata]), FooOneDec[Symbol.metadata]);
6887
})();

crates/swc_ecma_transforms_proposal/tests/decorator-evanw-split/Decorator-metadata-class-statement.js

+19
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,23 @@
6464
assertEq(() => Object.getPrototypeOf(foo), null);
6565
assertEq(() => order(bar), '0,1,2,3,11,12,13,14,4,5,6,7,15,16,17,18,8,19,9,20,10,21');
6666
assertEq(() => Object.getPrototypeOf(bar), foo);
67+
// Test an undecorated class
68+
class FooNoDec {
69+
}
70+
class BarNoDec extends FooNoDec {
71+
}
72+
assertEq(() => FooNoDec[Symbol.metadata], null);
73+
assertEq(() => BarNoDec[Symbol.metadata], null);
74+
// Test a class with no class decorator
75+
class FooOneDec {
76+
@dec
77+
x;
78+
}
79+
class BarOneDec extends FooOneDec {
80+
@dec
81+
y;
82+
}
83+
assertEq(() => JSON.stringify(FooOneDec[Symbol.metadata]), JSON.stringify({ x: 22 }));
84+
assertEq(() => JSON.stringify(BarOneDec[Symbol.metadata]), JSON.stringify({ y: 23 }));
85+
assertEq(() => Object.getPrototypeOf(BarOneDec[Symbol.metadata]), FooOneDec[Symbol.metadata]);
6786
})();

0 commit comments

Comments
 (0)