Skip to content

Commit 1c6f363

Browse files
committed
fix(transfomer/using): remove use of child ids
1 parent 4eaf781 commit 1c6f363

File tree

7 files changed

+133
-65
lines changed

7 files changed

+133
-65
lines changed

crates/oxc_semantic/src/scoping.rs

+6
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,12 @@ impl Scoping {
677677
});
678678
}
679679

680+
pub fn remove_child_scope(&mut self, scope_id: ScopeId, child_scope_ids: ScopeId) {
681+
self.cell.with_dependent_mut(|_allocator, cell| {
682+
cell.scope_child_ids[scope_id.index()].retain(|scope_id| *scope_id != child_scope_ids);
683+
});
684+
}
685+
680686
/// Create a scope.
681687
#[inline]
682688
pub fn add_scope(

crates/oxc_transformer/examples/transformer.rs

-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ fn main() {
4545
let ret = SemanticBuilder::new()
4646
// Estimate transformer will triple scopes, symbols, references
4747
.with_excess_capacity(2.0)
48-
.with_scope_tree_child_ids(true)
4948
.build(&program);
5049

5150
if !ret.errors.is_empty() {

crates/oxc_transformer/src/proposals/explicit_resource_management.rs

+56-39
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,26 @@ impl<'a> Traverse<'a> for ExplicitResourceManagement<'a, '_> {
158158
/// ```
159159
fn exit_static_block(&mut self, block: &mut StaticBlock<'a>, ctx: &mut TraverseCtx<'a>) {
160160
let scope_id = block.scope_id();
161-
if let Some(replacement) =
162-
self.transform_statements(&mut block.body, None, scope_id, scope_id, ctx)
161+
if let Some((new_stmts, needs_await, using_ctx)) =
162+
self.transform_statements(&mut block.body, scope_id, ctx)
163163
{
164-
block.body = ctx.ast.vec1(replacement);
164+
let static_block_new_scope_id = ctx.insert_scope_between(
165+
ctx.scoping().scope_parent_id(scope_id).unwrap(),
166+
scope_id,
167+
ScopeFlags::ClassStaticBlock,
168+
);
169+
170+
ctx.scoping_mut().set_symbol_scope_id(using_ctx.symbol_id, static_block_new_scope_id);
171+
ctx.scoping_mut().move_binding(scope_id, static_block_new_scope_id, &using_ctx.name);
172+
*ctx.scoping_mut().scope_flags_mut(scope_id) = ScopeFlags::StrictMode;
173+
174+
block.set_scope_id(static_block_new_scope_id);
175+
block.body = ctx.ast.vec1(Self::create_try_stmt(
176+
ctx.ast.block_statement_with_scope_id(SPAN, new_stmts, scope_id),
177+
Self::create_catch_clause(&using_ctx, static_block_new_scope_id, ctx),
178+
Self::create_finally_block(&using_ctx, static_block_new_scope_id, needs_await, ctx),
179+
ctx,
180+
));
165181
}
166182
}
167183

@@ -186,14 +202,22 @@ impl<'a> Traverse<'a> for ExplicitResourceManagement<'a, '_> {
186202
/// }
187203
/// ```
188204
fn enter_function_body(&mut self, body: &mut FunctionBody<'a>, ctx: &mut TraverseCtx<'a>) {
189-
if let Some(replacement) = self.transform_statements(
190-
&mut body.statements,
191-
None,
192-
ctx.current_hoist_scope_id(),
193-
ctx.current_hoist_scope_id(),
194-
ctx,
195-
) {
196-
body.statements = ctx.ast.vec1(replacement);
205+
if let Some((new_stmts, needs_await, using_ctx)) =
206+
self.transform_statements(&mut body.statements, ctx.current_hoist_scope_id(), ctx)
207+
{
208+
// FIXME: this creates the scopes in the correct place, however we never move the bindings contained
209+
// within `new_stmts` to the new scope.
210+
let block_stmt_scope_id =
211+
ctx.insert_scope_below_statements(&new_stmts, ScopeFlags::empty());
212+
213+
let current_scope_id = ctx.current_scope_id();
214+
215+
body.statements = ctx.ast.vec1(Self::create_try_stmt(
216+
ctx.ast.block_statement_with_scope_id(SPAN, new_stmts, block_stmt_scope_id),
217+
Self::create_catch_clause(&using_ctx, current_scope_id, ctx),
218+
Self::create_finally_block(&using_ctx, current_scope_id, needs_await, ctx),
219+
ctx,
220+
));
197221
}
198222
}
199223

@@ -461,15 +485,17 @@ impl<'a> ExplicitResourceManagement<'a, '_> {
461485
fn transform_block_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
462486
let Statement::BlockStatement(block_stmt) = stmt else { unreachable!() };
463487

464-
let scope_id = block_stmt.scope_id();
465-
if let Some(replacement) = self.transform_statements(
466-
&mut block_stmt.body,
467-
Some(scope_id),
468-
ctx.current_scope_id(),
469-
ctx.current_hoist_scope_id(),
470-
ctx,
471-
) {
472-
*stmt = replacement;
488+
if let Some((new_stmts, needs_await, using_ctx)) =
489+
self.transform_statements(&mut block_stmt.body, ctx.current_hoist_scope_id(), ctx)
490+
{
491+
let current_scope_id = ctx.current_scope_id();
492+
493+
*stmt = Self::create_try_stmt(
494+
ctx.ast.block_statement_with_scope_id(SPAN, new_stmts, block_stmt.scope_id()),
495+
Self::create_catch_clause(&using_ctx, current_scope_id, ctx),
496+
Self::create_finally_block(&using_ctx, current_scope_id, needs_await, ctx),
497+
ctx,
498+
);
473499
}
474500
}
475501

@@ -596,8 +622,6 @@ impl<'a> ExplicitResourceManagement<'a, '_> {
596622

597623
/// Transforms:
598624
/// - `node` - the statements to transform
599-
/// - `scope_id` - if provided, it will be used as the scope_id for the new block.
600-
/// - `parent_scope_id` - the parent scope
601625
/// - `hoist_scope_id` - the hoist scope, used for generating new var bindings
602626
/// - `ctx` - the traverse context
603627
///
@@ -624,11 +648,9 @@ impl<'a> ExplicitResourceManagement<'a, '_> {
624648
fn transform_statements(
625649
&mut self,
626650
stmts: &mut ArenaVec<'a, Statement<'a>>,
627-
scope_id: Option<ScopeId>,
628-
parent_scope_id: ScopeId,
629651
hoist_scope_id: ScopeId,
630652
ctx: &mut TraverseCtx<'a>,
631-
) -> Option<Statement<'a>> {
653+
) -> Option<(ArenaVec<'a, Statement<'a>>, bool, BoundIdentifier<'a>)> {
632654
let mut needs_await = false;
633655

634656
let mut using_ctx = None;
@@ -700,21 +722,16 @@ impl<'a> ExplicitResourceManagement<'a, '_> {
700722
);
701723
stmts.insert(0, Statement::from(helper));
702724

703-
let scope_id_children_to_move = scope_id.unwrap_or(parent_scope_id);
704-
705-
let scope_id = scope_id
706-
.unwrap_or_else(|| ctx.create_child_scope(parent_scope_id, ScopeFlags::empty()));
707-
let block = ctx.ast.block_statement_with_scope_id(SPAN, stmts, scope_id);
708-
709-
let child_ids = ctx.scoping_mut().get_scope_child_ids(scope_id_children_to_move).to_vec();
710-
for id in child_ids.iter().filter(|id| *id != &scope_id) {
711-
ctx.scoping_mut().change_scope_parent_id(*id, Some(scope_id));
712-
}
713-
714-
let catch = Self::create_catch_clause(&using_ctx, parent_scope_id, ctx);
715-
let finally = Self::create_finally_block(&using_ctx, parent_scope_id, needs_await, ctx);
725+
Some((stmts, needs_await, using_ctx))
726+
}
716727

717-
Some(ctx.ast.statement_try(SPAN, block, Some(catch), Some(finally)))
728+
fn create_try_stmt(
729+
body: BlockStatement<'a>,
730+
catch: ArenaBox<'a, CatchClause<'a>>,
731+
finally: ArenaBox<'a, BlockStatement<'a>>,
732+
ctx: &TraverseCtx<'a>,
733+
) -> Statement<'a> {
734+
ctx.ast.statement_try(SPAN, body, Some(catch), Some(finally))
718735
}
719736

720737
/// `catch (_) { _usingCtx.e = _; }`

crates/oxc_traverse/src/context/mod.rs

+29
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,35 @@ impl<'a> TraverseCtx<'a> {
315315
self.scoping.insert_scope_below_statements(stmts, flags)
316316
}
317317

318+
/// Insert a scope between a parent and a child scope.
319+
///
320+
/// For example, given the following scopes
321+
/// ```ts
322+
/// parentScope1: {
323+
/// childScope: { }
324+
/// childScope2: { }
325+
/// }
326+
/// ```
327+
/// and calling this function with `parentScope1` and `childScope`,
328+
/// the resulting scopes will be:
329+
/// ```ts
330+
/// parentScope1: {
331+
/// newScope: {
332+
/// childScope: { }
333+
/// }
334+
/// childScope2: { }
335+
/// }
336+
/// ```
337+
/// This is a shortcut for `ctx.scoping.insert_scope_between`.
338+
pub fn insert_scope_between(
339+
&mut self,
340+
parent_id: ScopeId,
341+
child_id: ScopeId,
342+
flags: ScopeFlags,
343+
) -> ScopeId {
344+
self.scoping.insert_scope_between(parent_id, child_id, flags)
345+
}
346+
318347
/// Remove scope for an expression from the scope chain.
319348
///
320349
/// Delete the scope and set parent of its child scopes to its parent scope.

crates/oxc_traverse/src/context/scoping.rs

+40
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,46 @@ impl TraverseScoping {
176176
new_scope_id
177177
}
178178

179+
/// Insert a scope between a parent and a child scope.
180+
///
181+
/// For example, given the following scopes
182+
/// ```ts
183+
/// parentScope1: {
184+
/// childScope: { }
185+
/// childScope2: { }
186+
/// }
187+
/// ```
188+
/// and calling this function with `parentScope1` and `childScope`,
189+
/// the resulting scopes will be:
190+
/// ```ts
191+
/// parentScope1: {
192+
/// newScope: {
193+
/// childScope: { }
194+
/// }
195+
/// childScope2: { }
196+
/// }
197+
/// ```
198+
pub fn insert_scope_between(
199+
&mut self,
200+
parent_id: ScopeId,
201+
child_id: ScopeId,
202+
flags: ScopeFlags,
203+
) -> ScopeId {
204+
let scope_id = self.create_child_scope(parent_id, flags);
205+
206+
debug_assert_eq!(
207+
self.scoping.scope_parent_id(child_id),
208+
Some(parent_id),
209+
"Child scope must be a child of parent scope"
210+
);
211+
212+
if self.scoping.has_scope_child_ids() {
213+
self.scoping.remove_child_scope(parent_id, child_id);
214+
}
215+
self.scoping.set_scope_parent_id(child_id, Some(scope_id));
216+
scope_id
217+
}
218+
179219
/// Remove scope for an expression from the scope chain.
180220
///
181221
/// Delete the scope and set parent of its child scopes to its parent scope.

tasks/transform_conformance/snapshots/babel.snap.md

+2-13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
commit: 578ac4df
22

3-
Passed: 713/1191
3+
Passed: 714/1191
44

55
# All Passed:
66
* babel-plugin-transform-logical-assignment-operators
@@ -2746,7 +2746,7 @@ transform-react-jsx: unknown field `autoImport`, expected one of `runtime`, `dev
27462746
x Output mismatch
27472747

27482748

2749-
# babel-plugin-proposal-explicit-resource-management (20/24)
2749+
# babel-plugin-proposal-explicit-resource-management (21/24)
27502750
* transform-sync/function-body/input.js
27512751
Bindings mismatch:
27522752
after transform: ScopeId(1): ["_usingCtx", "x"]
@@ -2769,17 +2769,6 @@ Symbol scope ID mismatch for "z":
27692769
after transform: SymbolId(2): ScopeId(3)
27702770
rebuilt : SymbolId(5): ScopeId(4)
27712771

2772-
* transform-sync/static-block/input.js
2773-
Bindings mismatch:
2774-
after transform: ScopeId(2): ["_usingCtx", "x"]
2775-
rebuilt : ScopeId(2): ["_usingCtx"]
2776-
Bindings mismatch:
2777-
after transform: ScopeId(3): []
2778-
rebuilt : ScopeId(3): ["x"]
2779-
Symbol scope ID mismatch for "x":
2780-
after transform: SymbolId(1): ScopeId(2)
2781-
rebuilt : SymbolId(2): ScopeId(3)
2782-
27832772
* transform-top-level/hoisting-default-class/input.mjs
27842773
Symbol span mismatch for "C":
27852774
after transform: SymbolId(1): Span { start: 37, end: 38 }

tasks/transform_conformance/snapshots/oxc.snap.md

-12
Original file line numberDiff line numberDiff line change
@@ -323,21 +323,9 @@ x Output mismatch
323323
Bindings mismatch:
324324
after transform: ScopeId(1): ["_usingCtx", "a", "b", "x", "y"]
325325
rebuilt : ScopeId(1): ["_usingCtx", "a", "b"]
326-
Scope children mismatch:
327-
after transform: ScopeId(1): [ScopeId(5), ScopeId(6), ScopeId(8)]
328-
rebuilt : ScopeId(1): [ScopeId(2), ScopeId(3), ScopeId(4), ScopeId(6), ScopeId(8)]
329-
Scope parent mismatch:
330-
after transform: ScopeId(2): Some(ScopeId(5))
331-
rebuilt : ScopeId(2): Some(ScopeId(1))
332-
Scope parent mismatch:
333-
after transform: ScopeId(3): Some(ScopeId(5))
334-
rebuilt : ScopeId(3): Some(ScopeId(1))
335326
Bindings mismatch:
336327
after transform: ScopeId(5): []
337328
rebuilt : ScopeId(4): ["x", "y"]
338-
Scope children mismatch:
339-
after transform: ScopeId(5): [ScopeId(2), ScopeId(3), ScopeId(4)]
340-
rebuilt : ScopeId(4): [ScopeId(5)]
341329
Symbol scope ID mismatch for "x":
342330
after transform: SymbolId(3): ScopeId(1)
343331
rebuilt : SymbolId(4): ScopeId(4)

0 commit comments

Comments
 (0)