Repository: swc-project/swc (rust)

Issue:
super.method() in extended class in loose mode generates code with infinite recursive loop
### Describe the bug

The playground output, when executed, logs "hola" until the call stack size is exceeded. This does not happen if you turn loose mode off. The bug seems to happen with many browser targets, so it's hard to pin down exactly which transform is problematic here.


### Input code

```typescript
class Superclass {
  doStuff() {
  }
}

class Subclass extends Superclass {
  doStuff() {
    console.log('hola');
    super.doStuff();
  }
}

new Subclass().doStuff()
```


### Config

```json
{
  "jsc": {
    "parser": {
      "syntax": "typescript",
      "tsx": true,
      "importMeta": true
    },
    "transform": {
      "react": {
        "refresh": true
      }
    },
    "target": "es2022",
    "loose": true,
    "minify": {
      "compress": false,
      "mangle": false
    }
  },
  "module": {
    "type": "es6"
  },
  "minify": false,
  "isModule": true,
  "env": {
    "targets": ""
  }
}
```


### Playground link

https://play.swc.rs/?version=1.3.2&code=H4sIAAAAAAAAA0vOSSwuVgguLUgtSgYzq7kUFFLyg0tK09I0NMG8Wq5aLq5kqLokCCO1oiQ1L4WARgWF5Py84vycVL2c%2FHQN9Yz8nER1TWuwRDFInx5cuTXMmrzUcrglGpoIBQC1OJKqpwAAAA%3D%3D&config=H4sIAAAAAAAAA0WOOw7DMAiG78KcocrQIXfIIZCLU1d%2BCUjVKPLdazuuusH%2F%2BOCElxhYTsjIQtwmOaLiBxbQI5MYdllhApUqKe80gQs5sa6keCmluoxRbOLQAExo9BoskzxHqsWQN6oWkMy3ea5Yn5LQDxxcdPZoTZNCrlWBxaKXZmHcPI21oUJ67E04%2B5sdeYfyZ4yek3UErxMU373TH6l4gFK%2B2VlBQgQBAAA%3D

### Expected behavior

I would expect the output code to log `hola` once and then exit. `super.doStuff()` should not cause recursion.

### Actual behavior

`super.doStuff()` seems to call itself.

### Version

1.3.2

### Additional context

_No response_

Pull request:
fix(es/compat): Super method call in loose mode
<!-- Note: CI script will automatically rebase your PR so please do not rebase unless required -->

<!-- Thanks for submitting a pull request! Please provide enough information so that others can review your pull request. -->

**Description:**

<!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? -->

Seems like to be caused by #5576 cc @magic-akari 

**BREAKING CHANGE:**

<!--
If this PR introduces a breaking change, it must contain a notice for it to be included in the CHANGELOG. Add description or remove entirely if not breaking.

You may need to update `MIGRATION.md` for the breaking changes.
-->

**Related issue (if exists):**
closes #5936

