Repository: swc-project/swc (rust)

Issue:
`swc` `explicitResourceManagement` `SuppressedError` implement does not obey spec
### Describe the bug

`swc` `explicitResourceManagement` `SuppressedError` implement does not obey spec.

`swc` runtime output is different from `tsc` runtime output.


### Input code

```typescript
import { deepStrictEqual } from 'node:assert'
import process from 'node:process'

let i = 0
let err: any
try {
  await using _x1 = {
    async [Symbol.asyncDispose]() {
      throw [1, ++i]
    }
  }

  await using _x2 = {
    async [Symbol.asyncDispose]() {
      throw [2, ++i]
    }
  }

  await using _x3 = {
    async [Symbol.asyncDispose]() {
      throw [3, ++i]
    }
  }

  await using _x4 = {
    async [Symbol.asyncDispose]() {
      throw [4, ++i]
    }
  }

  throw [5, ++i]
} catch (e) {
  err = e
}

if (process.env['REPRODUCE_SWC_BUG'] === undefined) {
  deepStrictEqual(err.error, [1, 5])
  deepStrictEqual(err.suppressed.error, [2, 4])
  deepStrictEqual(err.suppressed.suppressed.error, [3, 3])
  deepStrictEqual(err.suppressed.suppressed.suppressed.error, [4, 2])
  deepStrictEqual(err.suppressed.suppressed.suppressed.suppressed, [5, 1])
} else {
  deepStrictEqual(err.suppressed, [1, 5])
  deepStrictEqual(err.error.suppressed, [2, 4])
  deepStrictEqual(err.error.error.suppressed, [3, 3])
  deepStrictEqual(err.error.error.error.suppressed, [4, 2])
  deepStrictEqual(err.error.error.error.error, [5, 1])
}
```

### Config

```json
{
    "$schema": "https://json.schemastore.org/swcrc",
    "jsc": {
        "parser": {
            "syntax": "typescript",
            "explicitResourceManagement": true,
        },
        "target": "es2022",
        "loose": false,
        "minify": {
            "compress": false,
            "mangle": false
        }
    },
    "module": {
        "type": "es6"
    },
    "minify": false,
    "isModule": true
}
```


### Playground link (or link to the minimal reproduction)

https://github.com/swc-project/swc/issues/8853

### SWC Info output

_No response_

### Expected behavior

-  If `tsc` is correct, expect same output as `tsc`.

### Actual behavior

```sh
$ yarn tsc --build
...
$ yarn swc ./reproduce.mts -o reproduce/reproduce.swc.mjs
...
$ node ./reproduce/reproduce.tsc.mjs && REPRODUCE_SWC_BUG=1 node ./reproduce/reproduce.swc.mjs && echo OK
OK
```

### Version

0.3.12

### Additional context

@swc/cli: 0.3.12
@swc/core: 1.4.13

Downstream

https://github.com/denoland/deno/issues/23249

Pull request description:
fix(es/proposal): Update explicit resource management to match spec
**Description:**


**Related issue:**

 - Closes #8853

