Repository: swc-project/swc (rust)

Issue:
`arguments` is not transform correct in async function
### Describe the bug

After the code transform, it breaks the original behavior.

### Input code

```typescript
var foo = {
    async bar({ name }) {
        console.log('arguments.length', arguments.length)
    }
}

foo.bar({})
```

### Config

```json

```

### Playground link (or link to the minimal reproduction)

https://play.swc.rs/?version=1.11.7&code=H4sIAAAAAAAAAytLLFJIy89XsFWo5lIAgsTiyrxkhaTEIo1qhbzE3FSFWk2oDAgk5%2BcV5%2Bek6uXkp2uoJxall%2Bam5pUU6%2BWk5qWXZKjrKKALaYJ11nLVcnEBLdEDG1urCQBsC3CYdAAAAA%3D%3D&config=H4sIAAAAAAAAA1WPSw7DIAxE95wCed1tN71DD2FRJwLxEyZSUZS7Fyikzc4z8%2ByRdyElGFbwkHsdq4iYmNKpq8PFZ3xXB0g5ZJV0zHCbqeEWLWiZunV8E8iYVsp9i%2B8DBxsC08SH57TXS%2FkvVMHFRMxXsKHoV0vXOjEqwYXX1sPxSC6xqXbNBW8YfuRsPK%2BD5udcz2kjcXwAmJf30BkBAAA%3D

### SWC Info output

_No response_

### Expected behavior

Output: 1

The `arguments` should get a correct value

### Actual behavior

Output: 0

The `arguments` should be correct

### Version

1.11.7

### Additional context

Note: once remove `async`, it works fine like expect

<img width="1239" alt="Image" src="https://github.com/user-attachments/assets/b3b56eb0-fce8-495a-a8f2-58310faf59bd" />

Pull request description:
fix(es/compat): Hoist `arguments` in object method while lowering async functions
**Related issue:**

- Closes #10149



Patch:
diff --git a/.changeset/foo-bar.md b/.changeset/foo-bar.md
new file mode 100644
index 0000000000..4da5351b9c
--- /dev/null
+++ b/.changeset/foo-bar.md
@@ -0,0 +1,6 @@
+---
+swc_ecma_compat_es2017: patch
+swc_core: patch
+---
+
+fix(es/compat): Hoist arguments in object method while lowering async functions
diff --git a/crates/swc_ecma_compat_es2017/src/async_to_generator.rs b/crates/swc_ecma_compat_es2017/src/async_to_generator.rs
index 2592c8eac6..cb4344fe28 100644
--- a/crates/swc_ecma_compat_es2017/src/async_to_generator.rs
+++ b/crates/swc_ecma_compat_es2017/src/async_to_generator.rs
@@ -6,7 +6,7 @@ use swc_ecma_ast::*;
 use swc_ecma_transforms_base::{helper, helper_expr, perf::Check};
 use swc_ecma_transforms_macros::fast_path;
 use swc_ecma_utils::{
-    contains_this_expr, find_pat_ids,
+    find_pat_ids,
     function::{init_this, FnEnvHoister, FnWrapperResult, FunctionWrapper},
     prepend_stmt, private_ident, quote_ident, ExprFactory, Remapper, StmtLike,
 };
@@ -339,54 +339,52 @@ impl VisitMut for Actual {
             return;
         }
 
