main
ts 66 lines 1.86 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 ReactiveStatement,
12 ReactiveTerminalStatement,
13 } from '../HIR/HIR';
14 import {
15 ReactiveFunctionTransform,
16 Transformed,
17 visitReactiveFunction,
18 } from './visitors';
19
20 /*
21 * Flattens labeled terminals where the label is not reachable, and
22 * nulls out labels for other terminals where the label is unused.
23 */
24 export function pruneUnusedLabels(fn: ReactiveFunction): void {
25 const labels: Labels = new Set();
26 visitReactiveFunction(fn, new Transform(), labels);
27 }
28
29 type Labels = Set<BlockId>;
30
31 class Transform extends ReactiveFunctionTransform<Labels> {
32 override transformTerminal(
33 stmt: ReactiveTerminalStatement,
34 state: Labels,
35 ): Transformed<ReactiveStatement> {
36 this.traverseTerminal(stmt, state);
37 const {terminal} = stmt;
38 if (
39 (terminal.kind === 'break' || terminal.kind === 'continue') &&
40 terminal.targetKind === 'labeled'
41 ) {
42 state.add(terminal.target);
43 }
44 // Is this terminal reachable via a break/continue to its label?
45 const isReachableLabel = stmt.label !== null && state.has(stmt.label.id);
46 if (stmt.terminal.kind === 'label' && !isReachableLabel) {
47 // Flatten labeled terminals where the label isn't necessary
48 const block = [...stmt.terminal.block];
49 const last = block.at(-1);
50 if (
51 last !== undefined &&
52 last.kind === 'terminal' &&
53 last.terminal.kind === 'break' &&
54 last.terminal.target === null
55 ) {
56 block.pop();
57 }
58 return {kind: 'replace-many', value: block};
59 } else {
60 if (!isReachableLabel && stmt.label != null) {
61 stmt.label.implicit = true;
62 }
63 return {kind: 'keep'};
64 }
65 }
66 }