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 000000000000..5076a16cd877 --- /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 ed966b27e2d9..691fddef79ce 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 625c03b91cf4..5dbc11d28f70 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/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 000000000000..5876957eb2f1 --- /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 000000000000..88d6474578a1 --- /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 000000000000..7d8080129a94 --- /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 6f2b7f937492..bc88967b14d5 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 5d4390b9ffbe..ab8caf7f5d1e 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 6982b489b611..ae57c226afa8 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 64e1f0205047..69d1662c66a6 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 11e7a96f20a0..664ad9129e5e 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 4330fb187b30..73a4f9acebd4 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 41666e989ff9..37a67bf08873 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 71f3493adabe..f4bf134efc46 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 84cc09b386a1..a43a642ea82b 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 8ac894590485..74d1135cdc3f 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 cec9c08fbe4e..40822b4ddd8f 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 1e0a43605140..95ff79d27880 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 ece84ba9eeb6..92818dd6f95f 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 c71d8b9c7953..34ad7a56f1df 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 8a2ecaa8ac0d..682561df4403 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 ebbc6014dd8c..7357daba7ddf 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 ebbc6014dd8c..7357daba7ddf 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 c4b66aaf50b7..022a0f510e9d 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 }; diff --git a/packages/helpers/esm/_using_ctx.js b/packages/helpers/esm/_using_ctx.js new file mode 100644 index 000000000000..76e6d86e83b9 --- /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 c637a26d182a..986f160c433c 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",