-        let is_this_used = contains_this_expr(&prop.function.body);
-
-        let original_fn_params = prop.function.params.take();
-
-        let prop_method_span = prop.function.span;
-        let prop_method_body_span = if let Some(body) = &prop.function.body {
-            body.span
-        } else {
-            DUMMY_SP
+        let Function {
+            span,
+            params,
+            body: Some(body),
+            ..
+        } = &mut *prop.function
+        else {
+            return;
         };
-        prop.function.span = prop_method_body_span;
 
-        let fn_ref = make_fn_ref(
-            Function {
-                params: Vec::new(),
-                ..*prop.function.take()
-            }
-            .into(),
-        );
+        let span = *span;
+        let params = params.take();
+        let mut visitor = FnEnvHoister::new(self.unresolved_ctxt);
+        body.visit_mut_with(&mut visitor);
 
-        let fn_ref = if is_this_used {
-            fn_ref.apply(DUMMY_SP, ThisExpr { span: DUMMY_SP }.into(), Vec::new())
-        } else {
-            CallExpr {
-                span: DUMMY_SP,
-                callee: fn_ref.as_callee(),
-                args: Vec::new(),
-                ..Default::default()
-            }
-            .into()
-        };
+        let expr = make_fn_ref(FnExpr {
+            ident: None,
+            function: prop.function.take(),
+        });
 
         prop.function = Function {
-            params: original_fn_params,
-            span: prop_method_span,
-            is_async: false,
-            is_generator: false,
+            span,
+            params,
             body: Some(BlockStmt {
-                span: DUMMY_SP,
-                stmts: vec![Stmt::Return(ReturnStmt {
-                    span: DUMMY_SP,
-                    arg: Some(Box::new(fn_ref)),
-                })],
+                stmts: visitor
+                    .to_stmt()
+                    .into_iter()
+                    .chain(iter::once(
+                        ReturnStmt {
+                            span: DUMMY_SP,
+                            arg: Some(
+                                CallExpr {
+                                    callee: expr.as_callee(),
+                                    ..Default::default()
+                                }
+                                .into(),
+                            ),
+                        }
+                        .into(),
+                    ))
+                    .collect(),
                 ..Default::default()
             }),
             ..Default::default()
         }
-        .into()
+        .into();
     }
 
     fn visit_mut_stmts(&mut self, _n: &mut Vec<Stmt>) {}


