Skip to content

Commit

Permalink
fix(es/compat): Handle class fields correctly (#8835)
Browse files Browse the repository at this point in the history
**Related issue:**

- Closes #8830
  • Loading branch information
magic-akari committed Apr 11, 2024
1 parent ea830aa commit 5cc585b
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 100 deletions.
12 changes: 12 additions & 0 deletions crates/swc/tests/fixture/issues-8xxx/8830/input/.swcrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"jsc": {
"target": "es2018",
"parser": {
"syntax": "ecmascript"
},
"transform": {
"useDefineForClassFields": false
}
},
"isModule": true
}
16 changes: 16 additions & 0 deletions crates/swc/tests/fixture/issues-8xxx/8830/input/index.js
Original file line number Diff line number Diff line change
@@ -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";
}
14 changes: 14 additions & 0 deletions crates/swc/tests/fixture/issues-8xxx/8830/output/index.js
Original file line number Diff line number Diff line change
@@ -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
}
}
179 changes: 79 additions & 100 deletions crates/swc_ecma_transforms_compat/src/class_fields_use_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -170,113 +157,105 @@ impl ClassFieldsUseSet {
}
}

#[derive(Debug, Default)]
#[derive(Debug)]
struct FieldsHandler {
constructor_inits: Vec<Box<Expr>>,
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<ClassMember>) {
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<Box<Expr>>,
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)]
Expand Down

0 comments on commit 5cc585b

Please sign in to comment.