Patch:
diff --git a/crates/swc_ecma_transforms_classes/src/super_field.rs b/crates/swc_ecma_transforms_classes/src/super_field.rs
index 0d49fa7575..ffa8aa2095 100644
--- a/crates/swc_ecma_transforms_classes/src/super_field.rs
+++ b/crates/swc_ecma_transforms_classes/src/super_field.rs
@@ -353,18 +353,39 @@ impl<'a> SuperFieldAccessFolder<'a> {
     }
 
     fn super_to_get_call(&mut self, super_token: Span, prop: SuperProp) -> Expr {
-        let proto_arg = self.proto_arg();
+        if self.constant_super {
+            Expr::Member(MemberExpr {
+                span: super_token,
+                obj: Box::new({
+                    let name = self.super_class.clone().unwrap_or_else(|| {
+                        quote_ident!(if self.is_static { "Function" } else { "Object" })
+                    });
+                    // in static default super class is Function.prototype
+                    if self.is_static && self.super_class.is_some() {
+                        Expr::Ident(name)
+                    } else {
+                        name.make_member(quote_ident!("prototype"))
+                    }
+                }),
+                prop: match prop {
+                    SuperProp::Ident(i) => MemberProp::Ident(i),
+                    SuperProp::Computed(c) => MemberProp::Computed(c),
+                },
+            })
+        } else {
+            let proto_arg = self.proto_arg();
 
-        let prop_arg = prop_arg(prop).as_arg();
+            let prop_arg = prop_arg(prop).as_arg();
 
-        let this_arg = self.this_arg(super_token).as_arg();
+            let this_arg = self.this_arg(super_token).as_arg();
 
-        Expr::Call(CallExpr {
-            span: super_token,
-            callee: helper!(get, "get"),
-            args: vec![proto_arg.as_arg(), prop_arg, this_arg],
-            type_args: Default::default(),
-        })
+            Expr::Call(CallExpr {
+                span: super_token,
+                callee: helper!(get, "get"),
+                args: vec![proto_arg.as_arg(), prop_arg, this_arg],
+                type_args: Default::default(),
+            })
+        }
     }
 
     fn super_to_set_call(


Test patch:
diff --git a/crates/swc_ecma_transforms_compat/tests/es2015_classes.rs b/crates/swc_ecma_transforms_compat/tests/es2015_classes.rs
index adb885e5f3..e8258d9943 100644
--- a/crates/swc_ecma_transforms_compat/tests/es2015_classes.rs
+++ b/crates/swc_ecma_transforms_compat/tests/es2015_classes.rs
@@ -6824,11 +6824,11 @@ function Test() {
   var _this;
   woops.super.test();
   _this = _super.call(this);
-  _get(Test.prototype, "test", _this).call(_assertThisInitialized(_this));
+  Foo.prototype.test.call(_assertThisInitialized(_this));
   _this = _super.call(this, ...arguments);
   _this = _super.call(this, "test", ...arguments);
-  _get(Test.prototype, "test", _this).apply(_assertThisInitialized(_this), arguments);
-  _get(Test.prototype, "test", _this).call(_assertThisInitialized(_this), "test", ...arguments);
+  Foo.prototype.test.apply(_assertThisInitialized(_this), arguments);
+  Foo.prototype.test.call(_assertThisInitialized(_this), "test", ...arguments);
   return _this;
 }
 
@@ -6867,8 +6867,8 @@ let Test = /*#__PURE__*/function (Foo) {
   function Test() {
     _classCallCheck(this, Test);
     var _this = _super.call(this);
-    _get(Test.prototype, "test", _this);
-    _get(Test.prototype, "test", _this).whatever;
+    Foo.prototype.test;
+    Foo.prototype.test.whatever;
     return _this;
   }
 
@@ -6912,9 +6912,9 @@ let Test = /*#__PURE__*/function (Foo) {
     _classCallCheck(this, Test);
     var _this = _super.call(this);
 
-    _get(Test.prototype, "test", _this).whatever();
+    Foo.prototype.test.whatever();
 
-    _get(Test.prototype, "test", _this).call(_assertThisInitialized(_this));
+    Foo.prototype.test.call(_assertThisInitialized(_this));
 
     return _this;
   }
@@ -6922,7 +6922,7 @@ let Test = /*#__PURE__*/function (Foo) {
   _createClass(Test, null, [{
     key: "test",
     value: function test() {
-      return _get(Test, "wow", this).call(this);
+      return Foo.wow.call(this);
     }
   }]);
   return Test;
@@ -6958,14 +6958,14 @@ let Test = /*#__PURE__*/function () {
 
   function Test() {
     _classCallCheck(this, Test);
-    _get(Test.prototype, "hasOwnProperty", this).call(this, "test");
-    return _get(Test.prototype, "constructor", this);
+    Object.prototype.hasOwnProperty.call(this, "test");
+    return Object.prototype.constructor;
   }
 
   _createClass(Test, null, [{
     key: "test",
     value: function test() {
-      return _get(Test, "constructor", this);
+      return Function.prototype.constructor;
     }
   }]);
   return Test;
@@ -7373,3 +7373,30 @@ D ??= function D() {
 };
 "#
 );
+
+test_exec!(
+    syntax(),
+    |t| classes(
+        Some(t.comments.clone()),
+        Config {
+            constant_super: true,
+            ..Default::default()
+        }
+    ),
+    issue_5936,
+    "
+class Superclass {
+  doStuff() {
+  }
+}
+
+class Subclass extends Superclass {
+  doStuff() {
+    console.log('hola');
+    super.doStuff();
+  }
+}
+
+expect(() => new Subclass().doStuff()).not.toThrowError()
+"
+);
diff --git a/crates/swc_ecma_transforms_compat/tests/es2022_class_properties.rs b/crates/swc_ecma_transforms_compat/tests/es2022_class_properties.rs
index 9afd597dff..dea265cb1d 100644
--- a/crates/swc_ecma_transforms_compat/tests/es2022_class_properties.rs
+++ b/crates/swc_ecma_transforms_compat/tests/es2022_class_properties.rs
@@ -6023,7 +6023,7 @@ var _B;
 
 class A extends (_B = class B {}) {}
 
-_defineProperty(A, "x", _get(A, "x", A));
+_defineProperty(A, "x", _B.x);
 "#
 );
 
@@ -6051,7 +6051,7 @@ class A extends B {
   }
 }
 
-_defineProperty(A, "foo", _get(A, "bar", A));
+_defineProperty(A, "foo", B.bar);
 "#
 );