main
rs 130 lines 4.45 KB
Raw
1 // Copyright (c) Meta Platforms, Inc. and affiliates.
2 //
3 // This source code is licensed under the MIT license found in the
4 // LICENSE file in the root directory of this source tree.
5
6 //! StabilizeBlockIds
7 //!
8 //! Rewrites block IDs to sequential values so that the output is deterministic
9 //! regardless of the order in which blocks were created.
10 //!
11 //! Corresponds to `src/ReactiveScopes/StabilizeBlockIds.ts`.
12
13 use rustc_hash::{FxBuildHasher, FxHashMap};
14
15 use indexmap::IndexSet;
16 use react_compiler_hir::{
17 BlockId, ReactiveFunction, ReactiveScopeBlock, ReactiveTerminal, ReactiveTerminalStatement,
18 environment::Environment,
19 };
20
21 use crate::visitors::{
22 ReactiveFunctionTransform, ReactiveFunctionVisitor, transform_reactive_function,
23 visit_reactive_function,
24 };
25
26 /// Rewrites block IDs to sequential values.
27 /// TS: `stabilizeBlockIds`
28 pub fn stabilize_block_ids(func: &mut ReactiveFunction, env: &mut Environment) {
29 // Pass 1: Collect referenced labels (preserving insertion order to match TS Set behavior)
30 let mut referenced: IndexSet<BlockId, FxBuildHasher> = IndexSet::default();
31 let collector = CollectReferencedLabels { env: &*env };
32 visit_reactive_function(func, &collector, &mut referenced);
33
34 // Build mappings: referenced block IDs -> sequential IDs (insertion-order deterministic)
35 let mut mappings: FxHashMap<BlockId, BlockId> = FxHashMap::default();
36 for block_id in &referenced {
37 let len = mappings.len() as u32;
38 mappings.entry(*block_id).or_insert(BlockId(len));
39 }
40
41 // Pass 2: Rewrite block IDs using ReactiveFunctionTransform
42 let mut rewriter = RewriteBlockIds { env };
43 let _ = transform_reactive_function(func, &mut rewriter, &mut mappings);
44 }
45
46 // =============================================================================
47 // Pass 1: CollectReferencedLabels
48 // =============================================================================
49
50 struct CollectReferencedLabels<'a> {
51 env: &'a Environment,
52 }
53
54 impl<'a> ReactiveFunctionVisitor for CollectReferencedLabels<'a> {
55 type State = IndexSet<BlockId, FxBuildHasher>;
56
57 fn env(&self) -> &Environment {
58 self.env
59 }
60
61 fn visit_scope(&self, scope: &ReactiveScopeBlock, state: &mut Self::State) {
62 let scope_data = &self.env.scopes[scope.scope.0 as usize];
63 if let Some(ref early_return) = scope_data.early_return_value {
64 state.insert(early_return.label);
65 }
66 self.traverse_scope(scope, state);
67 }
68
69 fn visit_terminal(&self, stmt: &ReactiveTerminalStatement, state: &mut Self::State) {
70 if let Some(ref label) = stmt.label {
71 if !label.implicit {
72 state.insert(label.id);
73 }
74 }
75 self.traverse_terminal(stmt, state);
76 }
77 }
78
79 // =============================================================================
80 // Pass 2: RewriteBlockIds
81 // =============================================================================
82
83 fn get_or_insert_mapping(mappings: &mut FxHashMap<BlockId, BlockId>, id: BlockId) -> BlockId {
84 let len = mappings.len() as u32;
85 *mappings.entry(id).or_insert(BlockId(len))
86 }
87
88 /// TS: `class RewriteBlockIds extends ReactiveFunctionVisitor<Map<BlockId, BlockId>>`
89 struct RewriteBlockIds<'a> {
90 env: &'a mut Environment,
91 }
92
93 impl<'a> ReactiveFunctionTransform for RewriteBlockIds<'a> {
94 type State = FxHashMap<BlockId, BlockId>;
95
96 fn env(&self) -> &Environment {
97 self.env
98 }
99
100 fn visit_scope(
101 &mut self,
102 scope: &mut ReactiveScopeBlock,
103 state: &mut Self::State,
104 ) -> Result<(), react_compiler_diagnostics::CompilerError> {
105 let scope_data = &mut self.env.scopes[scope.scope.0 as usize];
106 if let Some(ref mut early_return) = scope_data.early_return_value {
107 early_return.label = get_or_insert_mapping(state, early_return.label);
108 }
109 self.traverse_scope(scope, state)
110 }
111
112 fn visit_terminal(
113 &mut self,
114 stmt: &mut ReactiveTerminalStatement,
115 state: &mut Self::State,
116 ) -> Result<(), react_compiler_diagnostics::CompilerError> {
117 if let Some(ref mut label) = stmt.label {
118 label.id = get_or_insert_mapping(state, label.id);
119 }
120
121 match &mut stmt.terminal {
122 ReactiveTerminal::Break { target, .. } | ReactiveTerminal::Continue { target, .. } => {
123 *target = get_or_insert_mapping(state, *target);
124 }
125 _ => {}
126 }
127
128 self.traverse_terminal(stmt, state)
129 }
130 }