Repository: swc-project/swc (rust)

Issue:
Access super member in nested expr transform incorrect
<!--
If you are using swc at work, please considering adding your company to https://swc.rs/users/
If then, your issue will be fixed more quickly.
-->

## Describe the bug
transform `super.foo() + 1` will become `_get(_getPrototypeOf(Extended.prototype), 'getNext', this)() + 1` 
**Input code**
class Base {
	getNext() {
		return this.value++
	}
}

class Extended extends Base {
	getNext() {
		return super.getNext() + 1
	}
}

const ins = new Extended();
ins.getNext();
## Config

```json
{
  "module": {
    "type": "commonjs"
  }
}
```

## Expected behavior
should invoke super class member's function with call
``` js
 _get(_getPrototypeOf(Extended.prototype), "getNext", this).call(this) + 1;
```

**Version**
The version of @swc/core: 1.2.66

## Additional context
the code above will emit an error:
```
TypeError: Cannot read property 'value' of undefined
```

Pull request description:
fix(ecmascript/transforms/classes/super_fields): Fix broken code when transpile super member access
close #1959 

Patch:
diff --git a/ecmascript/transforms/classes/src/super_field.rs b/ecmascript/transforms/classes/src/super_field.rs
index 70a863efd7..6b74e4e4a5 100644
--- a/ecmascript/transforms/classes/src/super_field.rs
+++ b/ecmascript/transforms/classes/src/super_field.rs
@@ -43,23 +43,6 @@ pub struct SuperFieldAccessFolder<'a> {
     pub this_alias_mark: Option<Mark>,
 }
 