Test patch:
diff --git a/crates/swc/tests/fixture/issues-10xxx/10149/1/input/.swcrc b/crates/swc/tests/fixture/issues-10xxx/10149/1/input/.swcrc
new file mode 100644
index 0000000000..9dc1b39f49
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-10xxx/10149/1/input/.swcrc
@@ -0,0 +1,11 @@
+{
+    "jsc": {
+        "parser": {
+            "syntax": "ecmascript",
+            "jsx": false
+        },
+        "target": "es5"
+    },
+    "minify": false,
+    "isModule": true
+}
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-10xxx/10149/1/input/index.ts b/crates/swc/tests/fixture/issues-10xxx/10149/1/input/index.ts
new file mode 100644
index 0000000000..142cdfd37e
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-10xxx/10149/1/input/index.ts
@@ -0,0 +1,15 @@
+var foo = {
+    async bar({ name }) {
+        console.log("arguments.length", arguments.length);
+    },
+};
+
+class Foo {
+    async bar({ name }) {
+        console.log("arguments.length", arguments.length);
+    }
+}
+
+async function bar({ name }) {
+    console.log("arguments.length", arguments.length);
+}
diff --git a/crates/swc/tests/fixture/issues-10xxx/10149/1/output/index.ts b/crates/swc/tests/fixture/issues-10xxx/10149/1/output/index.ts
new file mode 100644
index 0000000000..547bae7afd
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-10xxx/10149/1/output/index.ts
@@ -0,0 +1,59 @@
+var _async_to_generator = require("@swc/helpers/_/_async_to_generator");
+var _class_call_check = require("@swc/helpers/_/_class_call_check");
+var _create_class = require("@swc/helpers/_/_create_class");
+var _ts_generator = require("@swc/helpers/_/_ts_generator");
+var foo = {
+    bar: function bar(param) {
+        var name = param.name;
+        var _arguments = arguments;
+        return _async_to_generator._(function() {
+            return _ts_generator._(this, function(_state) {
+                console.log("arguments.length", _arguments.length);
+                return [
+                    2
+                ];
+            });
+        })();
+    }
+};
+var Foo = /*#__PURE__*/ function() {
+    "use strict";
+    function Foo() {
+        _class_call_check._(this, Foo);
+    }
+    _create_class._(Foo, [
+        {
+            key: "bar",
+            value: function bar(param) {
+                var name = param.name;
+                var _arguments = arguments;
+                return _async_to_generator._(function() {
+                    return _ts_generator._(this, function(_state) {
+                        console.log("arguments.length", _arguments.length);
+                        return [
+                            2
+                        ];
+                    });
+                })();
+            }
+        }
+    ]);
+    return Foo;
+}();
+function bar(_) {
+    return _bar.apply(this, arguments);
+}
+function _bar() {
+    _bar = _async_to_generator._(function(param) {
+        var name;
+        var _arguments = arguments;
+        return _ts_generator._(this, function(_state) {
+            name = param.name;
+            console.log("arguments.length", _arguments.length);
+            return [
+                2
+            ];
+        });
+    });
+    return _bar.apply(this, arguments);
+}
diff --git a/crates/swc/tests/fixture/issues-10xxx/10149/2/input/.swcrc b/crates/swc/tests/fixture/issues-10xxx/10149/2/input/.swcrc
new file mode 100644
index 0000000000..ffca5838e1
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-10xxx/10149/2/input/.swcrc
@@ -0,0 +1,11 @@
+{
+    "jsc": {
+        "parser": {
+            "syntax": "ecmascript",
+            "jsx": false
+        },
+        "target": "es2016"
+    },
+    "minify": false,
+    "isModule": true
+}
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-10xxx/10149/2/input/index.ts b/crates/swc/tests/fixture/issues-10xxx/10149/2/input/index.ts
new file mode 100644
index 0000000000..142cdfd37e
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-10xxx/10149/2/input/index.ts
@@ -0,0 +1,15 @@
+var foo = {
+    async bar({ name }) {
+        console.log("arguments.length", arguments.length);
+    },
+};
+
+class Foo {
+    async bar({ name }) {
+        console.log("arguments.length", arguments.length);
+    }
+}
+
+async function bar({ name }) {
+    console.log("arguments.length", arguments.length);
+}
diff --git a/crates/swc/tests/fixture/issues-10xxx/10149/2/output/index.ts b/crates/swc/tests/fixture/issues-10xxx/10149/2/output/index.ts
new file mode 100644
index 0000000000..c2e1f9045a
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-10xxx/10149/2/output/index.ts
@@ -0,0 +1,26 @@
+var _async_to_generator = require("@swc/helpers/_/_async_to_generator");
+var foo = {
+    bar ({ name }) {
+        var _arguments = arguments;
+        return _async_to_generator._(function*() {
+            console.log("arguments.length", _arguments.length);
+        })();
+    }
+};
+class Foo {
+    bar({ name }) {
+        var _arguments = arguments;
+        return _async_to_generator._(function*() {
+            console.log("arguments.length", _arguments.length);
+        })();
+    }
+}
+function bar(_) {
+    return _bar.apply(this, arguments);
+}
+function _bar() {
+    _bar = _async_to_generator._(function*({ name }) {
+        console.log("arguments.length", arguments.length);
+    });
+    return _bar.apply(this, arguments);
+}
diff --git a/crates/swc/tests/fixture/issues-1xxx/1575/case1/output/index.js b/crates/swc/tests/fixture/issues-1xxx/1575/case1/output/index.js
index 8dbdd76d86..4999f07224 100644
--- a/crates/swc/tests/fixture/issues-1xxx/1575/case1/output/index.js
+++ b/crates/swc/tests/fixture/issues-1xxx/1575/case1/output/index.js
@@ -3,14 +3,15 @@ var _ts_generator = require("@swc/helpers/_/_ts_generator");
 Vue.component("test", {
     methods: {
         onSend: function onSend() {
+            var _this = this;
             return _async_to_generator._(function() {
                 return _ts_generator._(this, function(_state) {
-                    if (this.msg === "") {}
+                    if (_this.msg === "") {}
                     return [
                         2
                     ];
                 });
-            }).apply(this);
+            })();
         }
     }
 });
diff --git a/crates/swc/tests/fixture/issues-1xxx/1575/case2/output/index.js b/crates/swc/tests/fixture/issues-1xxx/1575/case2/output/index.js
index 2404710665..837132a84a 100644
--- a/crates/swc/tests/fixture/issues-1xxx/1575/case2/output/index.js
+++ b/crates/swc/tests/fixture/issues-1xxx/1575/case2/output/index.js
@@ -3,14 +3,15 @@ var _ts_generator = require("@swc/helpers/_/_ts_generator");
 var obj = {
     foo: 5,
     method: function method() {
+        var _this = this;
         return _async_to_generator._(function() {
             return _ts_generator._(this, function(_state) {
                 return [
                     2,
-                    this.foo
+                    _this.foo
                 ];
             });
-        }).apply(this);
+        })();
     }
 };
 obj.method().then(function(v) {