diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs
index 740a45b3e177b..7d0b92036d492 100644
--- a/crates/oxc_transformer/src/lib.rs
+++ b/crates/oxc_transformer/src/lib.rs
@@ -122,7 +122,10 @@ impl<'a> Transformer<'a> {
let mut transformer = TransformerImpl {
common: Common::new(&self.env, &self.ctx),
decorator: Decorator::new(self.decorator, &self.ctx),
- explicit_resource_management: ExplicitResourceManagement::new(&self.ctx),
+ explicit_resource_management: ExplicitResourceManagement::new(
+ &self.ctx,
+ self.env.es2017.async_to_generator,
+ ),
x0_typescript: program
.source_type
.is_typescript()
diff --git a/crates/oxc_transformer/src/proposals/explicit_resource_management.rs b/crates/oxc_transformer/src/proposals/explicit_resource_management.rs
index f6b432b0c3c97..0a9e0ddeaf1a1 100644
--- a/crates/oxc_transformer/src/proposals/explicit_resource_management.rs
+++ b/crates/oxc_transformer/src/proposals/explicit_resource_management.rs
@@ -50,6 +50,8 @@ use crate::{Helper, TransformCtx};
pub struct ExplicitResourceManagement<'a, 'ctx> {
ctx: &'ctx TransformCtx<'a>,
+ async_to_generator: bool,
+
top_level_using: FxHashMap
,
/// keeps track of whether the current static block contains a `using` declaration
@@ -59,15 +61,21 @@ pub struct ExplicitResourceManagement<'a, 'ctx> {
/// keeps track of whether the current switch statement contains a `using` declaration
/// so that we can transform it in `exit_statement`
switch_stmt_stack: NonEmptyStack,
+
+ /// keeps track of whether the current block statement contains a `using` declaration
+ /// so that we can transform it in `exit_statement`
+ block_stmt_stack: NonEmptyStack,
}
impl<'a, 'ctx> ExplicitResourceManagement<'a, 'ctx> {
- pub fn new(ctx: &'ctx TransformCtx<'a>) -> Self {
+ pub fn new(ctx: &'ctx TransformCtx<'a>, async_to_generator: bool) -> Self {
Self {
ctx,
+ async_to_generator,
top_level_using: FxHashMap::default(),
static_blocks_stack: NonEmptyStack::new(false),
switch_stmt_stack: NonEmptyStack::new(false),
+ block_stmt_stack: NonEmptyStack::new(false),
}
}
}
@@ -178,6 +186,7 @@ impl<'a> Traverse<'a> for ExplicitResourceManagement<'a, '_> {
ctx: &mut TraverseCtx<'a>,
) {
if matches!(node.kind, VariableDeclarationKind::Using | VariableDeclarationKind::AwaitUsing)
+ || self.top_level_using.contains_key(&Address::from_ptr(node))
{
match ctx.parent() {
Ancestor::StaticBlockBody(_) => {
@@ -186,6 +195,9 @@ impl<'a> Traverse<'a> for ExplicitResourceManagement<'a, '_> {
Ancestor::SwitchCaseConsequent(_) => {
*self.switch_stmt_stack.last_mut() = true;
}
+ Ancestor::BlockStatementBody(_) => {
+ *self.block_stmt_stack.last_mut() = true;
+ }
_ => {}
}
}
@@ -232,10 +244,11 @@ impl<'a> Traverse<'a> for ExplicitResourceManagement<'a, '_> {
// or `SwitchStatement`s. We want the common path for "nothing to do here" not to incur the cost of
// a function call.
#[inline]
- fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
+ fn enter_statement(&mut self, stmt: &mut Statement<'a>, _ctx: &mut TraverseCtx<'a>) {
match stmt {
- // TODO: move this to exit_statement
- Statement::BlockStatement(_) => self.transform_block_statement(stmt, ctx),
+ Statement::BlockStatement(_) => {
+ self.block_stmt_stack.push(false);
+ }
Statement::SwitchStatement(_) => {
self.switch_stmt_stack.push(false);
}
@@ -243,11 +256,20 @@ impl<'a> Traverse<'a> for ExplicitResourceManagement<'a, '_> {
}
}
+ #[inline]
fn exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
- if let Statement::SwitchStatement(_) = stmt {
- if self.switch_stmt_stack.pop() {
- self.transform_switch_statement(stmt, ctx);
+ match stmt {
+ Statement::BlockStatement(_) => {
+ if self.block_stmt_stack.pop() {
+ self.transform_block_statement(stmt, ctx);
+ }
}
+ Statement::SwitchStatement(_) => {
+ if self.switch_stmt_stack.pop() {
+ self.transform_switch_statement(stmt, ctx);
+ }
+ }
+ _ => {}
}
}
@@ -653,7 +675,7 @@ impl<'a> ExplicitResourceManagement<'a, '_> {
};
let catch = Self::create_catch_clause(&using_ctx, ctx.current_scope_id(), ctx);
- let finally = Self::create_finally_block(&using_ctx, current_scope_id, needs_await, ctx);
+ let finally = self.create_finally_block(&using_ctx, current_scope_id, needs_await, ctx);
*stmt = ctx.ast.statement_try(SPAN, block, Some(catch), Some(finally));
}
@@ -775,7 +797,7 @@ impl<'a> ExplicitResourceManagement<'a, '_> {
}
let catch = Self::create_catch_clause(&using_ctx, parent_scope_id, ctx);
- let finally = Self::create_finally_block(&using_ctx, parent_scope_id, needs_await, ctx);
+ let finally = self.create_finally_block(&using_ctx, parent_scope_id, needs_await, ctx);
Some(ctx.ast.statement_try(SPAN, block, Some(catch), Some(finally)))
}
@@ -829,6 +851,7 @@ impl<'a> ExplicitResourceManagement<'a, '_> {
/// `{ _usingCtx.d(); }`
fn create_finally_block(
+ &self,
using_ctx: &BoundIdentifier<'a>,
parent_scope_id: ScopeId,
needs_await: bool,
@@ -850,7 +873,15 @@ impl<'a> ExplicitResourceManagement<'a, '_> {
false,
);
- let stmt = if needs_await { ctx.ast.expression_await(SPAN, expr) } else { expr };
+ let stmt = if needs_await {
+ if self.async_to_generator {
+ ctx.ast.expression_yield(SPAN, false, Some(expr))
+ } else {
+ ctx.ast.expression_await(SPAN, expr)
+ }
+ } else {
+ expr
+ };
ctx.ast.alloc_block_statement_with_scope_id(
SPAN,
diff --git a/tasks/coverage/snapshots/semantic_typescript.snap b/tasks/coverage/snapshots/semantic_typescript.snap
index eb9a460149df9..f5b1eaf6f6d89 100644
--- a/tasks/coverage/snapshots/semantic_typescript.snap
+++ b/tasks/coverage/snapshots/semantic_typescript.snap
@@ -41524,24 +41524,24 @@ rebuilt : ScopeId(9): []
tasks/coverage/typescript/tests/cases/conformance/statements/VariableStatements/usingDeclarations/usingDeclarations.1.ts
semantic error: Bindings mismatch:
-after transform: ScopeId(0): ["C1", "C2", "C3", "N", "_af", "_ag", "_asyncToGenerator", "_awaitAsyncGenerator", "_defineProperty", "_usingCtx2", "_usingCtx20", "_usingCtx23", "_usingCtx24", "_usingCtx25", "_usingCtx26", "_usingCtx27", "_usingCtx28", "_usingCtx29", "_usingCtx6", "_wrapAsyncGenerator", "a", "af", "ag", "d1", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31", "d32", "f", "g"]
-rebuilt : ScopeId(0): ["C1", "C2", "C3", "N", "_af", "_ag", "_asyncToGenerator", "_awaitAsyncGenerator", "_defineProperty", "_usingCtx2", "_usingCtx20", "_usingCtx21", "_usingCtx22", "_usingCtx23", "_usingCtx24", "_usingCtx25", "_usingCtx26", "_usingCtx27", "_usingCtx28", "_usingCtx29", "_usingCtx6", "_wrapAsyncGenerator", "a", "af", "ag", "d1", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31", "d32", "f", "g"]
+after transform: ScopeId(0): ["C1", "C2", "C3", "N", "_af", "_ag", "_asyncToGenerator", "_awaitAsyncGenerator", "_defineProperty", "_usingCtx19", "_usingCtx2", "_usingCtx22", "_usingCtx23", "_usingCtx24", "_usingCtx25", "_usingCtx26", "_usingCtx27", "_usingCtx28", "_usingCtx29", "_wrapAsyncGenerator", "a", "af", "ag", "d1", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31", "d32", "f", "g"]
+rebuilt : ScopeId(0): ["C1", "C2", "C3", "N", "_af", "_ag", "_asyncToGenerator", "_awaitAsyncGenerator", "_defineProperty", "_usingCtx19", "_usingCtx2", "_usingCtx20", "_usingCtx21", "_usingCtx22", "_usingCtx23", "_usingCtx24", "_usingCtx25", "_usingCtx26", "_usingCtx27", "_usingCtx28", "_usingCtx29", "_wrapAsyncGenerator", "a", "af", "ag", "d1", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31", "d32", "f", "g"]
Bindings mismatch:
-after transform: ScopeId(70): ["_usingCtx21", "_usingCtx22"]
+after transform: ScopeId(70): ["_usingCtx20", "_usingCtx21"]
rebuilt : ScopeId(29): []
Symbol span mismatch for "N":
after transform: SymbolId(26): Span { start: 1385, end: 1386 }
rebuilt : SymbolId(66): Span { start: 0, end: 0 }
+Symbol scope ID mismatch for "_usingCtx20":
+after transform: SymbolId(88): ScopeId(70)
+rebuilt : SymbolId(74): ScopeId(0)
Symbol scope ID mismatch for "_usingCtx21":
after transform: SymbolId(90): ScopeId(70)
-rebuilt : SymbolId(74): ScopeId(0)
-Symbol scope ID mismatch for "_usingCtx22":
-after transform: SymbolId(92): ScopeId(70)
rebuilt : SymbolId(78): ScopeId(0)
tasks/coverage/typescript/tests/cases/conformance/statements/VariableStatements/usingDeclarations/usingDeclarationsDeclarationEmit.2.ts
semantic error: Scope children mismatch:
-after transform: ScopeId(0): [ScopeId(2), ScopeId(4), ScopeId(5), ScopeId(6), ScopeId(8)]
+after transform: ScopeId(0): [ScopeId(2), ScopeId(4), ScopeId(5), ScopeId(7), ScopeId(9)]
rebuilt : ScopeId(0): [ScopeId(1), ScopeId(5), ScopeId(7)]
Symbol reference IDs mismatch for "r1":
after transform: SymbolId(0): [ReferenceId(1)]
diff --git a/tasks/transform_conformance/overrides/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/multiple-nested/output.js b/tasks/transform_conformance/overrides/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/multiple-nested/output.js
new file mode 100644
index 0000000000000..b64e874664dc4
--- /dev/null
+++ b/tasks/transform_conformance/overrides/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/multiple-nested/output.js
@@ -0,0 +1,30 @@
+try {
+ var _usingCtx3 = babelHelpers.usingCtx();
+ const x = _usingCtx3.u(obj);
+ try {
+ var _usingCtx2 = babelHelpers.usingCtx();
+ const y = _usingCtx2.u(
+ call(() => {
+ try {
+ var _usingCtx = babelHelpers.usingCtx();
+ const z = _usingCtx.u(obj);
+ return z;
+ } catch (_) {
+ _usingCtx.e = _;
+ } finally {
+ _usingCtx.d();
+ }
+ })
+ );
+ stmt;
+ } catch (_) {
+ _usingCtx2.e = _;
+ } finally {
+ _usingCtx2.d();
+ }
+ stmt;
+} catch (_) {
+ _usingCtx3.e = _;
+} finally {
+ _usingCtx3.d();
+}