Skip to content

Commit 5cc585b

Browse files
authoredApr 11, 2024··
fix(es/compat): Handle class fields correctly (#8835)
**Related issue:** - Closes #8830
1 parent ea830aa commit 5cc585b

File tree

4 files changed

+121
-100
lines changed

4 files changed

+121
-100
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"jsc": {
3+
"target": "es2018",
4+
"parser": {
5+
"syntax": "ecmascript"
6+
},
7+
"transform": {
8+
"useDefineForClassFields": false
9+
}
10+
},
11+
"isModule": true
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
class MyClass {
2+
// also same behavior for static methods
3+
methodReturningClass() {
4+
return class SomeOtherClass {
5+
constructor() {
6+
// Assignment to foo goes here
7+
}
8+
};
9+
}
10+
11+
constructor(args) {
12+
// Assignment to foo should go here
13+
}
14+
15+
foo = "bar";
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
class MyClass {
2+
// also same behavior for static methods
3+
methodReturningClass() {
4+
return class SomeOtherClass {
5+
constructor(){
6+
// Assignment to foo goes here
7+
}
8+
};
9+
}
10+
constructor(args){
11+
this.foo = "bar";
12+
// Assignment to foo should go here
13+
}
14+
}

‎crates/swc_ecma_transforms_compat/src/class_fields_use_set.rs

+79-100
Original file line numberDiff line numberDiff line change
@@ -104,27 +104,14 @@ impl VisitMut for ClassFieldsUseSet {
104104
}
105105

106106
fn visit_mut_class(&mut self, n: &mut Class) {
107+
// visit inner classes first
107108
n.visit_mut_children_with(self);
108109

109-
let mut fields_handler = FieldsHandler::default();
110-
n.visit_mut_with(&mut fields_handler);
111-
112-
let FieldsHandler {
113-
constructor_inits,
114-
constructor_found,
115-
..
116-
} = fields_handler;
117-
118-
if constructor_inits.is_empty() {
119-
return;
120-
}
121-
122-
let mut constructor_handler = ConstructorHandler {
110+
let mut fields_handler = FieldsHandler {
123111
has_super: n.super_class.is_some(),
124-
constructor_inits,
125-
constructor_found,
126112
};
127-
n.visit_mut_with(&mut constructor_handler);
113+
114+
n.body.visit_mut_with(&mut fields_handler);
128115
}
129116
}
130117

@@ -170,113 +157,105 @@ impl ClassFieldsUseSet {
170157
}
171158
}
172159

173-
#[derive(Debug, Default)]
160+
#[derive(Debug)]
174161
struct FieldsHandler {
175-
constructor_inits: Vec<Box<Expr>>,
176-
constructor_found: bool,
162+
has_super: bool,
177163
}
178164

179165
impl VisitMut for FieldsHandler {
180166
noop_visit_mut_type!();
181167

182-
fn visit_mut_class(&mut self, n: &mut Class) {
183-
n.body.visit_mut_with(self);
168+
fn visit_mut_class(&mut self, _: &mut Class) {
169+
// skip inner classes
170+
// In fact, FieldsHandler does not visit children recursively.
171+
// We call FieldsHandler with the class.body as the only entry point.
172+
// The FieldsHandler actually operates in a iterative way.
184173
}
185174

186-
fn visit_mut_class_member(&mut self, n: &mut ClassMember) {
187-
match n {
188-
ClassMember::Constructor(..) => self.constructor_found = true,
189-
ClassMember::ClassProp(ClassProp {
190-
ref span,
191-
ref is_static,
192-
key,
193-
value,
194-
..
195-
}) => {
196-
if let Some(value) = value.take() {
197-
let init_expr: Expr = AssignExpr {
198-
span: *span,
199-
op: op!("="),
200-
left: MemberExpr {
201-
span: DUMMY_SP,
202-
obj: ThisExpr::dummy().into(),
203-
prop: prop_name_to_member_prop(key.take()),
204-
}
205-
.into(),
206-
right: value,
207-
}
208-
.into();
209-
210-
if *is_static {
211-
*n = StaticBlock {
212-
span: DUMMY_SP,
213-
body: BlockStmt {
175+
fn visit_mut_class_members(&mut self, n: &mut Vec<ClassMember>) {
176+
let mut constructor_inits = vec![];
177+
178+
for member in n.iter_mut() {
179+
match member {
180+
ClassMember::ClassProp(ClassProp {
181+
ref span,
182+
ref is_static,
183+
key,
184+
value,
185+
..
186+
}) => {
187+
if let Some(value) = value.take() {
188+
let init_expr: Expr = AssignExpr {
189+
span: *span,
190+
op: op!("="),
191+
left: MemberExpr {
214192
span: DUMMY_SP,
215-
stmts: vec![init_expr.into_stmt()],
216-
},
193+
obj: ThisExpr::dummy().into(),
194+
prop: prop_name_to_member_prop(key.take()),
195+
}
196+
.into(),
197+
right: value,
217198
}
218199
.into();
219200

220-
return;
221-
} else {
222-
self.constructor_inits.push(init_expr.into());
201+
if *is_static {
202+
*member = StaticBlock {
203+
span: DUMMY_SP,
204+
body: BlockStmt {
205+
span: DUMMY_SP,
206+
stmts: vec![init_expr.into_stmt()],
207+
},
208+
}
209+
.into();
210+
211+
continue;
212+
} else {
213+
constructor_inits.push(init_expr.into());
214+
}
223215
}
224-
}
225216

226-
n.take();
227-
}
228-
ClassMember::PrivateProp(PrivateProp {
229-
ref span,
230-
is_static: false,
231-
key,
232-
value,
233-
..
234-
}) => {
235-
if let Some(value) = value.take() {
236-
let init_expr: Expr = AssignExpr {
237-
span: *span,
238-
op: op!("="),
239-
left: MemberExpr {
240-
span: DUMMY_SP,
241-
obj: ThisExpr::dummy().into(),
242-
prop: MemberProp::PrivateName(key.clone()),
217+
member.take();
218+
}
219+
ClassMember::PrivateProp(PrivateProp {
220+
ref span,
221+
is_static: false,
222+
key,
223+
value,
224+
..
225+
}) => {
226+
if let Some(value) = value.take() {
227+
let init_expr: Expr = AssignExpr {
228+
span: *span,
229+
op: op!("="),
230+
left: MemberExpr {
231+
span: DUMMY_SP,
232+
obj: ThisExpr::dummy().into(),
233+
prop: MemberProp::PrivateName(key.clone()),
234+
}
235+
.into(),
236+
right: value,
243237
}
244-
.into(),
245-
right: value,
246-
}
247-
.into();
238+
.into();
248239

249-
self.constructor_inits.push(init_expr.into());
240+
constructor_inits.push(init_expr.into());
241+
}
250242
}
243+
_ => {}
251244
}
252-
_ => {}
253245
}
254-
}
255-
}
256-
257-
#[derive(Debug, Default)]
258-
struct ConstructorHandler {
259-
has_super: bool,
260-
constructor_inits: Vec<Box<Expr>>,
261-
constructor_found: bool,
262-
}
263246

264-
impl VisitMut for ConstructorHandler {
265-
noop_visit_mut_type!();
247+
if constructor_inits.is_empty() {
248+
return;
249+
}
266250

267-
fn visit_mut_class(&mut self, n: &mut Class) {
268-
if !self.constructor_found {
269-
let mut constructor = default_constructor(self.has_super);
270-
constructor.visit_mut_with(self);
271-
n.body.push(constructor.into());
251+
if let Some(c) = n.iter_mut().find_map(|m| m.as_mut_constructor()) {
252+
inject_after_super(c, constructor_inits.take());
272253
} else {
273-
n.body.visit_mut_children_with(self);
254+
let mut c = default_constructor(self.has_super);
255+
inject_after_super(&mut c, constructor_inits.take());
256+
n.push(c.into());
274257
}
275258
}
276-
277-
fn visit_mut_constructor(&mut self, n: &mut Constructor) {
278-
inject_after_super(n, self.constructor_inits.take());
279-
}
280259
}
281260

282261
#[derive(Debug)]

0 commit comments

Comments
 (0)
Please sign in to comment.