main
md 180 lines 6.76 KB
Rendered Raw
1 # buildReactiveFunction
2
3 ## File
4 `src/ReactiveScopes/BuildReactiveFunction.ts`
5
6 ## Purpose
7 The `buildReactiveFunction` pass converts the compiler's HIR (High-level Intermediate Representation) from a **Control Flow Graph (CFG)** representation to a **tree-based ReactiveFunction** representation that is closer to an AST. This is a critical transformation in the React Compiler pipeline that:
8
9 1. **Restores control flow constructs** - Reconstructs `if`, `while`, `for`, `switch`, and other control flow statements from the CFG's basic blocks and terminals
10 2. **Eliminates phi nodes** - Replaces SSA phi nodes with compound value expressions (ternaries, logical expressions, sequence expressions)
11 3. **Handles labeled break/continue** - Tracks control flow targets to emit explicit labeled `break` and `continue` statements when needed
12 4. **Preserves reactive scope information** - Scope terminals are converted to `ReactiveScopeBlock` nodes in the tree
13
14 ## Input Invariants
15 - HIR is in SSA form (variables have been renamed with unique identifiers)
16 - Basic blocks are connected (valid predecessor/successor relationships)
17 - Each block ends with a valid terminal
18 - Phi nodes exist at merge points for values from different control flow paths
19 - Reactive scopes have been constructed (`scope` terminals exist)
20 - Scope dependencies are computed (`PropagateScopeDependenciesHIR` has run)
21
22 ## Output Guarantees
23 - **Tree structure** - The output is a `ReactiveFunction` with a `body: ReactiveBlock` containing a tree of `ReactiveStatement` nodes
24 - **No CFG structure** - Basic blocks are eliminated; control flow is represented through nested reactive terminals
25 - **No phi nodes** - Value merges are represented as `ConditionalExpression`, `LogicalExpression`, or `SequenceExpression` values
26 - **Labels emitted for all control flow** - Every terminal that can be a break/continue target has a label; unnecessary labels are removed by subsequent `PruneUnusedLabels` pass
27 - **Each block emitted exactly once** - A block cannot be generated twice
28 - **Scope blocks preserved** - `scope` terminals become `ReactiveScopeBlock` nodes
29
30 ## Algorithm
31
32 ### Core Classes
33
34 1. **`Driver`** - Traverses blocks and emits ReactiveBlock arrays
35 2. **`Context`** - Tracks state:
36 - `emitted: Set<BlockId>` - Which blocks have been generated
37 - `#scheduled: Set<BlockId>` - Blocks that will be emitted by parent constructs
38 - `#controlFlowStack: Array<ControlFlowTarget>` - Stack of active break/continue targets
39 - `scopeFallthroughs: Set<BlockId>` - Fallthroughs for scope blocks
40
41 ### Traversal Strategy
42
43 1. Start at the entry block and call `traverseBlock(entryBlock)`
44 2. For each block:
45 - Emit all instructions as `ReactiveInstructionStatement`
46 - Process the terminal based on its kind
47
48 ### Terminal Processing
49
50 **Simple Terminals:**
51 - `return`, `throw` - Emit directly as `ReactiveTerminal`
52 - `unreachable` - No-op
53
54 **Control Flow Terminals:**
55 - `if` - Schedule fallthrough, recursively traverse consequent/alternate, emit `ReactiveIfTerminal`
56 - `while`, `do-while`, `for`, `for-of`, `for-in` - Use `scheduleLoop()` which tracks continue targets
57 - `switch` - Process cases in reverse order
58 - `label` - Schedule fallthrough, traverse inner block
59
60 **Value Terminals (expressions that produce values):**
61 - `ternary`, `logical`, `optional`, `sequence` - Produce `ReactiveValue` compound expressions
62
63 **Break/Continue:**
64 - `goto` with `GotoVariant.Break` - Determine if break is implicit, unlabeled, or labeled
65 - `goto` with `GotoVariant.Continue` - Determine continue type
66
67 **Scope Terminals:**
68 - `scope`, `pruned-scope` - Schedule fallthrough, traverse inner block, emit as `ReactiveScopeBlock`
69
70 ## Key Data Structures
71
72 ### ReactiveFunction
73 ```typescript
74 type ReactiveFunction = {
75 loc: SourceLocation;
76 id: ValidIdentifierName | null;
77 params: Array<Place | SpreadPattern>;
78 generator: boolean;
79 async: boolean;
80 body: ReactiveBlock;
81 env: Environment;
82 directives: Array<string>;
83 };
84 ```
85
86 ### ReactiveBlock
87 ```typescript
88 type ReactiveBlock = Array<ReactiveStatement>;
89 ```
90
91 ### ReactiveStatement
92 ```typescript
93 type ReactiveStatement =
94 | ReactiveInstructionStatement // {kind: 'instruction', instruction}
95 | ReactiveTerminalStatement // {kind: 'terminal', terminal, label}
96 | ReactiveScopeBlock // {kind: 'scope', scope, instructions}
97 | PrunedReactiveScopeBlock; // {kind: 'pruned-scope', ...}
98 ```
99
100 ### ReactiveValue (for compound expressions)
101 ```typescript
102 type ReactiveValue =
103 | InstructionValue // Regular instruction values
104 | ReactiveLogicalValue // a && b, a || b, a ?? b
105 | ReactiveSequenceValue // (a, b, c)
106 | ReactiveTernaryValue // a ? b : c
107 | ReactiveOptionalCallValue; // a?.b()
108 ```
109
110 ### ControlFlowTarget
111 ```typescript
112 type ControlFlowTarget =
113 | {type: 'if'; block: BlockId; id: number}
114 | {type: 'switch'; block: BlockId; id: number}
115 | {type: 'case'; block: BlockId; id: number}
116 | {type: 'loop'; block: BlockId; continueBlock: BlockId; ...};
117 ```
118
119 ## Edge Cases
120
121 ### Nested Control Flow
122 The scheduling mechanism handles arbitrarily nested control flow by pushing/popping from the control flow stack.
123
124 ### Value Blocks with Complex Expressions
125 `SequenceExpression` handles cases where value blocks contain multiple instructions.
126
127 ### Scope Fallthroughs
128 Breaks to scope fallthroughs are treated as implicit (no explicit break needed).
129
130 ### Catch Handlers
131 Scheduled specially via `scheduleCatchHandler()` to prevent re-emission.
132
133 ### Unreachable Blocks
134 The `reachable()` check prevents emitting unreachable blocks.
135
136 ## TODOs
137 The code contains several `CompilerError.throwTodo()` calls for unsupported patterns:
138 1. Optional chaining test blocks must end in `branch`
139 2. Logical expression test blocks must end in `branch`
140 3. Support for value blocks within try/catch statements
141 4. Support for labeled statements combined with value blocks
142
143 ## Example
144
145 ### Fixture: `ternary-expression.js`
146
147 **Input:**
148 ```javascript
149 function ternary(props) {
150 const a = props.a && props.b ? props.c || props.d : (props.e ?? props.f);
151 const b = props.a ? (props.b && props.c ? props.d : props.e) : props.f;
152 return a ? b : null;
153 }
154 ```
155
156 **HIR (CFG with many basic blocks):**
157 The HIR contains 33 basic blocks with `Ternary`, `Logical`, `Branch`, and `Goto` terminals, plus phi nodes at merge points.
158
159 **ReactiveFunction Output (Tree):**
160 ```
161 function ternary(props$62{reactive}) {
162 [1] $84 = Ternary
163 Sequence
164 [2] $66 = Logical
165 Sequence [...]
166 && Sequence [...]
167 ?
168 Sequence [...] // props.c || props.d
169 :
170 Sequence [...] // props.e ?? props.f
171 [40] StoreLocal a$99 = $98
172 ...
173 [82] return $145
174 }
175 ```
176
177 The transformation eliminates:
178 - 33 basic blocks reduced to a single tree
179 - Phi nodes replaced with nested `Ternary` and `Logical` value expressions
180 - CFG edges replaced with tree nesting