| 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 | } |