diff --git a/crates/swc/tests/fixture/issues-8xxx/8830/input/.swcrc b/crates/swc/tests/fixture/issues-8xxx/8830/input/.swcrc new file mode 100644 index 000000000000..743551ad27e8 --- /dev/null +++ b/crates/swc/tests/fixture/issues-8xxx/8830/input/.swcrc @@ -0,0 +1,12 @@ +{ + "jsc": { + "target": "es2018", + "parser": { + "syntax": "ecmascript" + }, + "transform": { + "useDefineForClassFields": false + } + }, + "isModule": true +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-8xxx/8830/input/index.js b/crates/swc/tests/fixture/issues-8xxx/8830/input/index.js new file mode 100644 index 000000000000..8aa61bfa737b --- /dev/null +++ b/crates/swc/tests/fixture/issues-8xxx/8830/input/index.js @@ -0,0 +1,16 @@ +class MyClass { + // also same behavior for static methods + methodReturningClass() { + return class SomeOtherClass { + constructor() { + // Assignment to foo goes here + } + }; + } + + constructor(args) { + // Assignment to foo should go here + } + + foo = "bar"; +} diff --git a/crates/swc/tests/fixture/issues-8xxx/8830/output/index.js b/crates/swc/tests/fixture/issues-8xxx/8830/output/index.js new file mode 100644 index 000000000000..a7611a163b0e --- /dev/null +++ b/crates/swc/tests/fixture/issues-8xxx/8830/output/index.js @@ -0,0 +1,14 @@ +class MyClass { + // also same behavior for static methods + methodReturningClass() { + return class SomeOtherClass { + constructor(){ + // Assignment to foo goes here + } + }; + } + constructor(args){ + this.foo = "bar"; + // Assignment to foo should go here + } +} diff --git a/crates/swc_ecma_transforms_compat/src/class_fields_use_set.rs b/crates/swc_ecma_transforms_compat/src/class_fields_use_set.rs index 7fb3da66e4c2..345877a132b7 100644 --- a/crates/swc_ecma_transforms_compat/src/class_fields_use_set.rs +++ b/crates/swc_ecma_transforms_compat/src/class_fields_use_set.rs @@ -104,27 +104,14 @@ impl VisitMut for ClassFieldsUseSet { } fn visit_mut_class(&mut self, n: &mut Class) { + // visit inner classes first n.visit_mut_children_with(self); - let mut fields_handler = FieldsHandler::default(); - n.visit_mut_with(&mut fields_handler); - - let FieldsHandler { - constructor_inits, - constructor_found, - .. - } = fields_handler; - - if constructor_inits.is_empty() { - return; - } - - let mut constructor_handler = ConstructorHandler { + let mut fields_handler = FieldsHandler { has_super: n.super_class.is_some(), - constructor_inits, - constructor_found, }; - n.visit_mut_with(&mut constructor_handler); + + n.body.visit_mut_with(&mut fields_handler); } } @@ -170,113 +157,105 @@ impl ClassFieldsUseSet { } } -#[derive(Debug, Default)] +#[derive(Debug)] struct FieldsHandler { - constructor_inits: Vec>, - constructor_found: bool, + has_super: bool, } impl VisitMut for FieldsHandler { noop_visit_mut_type!(); - fn visit_mut_class(&mut self, n: &mut Class) { - n.body.visit_mut_with(self); + fn visit_mut_class(&mut self, _: &mut Class) { + // skip inner classes + // In fact, FieldsHandler does not visit children recursively. + // We call FieldsHandler with the class.body as the only entry point. + // The FieldsHandler actually operates in a iterative way. } - fn visit_mut_class_member(&mut self, n: &mut ClassMember) { - match n { - ClassMember::Constructor(..) => self.constructor_found = true, - ClassMember::ClassProp(ClassProp { - ref span, - ref is_static, - key, - value, - .. - }) => { - if let Some(value) = value.take() { - let init_expr: Expr = AssignExpr { - span: *span, - op: op!("="), - left: MemberExpr { - span: DUMMY_SP, - obj: ThisExpr::dummy().into(), - prop: prop_name_to_member_prop(key.take()), - } - .into(), - right: value, - } - .into(); - - if *is_static { - *n = StaticBlock { - span: DUMMY_SP, - body: BlockStmt { + fn visit_mut_class_members(&mut self, n: &mut Vec) { + let mut constructor_inits = vec![]; + + for member in n.iter_mut() { + match member { + ClassMember::ClassProp(ClassProp { + ref span, + ref is_static, + key, + value, + .. + }) => { + if let Some(value) = value.take() { + let init_expr: Expr = AssignExpr { + span: *span, + op: op!("="), + left: MemberExpr { span: DUMMY_SP, - stmts: vec![init_expr.into_stmt()], - }, + obj: ThisExpr::dummy().into(), + prop: prop_name_to_member_prop(key.take()), + } + .into(), + right: value, } .into(); - return; - } else { - self.constructor_inits.push(init_expr.into()); + if *is_static { + *member = StaticBlock { + span: DUMMY_SP, + body: BlockStmt { + span: DUMMY_SP, + stmts: vec![init_expr.into_stmt()], + }, + } + .into(); + + continue; + } else { + constructor_inits.push(init_expr.into()); + } } - } - n.take(); - } - ClassMember::PrivateProp(PrivateProp { - ref span, - is_static: false, - key, - value, - .. - }) => { - if let Some(value) = value.take() { - let init_expr: Expr = AssignExpr { - span: *span, - op: op!("="), - left: MemberExpr { - span: DUMMY_SP, - obj: ThisExpr::dummy().into(), - prop: MemberProp::PrivateName(key.clone()), + member.take(); + } + ClassMember::PrivateProp(PrivateProp { + ref span, + is_static: false, + key, + value, + .. + }) => { + if let Some(value) = value.take() { + let init_expr: Expr = AssignExpr { + span: *span, + op: op!("="), + left: MemberExpr { + span: DUMMY_SP, + obj: ThisExpr::dummy().into(), + prop: MemberProp::PrivateName(key.clone()), + } + .into(), + right: value, } - .into(), - right: value, - } - .into(); + .into(); - self.constructor_inits.push(init_expr.into()); + constructor_inits.push(init_expr.into()); + } } + _ => {} } - _ => {} } - } -} - -#[derive(Debug, Default)] -struct ConstructorHandler { - has_super: bool, - constructor_inits: Vec>, - constructor_found: bool, -} -impl VisitMut for ConstructorHandler { - noop_visit_mut_type!(); + if constructor_inits.is_empty() { + return; + } - fn visit_mut_class(&mut self, n: &mut Class) { - if !self.constructor_found { - let mut constructor = default_constructor(self.has_super); - constructor.visit_mut_with(self); - n.body.push(constructor.into()); + if let Some(c) = n.iter_mut().find_map(|m| m.as_mut_constructor()) { + inject_after_super(c, constructor_inits.take()); } else { - n.body.visit_mut_children_with(self); + let mut c = default_constructor(self.has_super); + inject_after_super(&mut c, constructor_inits.take()); + n.push(c.into()); } } - - fn visit_mut_constructor(&mut self, n: &mut Constructor) { - inject_after_super(n, self.constructor_inits.take()); - } } #[derive(Debug)]