| 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 {BlockId, HIRFunction, PrunedScopeTerminal} from '../HIR'; |
| 9 | import {assertExhaustive, retainWhere} from '../Utils/utils'; |
| 10 | |
| 11 | /** |
| 12 | * Prunes any reactive scopes that are within a loop (for, while, etc). We don't yet |
| 13 | * support memoization within loops because this would require an extra layer of reconciliation |
| 14 | * (plus a way to identify values across runs, similar to how we use `key` in JSX for lists). |
| 15 | * Eventually we may integrate more deeply into the runtime so that we can do a single level |
| 16 | * of reconciliation, but for now we've found it's sufficient to memoize *around* the loop. |
| 17 | */ |
| 18 | export function flattenReactiveLoopsHIR(fn: HIRFunction): void { |
| 19 | const activeLoops = Array<BlockId>(); |
| 20 | for (const [, block] of fn.body.blocks) { |
| 21 | retainWhere(activeLoops, id => id !== block.id); |
| 22 | const {terminal} = block; |
| 23 | switch (terminal.kind) { |
| 24 | case 'do-while': |
| 25 | case 'for': |
| 26 | case 'for-in': |
| 27 | case 'for-of': |
| 28 | case 'while': { |
| 29 | activeLoops.push(terminal.fallthrough); |
| 30 | break; |
| 31 | } |
| 32 | case 'scope': { |
| 33 | if (activeLoops.length !== 0) { |
| 34 | block.terminal = { |
| 35 | kind: 'pruned-scope', |
| 36 | block: terminal.block, |
| 37 | fallthrough: terminal.fallthrough, |
| 38 | id: terminal.id, |
| 39 | loc: terminal.loc, |
| 40 | scope: terminal.scope, |
| 41 | } as PrunedScopeTerminal; |
| 42 | } |
| 43 | break; |
| 44 | } |
| 45 | case 'branch': |
| 46 | case 'goto': |
| 47 | case 'if': |
| 48 | case 'label': |
| 49 | case 'logical': |
| 50 | case 'maybe-throw': |
| 51 | case 'optional': |
| 52 | case 'pruned-scope': |
| 53 | case 'return': |
| 54 | case 'sequence': |
| 55 | case 'switch': |
| 56 | case 'ternary': |
| 57 | case 'throw': |
| 58 | case 'try': |
| 59 | case 'unreachable': |
| 60 | case 'unsupported': { |
| 61 | break; |
| 62 | } |
| 63 | default: { |
| 64 | assertExhaustive( |
| 65 | terminal, |
| 66 | `Unexpected terminal kind \`${(terminal as any).kind}\``, |
| 67 | ); |
| 68 | } |
| 69 | } |
| 70 | } |
| 71 | } |