Patch:
diff --git a/crates/swc_ecma_transforms_base/src/helpers/_using_ctx.js b/crates/swc_ecma_transforms_base/src/helpers/_using_ctx.js
new file mode 100644
index 0000000000..5076a16cd8
--- /dev/null
+++ b/crates/swc_ecma_transforms_base/src/helpers/_using_ctx.js
@@ -0,0 +1,76 @@
+function _using_ctx() {
+    var _disposeSuppressedError =
+        typeof SuppressedError === "function"
+            ? // eslint-disable-next-line no-undef
+            SuppressedError
+            : (function (error, suppressed) {
+                var err = new Error();
+                err.name = "SuppressedError";
+                err.suppressed = suppressed;
+                err.error = error;
+                return err;
+            }),
+        empty = {},
+        stack = [];
+    function using(isAwait, value) {
+        if (value != null) {
+            if (Object(value) !== value) {
+                throw new TypeError(
+                    "using declarations can only be used with objects, functions, null, or undefined.",
+                );
+            }
+            // core-js-pure uses Symbol.for for polyfilling well-known symbols
+            if (isAwait) {
+                var dispose =
+                    value[Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")];
+            }
+            if (dispose == null) {
+                dispose = value[Symbol.dispose || Symbol.for("Symbol.dispose")];
+            }
+            if (typeof dispose !== "function") {
+                throw new TypeError(`Property [Symbol.dispose] is not a function.`);
+            }
+            stack.push({ v: value, d: dispose, a: isAwait });
+        } else if (isAwait) {
+            // provide the nullish `value` as `d` for minification gain
+            stack.push({ d: value, a: isAwait });
+        }
+        return value;
+    }
+    return {
+        // error
+        e: empty,
+        // using
+        u: using.bind(null, false),
+        // await using
+        a: using.bind(null, true),
+        // dispose
+        d: function () {
+            var error = this.e;
+
+            function next() {
+                // eslint-disable-next-line @typescript-eslint/no-use-before-define
+                while ((resource = stack.pop())) {
+                    try {
+                        var resource,
+                            disposalResult = resource.d && resource.d.call(resource.v);
+                        if (resource.a) {
+                            return Promise.resolve(disposalResult).then(next, err);
+                        }
+                    } catch (e) {
+                        return err(e);
+                    }
+                }
+                if (error !== empty) throw error;
+            }
+
+            function err(e) {
+                error = error !== empty ? new _disposeSuppressedError(error, e) : e;
+
+                return next();
+            }
+
+            return next();
+        },
+    };
+}
\ No newline at end of file
diff --git a/crates/swc_ecma_transforms_base/src/helpers/mod.rs b/crates/swc_ecma_transforms_base/src/helpers/mod.rs
index ed966b27e2..691fddef79 100644
--- a/crates/swc_ecma_transforms_base/src/helpers/mod.rs
+++ b/crates/swc_ecma_transforms_base/src/helpers/mod.rs
@@ -384,6 +384,7 @@ define_helpers!(Helpers {
     identity: (),
     dispose: (),
     using: (),
+    using_ctx: (),
 });
 
 pub fn inject_helpers(global_mark: Mark) -> impl Fold + VisitMut {
diff --git a/crates/swc_ecma_transforms_proposal/src/explicit_resource_management.rs b/crates/swc_ecma_transforms_proposal/src/explicit_resource_management.rs
index 625c03b91c..5dbc11d28f 100644
--- a/crates/swc_ecma_transforms_proposal/src/explicit_resource_management.rs
+++ b/crates/swc_ecma_transforms_proposal/src/explicit_resource_management.rs
@@ -1,9 +1,9 @@
-use std::iter::once;
-
 use swc_common::{util::take::Take, DUMMY_SP};
 use swc_ecma_ast::*;
 use swc_ecma_transforms_base::helper;
-use swc_ecma_utils::{find_pat_ids, private_ident, ExprFactory, ModuleItemLike, StmtLike};
+use swc_ecma_utils::{
+    find_pat_ids, private_ident, quote_ident, ExprFactory, ModuleItemLike, StmtLike,
+};
 use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith};
 
 pub fn explicit_resource_management() -> impl Fold + VisitMut {
@@ -18,9 +18,7 @@ struct ExplicitResourceManagement {
 }
 
 struct State {
-    stack: Ident,
-    has_error: Ident,
-    error_var: Ident,
+    using_ctx: Ident,
     catch_var: Ident,
 
     has_await: bool,
@@ -29,9 +27,7 @@ struct State {
 impl Default for State {
     fn default() -> Self {
         Self {
-            stack: private_ident!("_stack"),
-            has_error: private_ident!("_hasError"),
-            error_var: private_ident!("_error"),
+            using_ctx: private_ident!("_usingCtx"),
             catch_var: private_ident!("_"),
             has_await: false,
         }
@@ -63,13 +59,15 @@ impl ExplicitResourceManagement {
         let mut extras = vec![];
         let mut try_body = vec![];
 
-        let stack_var_decl = VarDeclarator {
+        let using_ctx_var = VarDeclarator {
             span: DUMMY_SP,
-            name: state.stack.clone().into(),
+            name: state.using_ctx.clone().into(),
             init: Some(
-                ArrayLit {
+                CallExpr {
+                    callee: helper!(using_ctx),
                     span: DUMMY_SP,
-                    elems: vec![],
+                    args: Default::default(),
+                    type_args: Default::default(),
                 }
                 .into(),
             ),
@@ -80,7 +78,7 @@ impl ExplicitResourceManagement {
             span: DUMMY_SP,
             kind: VarDeclKind::Var,
             declare: false,
-            decls: vec![stack_var_decl],
+            decls: vec![using_ctx_var],
         }))));
 
         for stmt in stmts.take() {
@@ -88,8 +86,8 @@ impl ExplicitResourceManagement {
                 Ok(stmt @ Stmt::Decl(Decl::Fn(..))) => {
                     new.push(T::from_stmt(stmt));
                 }
-                Ok(Stmt::Decl(Decl::Var(mut var))) => {
-                    var.kind = VarDeclKind::Var;
+                Ok(Stmt::Decl(Decl::Var(var))) => {
+                    // var.kind = VarDeclKind::Var;
                     try_body.push(Stmt::Decl(Decl::Var(var)));
                 }
                 Ok(stmt) => try_body.push(stmt),
@@ -310,39 +308,28 @@ impl ExplicitResourceManagement {
         // Drop `;`
         try_body.retain(|stmt| !matches!(stmt, Stmt::Empty(..)));
 
-        // var error = $catch_var
-        let error_catch_var = Stmt::Decl(Decl::Var(Box::new(VarDecl {
-            span: DUMMY_SP,
-            kind: VarDeclKind::Var,
-            declare: false,
-            decls: vec![VarDeclarator {
-                span: DUMMY_SP,
-                name: state.error_var.clone().into(),
-                init: Some(state.catch_var.clone().into()),
-                definite: false,
-            }],
-        })));
-
-        // var has_error = true
-        let has_error_true = Stmt::Decl(Decl::Var(Box::new(VarDecl {
+        // usingCtx.e = $catch_var
+        let assign_error = AssignExpr {
             span: DUMMY_SP,
-            kind: VarDeclKind::Var,
-            declare: false,
-            decls: vec![VarDeclarator {
-                span: DUMMY_SP,
-                name: state.has_error.clone().into(),
-                init: Some(true.into()),
-                definite: false,
-            }],
-        })));
+            op: op!("="),
+            left: state
+                .using_ctx
+                .clone()
+                .make_member(quote_ident!("e"))
+                .into(),
+            right: state.catch_var.clone().into(),
+        }
+        .into_stmt();
+
+        // _usingCtx.d()
         let dispose_expr = CallExpr {
             span: DUMMY_SP,
-            callee: helper!(dispose),
-            args: vec![
-                state.stack.as_arg(),
-                state.error_var.as_arg(),
-                state.has_error.as_arg(),
-            ],
+            callee: state
+                .using_ctx
+                .clone()
+                .make_member(quote_ident!("d"))
+                .as_callee(),
+            args: vec![],
             type_args: Default::default(),
         };
         let dispose_stmt = if state.has_await {
@@ -366,7 +353,7 @@ impl ExplicitResourceManagement {
                 param: Some(state.catch_var.into()),
                 body: BlockStmt {
                     span: DUMMY_SP,
-                    stmts: vec![error_catch_var, has_error_true],
+                    stmts: vec![assign_error],
                 },
             }),
             finalizer: Some(BlockStmt {
@@ -434,15 +421,16 @@ impl VisitMut for ExplicitResourceManagement {
                     .map(|d| {
                         let init = CallExpr {
                             span: decl.span,
-                            callee: helper!(using),
-                            args: once(state.stack.clone().as_arg())
-                                .chain(once(d.init.unwrap().as_arg()))
-                                .chain(if decl.is_await {
-                                    Some(true.as_arg())
+                            callee: state
+                                .using_ctx
+                                .clone()
+                                .make_member(if decl.is_await {
+                                    quote_ident!("a")
                                 } else {
-                                    None
+                                    quote_ident!("u")
                                 })
-                                .collect(),
+                                .as_callee(),
+                            args: vec![d.init.unwrap().as_arg()],
                             type_args: Default::default(),
                         };
 
diff --git a/packages/helpers/esm/_using_ctx.js b/packages/helpers/esm/_using_ctx.js
new file mode 100644
index 0000000000..76e6d86e83
--- /dev/null
+++ b/packages/helpers/esm/_using_ctx.js
@@ -0,0 +1,78 @@
+export function _usingCtx() {
+    var _disposeSuppressedError =
+        typeof SuppressedError === "function"
+            ? // eslint-disable-next-line no-undef
+            SuppressedError
+            : (function (error, suppressed) {
+                var err = new Error();
+                err.name = "SuppressedError";
+                err.suppressed = suppressed;
+                err.error = error;
+                return err;
+            }),
+        empty = {},
+        stack = [];
+    function using(isAwait, value) {
+        if (value != null) {
+            if (Object(value) !== value) {
+                throw new TypeError(
+                    "using declarations can only be used with objects, functions, null, or undefined.",
+                );
+            }
+            // core-js-pure uses Symbol.for for polyfilling well-known symbols
+            if (isAwait) {
+                var dispose =
+                    value[Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")];
+            }
+            if (dispose == null) {
+                dispose = value[Symbol.dispose || Symbol.for("Symbol.dispose")];
+            }
+            if (typeof dispose !== "function") {
+                throw new TypeError(`Property [Symbol.dispose] is not a function.`);
+            }
+            stack.push({ v: value, d: dispose, a: isAwait });
+        } else if (isAwait) {
+            // provide the nullish `value` as `d` for minification gain
+            stack.push({ d: value, a: isAwait });
+        }
+        return value;
+    }
+    return {
+        // error
+        e: empty,
+        // using
+        u: using.bind(null, false),
+        // await using
+        a: using.bind(null, true),
+        // dispose
+        d: function () {
+            var error = this.e;
+
+            function next() {
+                // eslint-disable-next-line @typescript-eslint/no-use-before-define
+                while ((resource = stack.pop())) {
+                    try {
+                        var resource,
+                            disposalResult = resource.d && resource.d.call(resource.v);
+                        if (resource.a) {
+                            return Promise.resolve(disposalResult).then(next, err);
+                        }
+                    } catch (e) {
+                        return err(e);
+                    }
+                }
+                if (error !== empty) throw error;
+            }
+
+            function err(e) {
+                error = error !== empty ? new _disposeSuppressedError(error, e) : e;
+
+                return next();
+            }
+
+            return next();
+        },
+    };
+}
+
+export { _usingCtx as _ }
\ No newline at end of file
diff --git a/packages/helpers/package.json b/packages/helpers/package.json
index c637a26d18..986f160c43 100644
--- a/packages/helpers/package.json
+++ b/packages/helpers/package.json
@@ -1,7 +1,7 @@
 {
     "name": "@swc/helpers",
     "packageManager": "yarn@4.0.2",
-    "version": "0.5.9",
+    "version": "0.5.10",
     "description": "External helpers for the swc project.",
     "module": "esm/index.js",
     "main": "cjs/index.cjs",


Test patch:
diff --git a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/exec-async/issue-8853.js b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/exec-async/issue-8853.js
new file mode 100644
index 0000000000..5876957eb2
--- /dev/null
+++ b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/exec-async/issue-8853.js
@@ -0,0 +1,40 @@
+const { deepStrictEqual } = require('node:assert')
+
+let i = 0
+let err
+try {
+    await using _x1 = {
+        async [Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")]() {
+            throw [1, ++i]
+        }
+    }
+
+    await using _x2 = {
+        async [Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")]() {
+            throw [2, ++i]
+        }
+    }
+
+    await using _x3 = {
+        async [Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")]() {
+            throw [3, ++i]
+        }
+    }
+
+    await using _x4 = {
+        async [Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")]() {
+            throw [4, ++i]
+        }
+    }
+
+    throw [5, ++i]
+} catch (e) {
+    err = e
+}
+
+console.log(err)
+deepStrictEqual(err.suppressed, [1, 5])
+deepStrictEqual(err.error.suppressed, [2, 4])
+deepStrictEqual(err.error.error.suppressed, [3, 3])
+deepStrictEqual(err.error.error.error.suppressed, [4, 2])
+deepStrictEqual(err.error.error.error.error, [5, 1])
\ No newline at end of file
diff --git a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-await/issue-8853/input.mjs b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-await/issue-8853/input.mjs
new file mode 100644
index 0000000000..88d6474578
--- /dev/null
+++ b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-await/issue-8853/input.mjs
@@ -0,0 +1,32 @@
+
+let i = 0
+let err
+try {
+    await using _x1 = {
+        async [Symbol.asyncDispose]() {
+            throw [1, ++i]
+        }
+    }
+
+    await using _x2 = {
+        async [Symbol.asyncDispose]() {
+            throw [2, ++i]
+        }
+    }
+
+    await using _x3 = {
+        async [Symbol.asyncDispose]() {
+            throw [3, ++i]
+        }
+    }
+
+    await using _x4 = {
+        async [Symbol.asyncDispose]() {
+            throw [4, ++i]
+        }
+    }
+
+    throw [5, ++i]
+} catch (e) {
+    err = e
+}
\ No newline at end of file
diff --git a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-await/issue-8853/output.mjs b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-await/issue-8853/output.mjs
new file mode 100644
index 0000000000..7d8080129a
--- /dev/null
+++ b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-await/issue-8853/output.mjs
@@ -0,0 +1,49 @@
+let i = 0;
+let err;
+try {
+    try {
+        var _usingCtx = _using_ctx();
+        const _x1 = _usingCtx.a({
+            async [Symbol.asyncDispose] () {
+                throw [
+                    1,
+                    ++i
+                ];
+            }
+        });
+        const _x2 = _usingCtx.a({
+            async [Symbol.asyncDispose] () {
+                throw [
+                    2,
+                    ++i
+                ];
+            }
+        });
+        const _x3 = _usingCtx.a({
+            async [Symbol.asyncDispose] () {
+                throw [
+                    3,
+                    ++i
+                ];
+            }
+        });
+        const _x4 = _usingCtx.a({
+            async [Symbol.asyncDispose] () {
+                throw [
+                    4,
+                    ++i
+                ];
+            }
+        });
+        throw [
+            5,
+            ++i
+        ];
+    } catch (_) {
+        _usingCtx.e = _;
+    } finally{
+        await _usingCtx.d();
+    }
+} catch (e) {
+    err = e;
+}
diff --git a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-await/mixed/output.mjs b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-await/mixed/output.mjs
index 6f2b7f9374..bc88967b14 100644
--- a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-await/mixed/output.mjs
+++ b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-await/mixed/output.mjs
@@ -1,13 +1,12 @@
 {
     try {
-        var _stack = [];
-        var a = _using(_stack, 1);
-        var b = _using(_stack, 2, true);
-        var c = _using(_stack, 3);
+        var _usingCtx = _using_ctx();
+        const a = _usingCtx.u(1);
+        const b = _usingCtx.a(2);
+        const c = _usingCtx.u(3);
     } catch (_) {
-        var _error = _;
-        var _hasError = true;
+        _usingCtx.e = _;
     } finally{
-        await _dispose(_stack, _error, _hasError);
+        await _usingCtx.d();
     }
 }
diff --git a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-await/only-using-await/output.mjs b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-await/only-using-await/output.mjs
index 5d4390b9ff..ab8caf7f5d 100644
--- a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-await/only-using-await/output.mjs
+++ b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-await/only-using-await/output.mjs
@@ -1,14 +1,13 @@
 {
     try {
-        var _stack = [];
-        var x = _using(_stack, obj, true);
+        var _usingCtx = _using_ctx();
+        const x = _usingCtx.a(obj);
         stmt;
-        var y = _using(_stack, obj, true), z = _using(_stack, obj, true);
+        const y = _usingCtx.a(obj), z = _usingCtx.a(obj);
         doSomethingWith(x, y);
     } catch (_) {
-        var _error = _;
-        var _hasError = true;
+        _usingCtx.e = _;
     } finally{
-        await _dispose(_stack, _error, _hasError);
+        await _usingCtx.d();
     }
 }
diff --git a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/bare-block/output.js b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/bare-block/output.js
index 6982b489b6..ae57c226af 100644
--- a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/bare-block/output.js
+++ b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/bare-block/output.js
@@ -1,12 +1,11 @@
 {
     try {
-        var _stack = [];
-        var x = _using(_stack, obj);
+        var _usingCtx = _using_ctx();
+        const x = _usingCtx.u(obj);
         doSomethingWith(x);
     } catch (_) {
-        var _error = _;
-        var _hasError = true;
+        _usingCtx.e = _;
     } finally{
-        _dispose(_stack, _error, _hasError);
+        _usingCtx.d();
     }
 }
diff --git a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/for-await-head/output.mjs b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/for-await-head/output.mjs
index 64e1f02050..69d1662c66 100644
--- a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/for-await-head/output.mjs
+++ b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/for-await-head/output.mjs
@@ -1,13 +1,12 @@
 for await (const x of y){
     try {
-        var _stack = [];
+        var _usingCtx = _using_ctx();
         {
             doSomethingWith(x);
         }
     } catch (_) {
-        var _error = _;
-        var _hasError = true;
+        _usingCtx.e = _;
     } finally{
-        _dispose(_stack, _error, _hasError);
+        _usingCtx.d();
     }
 }
diff --git a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/for-head/output.js b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/for-head/output.js
index 11e7a96f20..664ad9129e 100644
--- a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/for-head/output.js
+++ b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/for-head/output.js
@@ -1,13 +1,12 @@
 for (const x of y){
     try {
-        var _stack = [];
+        var _usingCtx = _using_ctx();
         {
             doSomethingWith(x);
         }
     } catch (_) {
-        var _error = _;
-        var _hasError = true;
+        _usingCtx.e = _;
     } finally{
-        _dispose(_stack, _error, _hasError);
+        _usingCtx.d();
     }
 }
diff --git a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/function-body/output.js b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/function-body/output.js
index 4330fb187b..73a4f9aceb 100644
--- a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/function-body/output.js
+++ b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/function-body/output.js
@@ -1,12 +1,11 @@
 function fn() {
     try {
-        var _stack = [];
-        var x = _using(_stack, obj);
+        var _usingCtx = _using_ctx();
+        const x = _usingCtx.u(obj);
         return doSomethingWith(x);
     } catch (_) {
-        var _error = _;
-        var _hasError = true;
+        _usingCtx.e = _;
     } finally{
-        _dispose(_stack, _error, _hasError);
+        _usingCtx.d();
     }
 }
diff --git a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/if-body/output.js b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/if-body/output.js
index 41666e989f..37a67bf088 100644
--- a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/if-body/output.js
+++ b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/if-body/output.js
@@ -1,12 +1,11 @@
 if (test) {
     try {
-        var _stack = [];
-        var x = _using(_stack, obj);
+        var _usingCtx = _using_ctx();
+        const x = _usingCtx.u(obj);
         doSomethingWith(x);
     } catch (_) {
-        var _error = _;
-        var _hasError = true;
+        _usingCtx.e = _;
     } finally{
-        _dispose(_stack, _error, _hasError);
+        _usingCtx.d();
     }
 }
diff --git a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/issue-8629/output.js b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/issue-8629/output.js
index 71f3493ada..f4bf134efc 100644
--- a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/issue-8629/output.js
+++ b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/issue-8629/output.js
@@ -1,19 +1,18 @@
 // main.ts
 var _Disposable;
 try {
-    var _stack = [];
+    var _usingCtx = _using_ctx();
     class Disposable {
         [Symbol.dispose]() {
             console.log('dispose');
         }
     }
     _Disposable = Disposable;
-    var _disposable = _using(_stack, new Disposable());
+    var _disposable = _usingCtx.u(new Disposable());
     console.log('ok');
 } catch (_) {
-    var _error = _;
-    var _hasError = true;
+    _usingCtx.e = _;
 } finally{
-    _dispose(_stack, _error, _hasError);
+    _usingCtx.d();
 }
 export { _Disposable as Disposable };
diff --git a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/multiple-nested/output.js b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/multiple-nested/output.js
index 84cc09b386..a43a642ea8 100644
--- a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/multiple-nested/output.js
+++ b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/multiple-nested/output.js
@@ -1,35 +1,32 @@
 {
     try {
-        var _stack = [];
-        var x = _using(_stack, obj);
+        var _usingCtx = _using_ctx();
+        const x = _usingCtx.u(obj);
         {
             try {
-                var _stack1 = [];
-                var y = _using(_stack1, call(()=>{
+                var _usingCtx1 = _using_ctx();
+                const y = _usingCtx1.u(call(()=>{
                     try {
-                        var _stack = [];
-                        var z = _using(_stack, obj);
+                        var _usingCtx = _using_ctx();
+                        const z = _usingCtx.u(obj);
                         return z;
                     } catch (_) {
-                        var _error = _;
-                        var _hasError = true;
+                        _usingCtx.e = _;
                     } finally{
-                        _dispose(_stack, _error, _hasError);
+                        _usingCtx.d();
                     }
                 }));
                 stmt;
             } catch (_) {
-                var _error = _;
-                var _hasError = true;
+                _usingCtx1.e = _;
             } finally{
-                _dispose(_stack1, _error, _hasError);
+                _usingCtx1.d();
             }
         }
         stmt;
     } catch (_) {
-        var _error1 = _;
-        var _hasError1 = true;
+        _usingCtx.e = _;
     } finally{
-        _dispose(_stack, _error1, _hasError1);
+        _usingCtx.d();
     }
 }
diff --git a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/multiple-same-level/output.js b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/multiple-same-level/output.js
index 8ac8945904..74d1135cdc 100644
--- a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/multiple-same-level/output.js
+++ b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/multiple-same-level/output.js
@@ -1,17 +1,16 @@
 {
     try {
-        var _stack = [];
+        var _usingCtx = _using_ctx();
         stmt;
-        var x = _using(_stack, obj);
+        const x = _usingCtx.u(obj);
         stmt;
-        var y = _using(_stack, obj), z = _using(_stack, obj);
+        const y = _usingCtx.u(obj), z = _usingCtx.u(obj);
         stmt;
-        var w = _using(_stack, obj);
+        const w = _usingCtx.u(obj);
         doSomethingWith(x, z);
     } catch (_) {
-        var _error = _;
-        var _hasError = true;
+        _usingCtx.e = _;
     } finally{
-        _dispose(_stack, _error, _hasError);
+        _usingCtx.d();
     }
 }
diff --git a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/static-block/output.js b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/static-block/output.js
index cec9c08fbe..40822b4ddd 100644
--- a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/static-block/output.js
+++ b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-sync/static-block/output.js
@@ -1,14 +1,13 @@
 class A {
     static{
         try {
-            var _stack = [];
-            var x = _using(_stack, y);
+            var _usingCtx = _using_ctx();
+            const x = _usingCtx.u(y);
             doSomethingWith(x);
         } catch (_) {
-            var _error = _;
-            var _hasError = true;
+            _usingCtx.e = _;
         } finally{
-            _dispose(_stack, _error, _hasError);
+            _usingCtx.d();
         }
     }
 }
diff --git a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/await-or-not-preserved/output.mjs b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/await-or-not-preserved/output.mjs
index 1e0a436051..95ff79d278 100644
--- a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/await-or-not-preserved/output.mjs
+++ b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/await-or-not-preserved/output.mjs
@@ -1,11 +1,10 @@
 export { x, y };
 try {
-    var _stack = [];
-    var x = _using(_stack, A);
-    var y = _using(_stack, B, true);
+    var _usingCtx = _using_ctx();
+    var x = _usingCtx.u(A);
+    var y = _usingCtx.a(B);
 } catch (_) {
-    var _error = _;
-    var _hasError = true;
+    _usingCtx.e = _;
 } finally{
-    await _dispose(_stack, _error, _hasError);
+    await _usingCtx.d();
 }
diff --git a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/hoisting-default-clas-anon/output.mjs b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/hoisting-default-clas-anon/output.mjs
index ece84ba9ee..92818dd6f9 100644
--- a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/hoisting-default-clas-anon/output.mjs
+++ b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/hoisting-default-clas-anon/output.mjs
@@ -1,12 +1,11 @@
 export { _default as default };
 try {
-    var _stack = [];
-    var x = _using(_stack, null);
+    var _usingCtx = _using_ctx();
+    var x = _usingCtx.u(null);
     var _default = class {
     };
 } catch (_) {
-    var _error = _;
-    var _hasError = true;
+    _usingCtx.e = _;
 } finally{
-    _dispose(_stack, _error, _hasError);
+    _usingCtx.d();
 }
diff --git a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/hoisting-default-class/output.mjs b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/hoisting-default-class/output.mjs
index c71d8b9c79..34ad7a56f1 100644
--- a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/hoisting-default-class/output.mjs
+++ b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/hoisting-default-class/output.mjs
@@ -1,12 +1,11 @@
 export { C as default };
 try {
-    var _stack = [];
-    var x = _using(_stack, null);
+    var _usingCtx = _using_ctx();
+    var x = _usingCtx.u(null);
     var C = class C {
     };
 } catch (_) {
-    var _error = _;
-    var _hasError = true;
+    _usingCtx.e = _;
 } finally{
-    _dispose(_stack, _error, _hasError);
+    _usingCtx.d();
 }
diff --git a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/hoisting-default-expr/output.mjs b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/hoisting-default-expr/output.mjs
index 8a2ecaa8ac..682561df44 100644
--- a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/hoisting-default-expr/output.mjs
+++ b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/hoisting-default-expr/output.mjs
@@ -1,11 +1,10 @@
 export { _default as default };
 try {
-    var _stack = [];
-    var x = _using(_stack, null);
+    var _usingCtx = _using_ctx();
+    var x = _usingCtx.u(null);
     var _default = doSomething();
 } catch (_) {
-    var _error = _;
-    var _hasError = true;
+    _usingCtx.e = _;
 } finally{
-    _dispose(_stack, _error, _hasError);
+    _usingCtx.d();
 }
diff --git a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/hoisting-default-fn-anon/output.mjs b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/hoisting-default-fn-anon/output.mjs
index ebbc6014dd..7357daba7d 100644
--- a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/hoisting-default-fn-anon/output.mjs
+++ b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/hoisting-default-fn-anon/output.mjs
@@ -1,11 +1,10 @@
 export { fn as default };
 try {
-    var _stack = [];
-    var x = _using(_stack, null);
+    var _usingCtx = _using_ctx();
+    var x = _usingCtx.u(null);
     var fn = function fn() {};
 } catch (_) {
-    var _error = _;
-    var _hasError = true;
+    _usingCtx.e = _;
 } finally{
-    _dispose(_stack, _error, _hasError);
+    _usingCtx.d();
 }
diff --git a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/hoisting-default-fn/output.mjs b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/hoisting-default-fn/output.mjs
index ebbc6014dd..7357daba7d 100644
--- a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/hoisting-default-fn/output.mjs
+++ b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/hoisting-default-fn/output.mjs
@@ -1,11 +1,10 @@
 export { fn as default };
 try {
-    var _stack = [];
-    var x = _using(_stack, null);
+    var _usingCtx = _using_ctx();
+    var x = _usingCtx.u(null);
     var fn = function fn() {};
 } catch (_) {
-    var _error = _;
-    var _hasError = true;
+    _usingCtx.e = _;
 } finally{
-    _dispose(_stack, _error, _hasError);
+    _usingCtx.d();
 }
diff --git a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/hoisting/output.mjs b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/hoisting/output.mjs
index c4b66aaf50..022a0f510e 100644
--- a/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/hoisting/output.mjs
+++ b/crates/swc_ecma_transforms_proposal/tests/explicit-resource-management/transform-top-level/hoisting/output.mjs
@@ -14,7 +14,7 @@ export { f };
 var _b;
 var _B;
 try {
-    var _stack = [];
+    var _usingCtx = _using_ctx();
     function g() {
         c;
     }
@@ -22,18 +22,17 @@ try {
     doSomething();
     let { b } = {};
     _b = b;
-    var c = 2;
+    let c = 2;
     class A {
     }
     class B {
     }
     _B = B;
-    var x = _using(_stack, null);
+    var x = _usingCtx.u(null);
 } catch (_) {
-    var _error = _;
-    var _hasError = true;
+    _usingCtx.e = _;
 } finally{
-    _dispose(_stack, _error, _hasError);
+    _usingCtx.d();
 }
 export { _g as g };
 export { _b as b };