-struct SuperCalleeFolder<'a> {
-    vars: &'a mut Vec<VarDeclarator>,
-    class_name: &'a Ident,
-    /// True if we should inject get and
-    inject_get: bool,
-    _inject_set: bool,
-    /// Mark for the `_this`. Used only when folding constructor.
-    constructor_this_mark: Option<Mark>,
-    is_static: bool,
-
-    /// True while folding a function / class.
-    in_nested_scope: bool,
-
-    /// `Some(mark)` if `var this2 = this`is required.
-    this_alias_mark: Option<Mark>,
-}
-
 macro_rules! mark_nested {
     ($name:ident, $T:tt) => {
         fn $name(&mut self, n: $T) -> $T {
@@ -76,15 +59,13 @@ macro_rules! mark_nested {
         }
     };
 }
-
-impl<'a> Fold for SuperCalleeFolder<'a> {
+impl<'a> Fold for SuperFieldAccessFolder<'a> {
     noop_fold_type!();
 
-    fold_only_key!();
+    mark_nested!(fold_function, Function);
+    mark_nested!(fold_class, Class);
 
-    fn fold_seq_expr(&mut self, n: SeqExpr) -> SeqExpr {
-        n
-    }
+    fold_only_key!();
 
     fn fold_expr(&mut self, n: Expr) -> Expr {
         match n {
@@ -101,7 +82,120 @@ impl<'a> Fold for SuperCalleeFolder<'a> {
             _ => {}
         }
 
-        let n = match n {
+        // We pretend method folding mode for while folding injected `_defineProperty`
+        // calls.
+        match n {
+            Expr::Call(CallExpr {
+                callee: ExprOrSuper::Expr(ref expr),
+                ..
+            }) => match &**expr {
+                Expr::Ident(Ident {
+                    sym: js_word!("_defineProperty"),
+                    ..
+                }) => {
+                    let old = self.in_injected_define_property_call;
+                    self.in_injected_define_property_call = true;
+                    let n = n.fold_children_with(self);
+                    self.in_injected_define_property_call = old;
+                    return n;
+                }
+                _ => {}
+            },
+            _ => {}
+        }
+
+        let n = self.fold_super_member_call(n);
+        let n = self.fold_super_member_set(n);
+        let n = self.fold_super_member_get(n);
+
+        n.fold_children_with(self)
+    }
+}
+
+impl<'a> SuperFieldAccessFolder<'a> {
+    /// # In
+    /// ```js
+    /// super.foo(a)
+    /// ```
+    /// # out
+    /// ```js
+    /// _get(_getPrototypeOf(Clazz.prototype), 'foo', this).call(this, a)
+    /// ```
+    fn fold_super_member_call(&mut self, n: Expr) -> Expr {
+        match n {
+            Expr::Call(CallExpr {
+                callee: ExprOrSuper::Expr(ref callee_expr),
+                ref args,
+                ref type_args,
+                ..
+            }) => match &**callee_expr {
+                Expr::Member(MemberExpr {
+                    obj:
+                        ExprOrSuper::Super(Super {
+                            span: super_token, ..
+                        }),
+                    prop,
+                    computed,
+                    ..
+                }) => {
+                    let this = match self.constructor_this_mark {
+                        Some(mark) => quote_ident!(DUMMY_SP.apply_mark(mark), "_this").as_arg(),
+                        _ => ThisExpr { span: DUMMY_SP }.as_arg(),
+                    };
+
+                    let callee = self.super_to_get_call(*super_token, prop.clone(), *computed);
+                    let mut args = args.clone();
+
+                    if args.len() == 1 && is_rest_arguments(&args[0]) {
+                        return Expr::Call(CallExpr {
+                            span: DUMMY_SP,
+                            callee: MemberExpr {
+                                span: DUMMY_SP,
+                                obj: ExprOrSuper::Expr(Box::new(callee)),
+                                prop: Box::new(Expr::Ident(quote_ident!("apply"))),
+                                computed: false,
+                            }
+                            .as_callee(),
+                            args: iter::once(this)
+                                .chain(iter::once({
+                                    let mut arg = args.pop().unwrap();
+                                    arg.spread = None;
+                                    arg
+                                }))
+                                .collect(),
+                            type_args: type_args.clone(),
+                        });
+                    }
+
+                    return Expr::Call(CallExpr {
+                        span: DUMMY_SP,
+                        callee: MemberExpr {
+                            span: DUMMY_SP,
+                            obj: ExprOrSuper::Expr(Box::new(callee)),
+                            prop: Box::new(Expr::Ident(quote_ident!("call"))),
+                            computed: false,
+                        }
+                        .as_callee(),
+                        args: iter::once(this).chain(args).collect(),
+                        type_args: type_args.clone(),
+                    });
+                }
+
+                _ => n,
+            },
+            _ => n,
+        }
+    }
+
+    /// # In
+    /// ```js
+    /// super.foo = bar
+    /// # out
+    /// ```js
+    /// _set(_getPrototypeOf(Clazz.prototype), "foo", bar, this, true)
+    /// ```
+    fn fold_super_member_set(&mut self, n: Expr) -> Expr {
+        match n {
             Expr::Update(UpdateExpr {
                 span,
                 arg,
@@ -132,22 +226,20 @@ impl<'a> Fold for SuperCalleeFolder<'a> {
                         }))),
                     )
                 }
-                _ => {
-                    return Expr::Update(UpdateExpr {
-                        span,
-                        arg,
-                        op,
-                        prefix,
-                    });
-                }
+                _ => Expr::Update(UpdateExpr {
+                    span,
+                    arg,
+                    op,
+                    prefix,
+                }),
             },
+
             Expr::Assign(AssignExpr {
                 span,
                 mut left,
                 op,
                 right,
             }) => {
-                //
                 if let PatOrExpr::Expr(expr) = left {
                     match *expr {
                         Expr::Member(MemberExpr {
@@ -157,14 +249,13 @@ impl<'a> Fold for SuperCalleeFolder<'a> {
                                 }),
                             prop,
                             ..
-                        }) => {
-                            return self.super_to_set_call(super_token, false, prop, op, right);
-                        }
+                        }) => return self.super_to_set_call(super_token, false, prop, op, right),
                         _ => {
                             left = PatOrExpr::Expr(expr);
                         }
                     }
                 }
+
                 if let PatOrExpr::Pat(pat) = left {
                     match *pat {
                         Pat::Expr(expr) => match *expr {
@@ -184,8 +275,7 @@ impl<'a> Fold for SuperCalleeFolder<'a> {
                         },
                         _ => left = PatOrExpr::Pat(pat),
                     }
-                }
-
+                };
                 Expr::Assign(AssignExpr {
                     span,
                     left: left.fold_children_with(self),
@@ -193,15 +283,22 @@ impl<'a> Fold for SuperCalleeFolder<'a> {
                     right,
                 })
             }
-            _ => n.fold_children_with(self),
-        };
+            _ => n,
+        }
+    }
 
