main
ts 87 lines 2.44 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 import {
9 BlockId,
10 ReactiveFunction,
11 ReactiveScopeBlock,
12 ReactiveTerminalStatement,
13 makeBlockId,
14 } from '../HIR';
15 import {getOrInsertDefault} from '../Utils/utils';
16 import {ReactiveFunctionVisitor, visitReactiveFunction} from './visitors';
17
18 export function stabilizeBlockIds(fn: ReactiveFunction): void {
19 const referenced: Set<BlockId> = new Set();
20 visitReactiveFunction(fn, new CollectReferencedLabels(), referenced);
21
22 const mappings = new Map<BlockId, BlockId>();
23 for (const blockId of referenced) {
24 mappings.set(blockId, makeBlockId(mappings.size));
25 }
26
27 visitReactiveFunction(fn, new RewriteBlockIds(), mappings);
28 }
29
30 class CollectReferencedLabels extends ReactiveFunctionVisitor<Set<BlockId>> {
31 override visitScope(scope: ReactiveScopeBlock, state: Set<BlockId>): void {
32 const {earlyReturnValue} = scope.scope;
33 if (earlyReturnValue != null) {
34 state.add(earlyReturnValue.label);
35 }
36 this.traverseScope(scope, state);
37 }
38 override visitTerminal(
39 stmt: ReactiveTerminalStatement,
40 state: Set<BlockId>,
41 ): void {
42 if (stmt.label != null) {
43 if (!stmt.label.implicit) {
44 state.add(stmt.label.id);
45 }
46 }
47 this.traverseTerminal(stmt, state);
48 }
49 }
50
51 class RewriteBlockIds extends ReactiveFunctionVisitor<Map<BlockId, BlockId>> {
52 override visitScope(
53 scope: ReactiveScopeBlock,
54 state: Map<BlockId, BlockId>,
55 ): void {
56 const {earlyReturnValue} = scope.scope;
57 if (earlyReturnValue != null) {
58 const rewrittenId = getOrInsertDefault(
59 state,
60 earlyReturnValue.label,
61 state.size,
62 );
63 earlyReturnValue.label = makeBlockId(rewrittenId);
64 }
65 this.traverseScope(scope, state);
66 }
67 override visitTerminal(
68 stmt: ReactiveTerminalStatement,
69 state: Map<BlockId, BlockId>,
70 ): void {
71 if (stmt.label != null) {
72 const rewrittenId = getOrInsertDefault(state, stmt.label.id, state.size);
73 stmt.label.id = makeBlockId(rewrittenId);
74 }
75
76 const terminal = stmt.terminal;
77 if (terminal.kind === 'break' || terminal.kind === 'continue') {
78 const rewrittenId = getOrInsertDefault(
79 state,
80 terminal.target,
81 state.size,
82 );
83 terminal.target = makeBlockId(rewrittenId);
84 }
85 this.traverseTerminal(stmt, state);
86 }
87 }