+    /// # In
+    /// ```js
+    /// super.foo
+    /// ```
+    /// # out
+    /// ```js
+    /// _get(_getPrototypeOf(Clazz.prototype), 'foo', this)
+    /// ```
+    fn fold_super_member_get(&mut self, n: Expr) -> Expr {
         match n {
             Expr::Member(MemberExpr {
-                obj:
-                    ExprOrSuper::Super(Super {
-                        span: super_token, ..
-                    }),
+                obj: ExprOrSuper::Super(Super { span: super_token }),
                 prop,
                 computed,
                 ..
@@ -210,12 +307,8 @@ impl<'a> Fold for SuperCalleeFolder<'a> {
             _ => n,
         }
     }
-}
 
-impl<'a> SuperCalleeFolder<'a> {
     fn super_to_get_call(&mut self, super_token: Span, prop: Box<Expr>, computed: bool) -> Expr {
-        self.inject_get = true;
-
         let proto_arg = get_prototype_of(if self.is_static {
             // Foo
             Expr::Ident(self.class_name.clone())
@@ -273,8 +366,6 @@ impl<'a> SuperCalleeFolder<'a> {
         op: AssignOp,
         rhs: Box<Expr>,
     ) -> Expr {
-        self._inject_set = true;
-
         let mut ref_ident = alias_ident_for(&rhs, "_ref");
         ref_ident.span = ref_ident.span.apply_mark(Mark::fresh(Mark::root()));
 
@@ -415,119 +506,3 @@ impl<'a> SuperCalleeFolder<'a> {
         }
     }
 }
-
-impl<'a> Fold for SuperFieldAccessFolder<'a> {
-    noop_fold_type!();
-
-    mark_nested!(fold_function, Function);
-    mark_nested!(fold_class, Class);
-
-    fold_only_key!();
-
-    fn fold_expr(&mut self, n: Expr) -> Expr {
-        // We pretend method folding mode for while folding injected `_defineProperty`
-        // calls.
-        match n {
-            Expr::Call(CallExpr {
-                callee: ExprOrSuper::Expr(ref expr),
-                ..
-            }) => match &**expr {
-                Expr::Ident(Ident {
-                    sym: js_word!("_defineProperty"),
-                    ..
-                }) => {
-                    let old = self.in_injected_define_property_call;
-                    self.in_injected_define_property_call = true;
-                    let n = n.fold_children_with(self);
-                    self.in_injected_define_property_call = old;
-                    return n;
-                }
-                _ => {}
-            },
-            _ => {}
-        }
-
-        let mut callee_folder = SuperCalleeFolder {
-            class_name: self.class_name,
-            inject_get: false,
-            _inject_set: false,
-            vars: self.vars,
-            constructor_this_mark: self.constructor_this_mark,
-            is_static: self.is_static,
-            in_nested_scope: self.in_nested_scope,
-            this_alias_mark: self.this_alias_mark,
-        };
-
-        let should_invoke_call = match n {
-            Expr::Call(CallExpr {
-                callee: ExprOrSuper::Expr(ref expr),
-                ..
-            }) => match &**expr {
-                Expr::Member(MemberExpr {
-                    obj: ExprOrSuper::Super(..),
-                    ..
-                }) => true,
-                _ => false,
-            },
-            _ => false,
-        };
-
-        let n = n.fold_with(&mut callee_folder);
-        if callee_folder.this_alias_mark.is_some() {
-            self.this_alias_mark = callee_folder.this_alias_mark;
-        }
-
-        if callee_folder.inject_get && should_invoke_call {
-            match n {
-                Expr::Call(CallExpr {
-                    callee,
-                    mut args,
-                    type_args,
-                    ..
-                }) => {
-                    let this = match self.constructor_this_mark {
-                        Some(mark) => quote_ident!(DUMMY_SP.apply_mark(mark), "_this").as_arg(),
-                        _ => ThisExpr { span: DUMMY_SP }.as_arg(),
-                    };
-
-                    if args.len() == 1 && is_rest_arguments(&args[0]) {
-                        return Expr::Call(CallExpr {
-                            span: DUMMY_SP,
-                            callee: MemberExpr {
-                                span: DUMMY_SP,
-                                obj: callee,
-                                prop: Box::new(Expr::Ident(quote_ident!("apply"))),
-                                computed: false,
-                            }
-                            .as_callee(),
-                            args: iter::once(this)
-                                .chain(iter::once({
-                                    let mut arg = args.pop().unwrap();
-                                    arg.spread = None;
-                                    arg
-                                }))
-                                .collect(),
-                            type_args,
-                        });
-                    }
-
-                    return Expr::Call(CallExpr {
-                        span: DUMMY_SP,
-                        callee: MemberExpr {
-                            span: DUMMY_SP,
-                            obj: callee,
-                            prop: Box::new(Expr::Ident(quote_ident!("call"))),
-                            computed: false,
-                        }
-                        .as_callee(),
-                        args: iter::once(this).chain(args).collect(),
-                        type_args,
-                    });
-                }
-                _ => unreachable!(),
-            }
-        }
-
-        n.fold_children_with(self)
-    }
-}
diff --git a/ecmascript/transforms/proposal/src/decorators.rs b/ecmascript/transforms/proposal/src/decorators.rs
index e6510962f9..77bb106590 100644
--- a/ecmascript/transforms/proposal/src/decorators.rs
+++ b/ecmascript/transforms/proposal/src/decorators.rs
@@ -355,7 +355,7 @@ impl Decorators {
                     vars: &mut vars,
                     constructor_this_mark: None,
                     is_static: method.is_static,
-                    folding_constructor: true,
+                    folding_constructor: false,
                     in_nested_scope: false,
                     in_injected_define_property_call: false,
                     this_alias_mark: None,


Test patch:
diff --git a/ecmascript/transforms/compat/tests/es2015_classes.rs b/ecmascript/transforms/compat/tests/es2015_classes.rs
index 6fcfef063c..5005bae76d 100644
--- a/ecmascript/transforms/compat/tests/es2015_classes.rs
+++ b/ecmascript/transforms/compat/tests/es2015_classes.rs
@@ -3695,7 +3695,7 @@ function (Hello1) {
     _classCallCheck(this, Outer);
     var _this = _possibleConstructorReturn(this, _getPrototypeOf(Outer).call(this));
     var Inner = {
-      [_get(_getPrototypeOf(Outer.prototype), 'toString', _assertThisInitialized(_this))()] () {
+      [_get(_getPrototypeOf(Outer.prototype), 'toString', _assertThisInitialized(_this)).call(_this)] () {
         return 'hello';
       }
 
@@ -6444,3 +6444,95 @@ test!(
     }
     "
 );
+
+test!(
+    syntax(),
+    |t| {
+        let global_mark = Mark::fresh(Mark::root());
+
+        chain!(
+            class_properties(),
+            es2015::es2015(
+                global_mark,
+                Some(t.comments.clone()),
+                es2015::Config {
+                    ..Default::default()
+                }
+            ),
+        )
+    },
+    issue_1959_1,
+    "
+    class Extended extends Base {
+      getNext() {
+        return super.getNext(114514) + 114514
+      }
+    }
+    ",
+    "
+var Extended = function(Base) {
+    'use strict';
+    _inherits(Extended, Base);
+    function Extended() {
+        _classCallCheck(this, Extended);
+        return _possibleConstructorReturn(this, _getPrototypeOf(Extended).apply(this, arguments));
+    }
+    _createClass(Extended, [
+        {
+            key: 'getNext',
+            value: function getNext() {
+                return _get(_getPrototypeOf(Extended.prototype), 'getNext', this).call(this, \
+     114514) + 114514;
+            }
+        }
+    ]);
+    return Extended;
+}(Base);
+    "
+);
+
+test!(
+    syntax(),
+    |t| {
+        let global_mark = Mark::fresh(Mark::root());
+
+        chain!(
+            class_properties(),
+            es2015::es2015(
+                global_mark,
+                Some(t.comments.clone()),
+                es2015::Config {
+                    ..Default::default()
+                }
+            ),
+        )
+    },
+    issue_1959_2,
+    "
+    class Extended extends Base {
+      getNext() {
+        return super.getNext(114514)
+      }
+    }
+    ",
+    "
+var Extended = function(Base) {
+    'use strict';
+    _inherits(Extended, Base);
+    function Extended() {
+        _classCallCheck(this, Extended);
+        return _possibleConstructorReturn(this, _getPrototypeOf(Extended).apply(this, arguments));
+    }
+    _createClass(Extended, [
+        {
+            key: 'getNext',
+            value: function getNext() {
+                return _get(_getPrototypeOf(Extended.prototype), 'getNext', this).call(this, \
+     114514);
+            }
+        }
+    ]);
+    return Extended;
+}(Base);
+    "
+);
diff --git a/ecmascript/transforms/compat/tests/es2020_class_properties.rs b/ecmascript/transforms/compat/tests/es2020_class_properties.rs
index 6de873639c..ddd802b0cb 100644
--- a/ecmascript/transforms/compat/tests/es2020_class_properties.rs
+++ b/ecmascript/transforms/compat/tests/es2020_class_properties.rs
@@ -1791,7 +1791,7 @@ function (A1) {
 
     _foo.set(_assertThisInitialized(_this), {
       writable: true,
-      value: _get(_getPrototypeOf(B.prototype), "foo", _assertThisInitialized(_this))()
+      value: _get(_getPrototypeOf(B.prototype), "foo", _assertThisInitialized(_this)).call(_this)
     });
 
     return _this;