main
ts 119 lines 3.56 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 IdentifierId,
10 ReactiveFunction,
11 ReactiveInstruction,
12 ReactiveScopeBlock,
13 isStableType,
14 } from '../HIR';
15 import {eachPatternOperand} from '../HIR/visitors';
16 import {collectReactiveIdentifiers} from './CollectReactiveIdentifiers';
17 import {ReactiveFunctionVisitor, visitReactiveFunction} from './visitors';
18
19 /*
20 * PropagateScopeDependencies infers dependencies without considering whether dependencies
21 * are actually reactive or not (ie, whether their value can change over time).
22 *
23 * This pass prunes dependencies that are guaranteed to be non-reactive.
24 */
25 export function pruneNonReactiveDependencies(fn: ReactiveFunction): void {
26 const reactiveIdentifiers = collectReactiveIdentifiers(fn);
27 visitReactiveFunction(fn, new Visitor(), reactiveIdentifiers);
28 }
29
30 type ReactiveIdentifiers = Set<IdentifierId>;
31
32 class Visitor extends ReactiveFunctionVisitor<ReactiveIdentifiers> {
33 override visitInstruction(
34 instruction: ReactiveInstruction,
35 state: ReactiveIdentifiers,
36 ): void {
37 this.traverseInstruction(instruction, state);
38
39 const {lvalue, value} = instruction;
40 switch (value.kind) {
41 case 'LoadLocal': {
42 if (lvalue !== null && state.has(value.place.identifier.id)) {
43 state.add(lvalue.identifier.id);
44 }
45 break;
46 }
47 case 'StoreLocal': {
48 if (state.has(value.value.identifier.id)) {
49 state.add(value.lvalue.place.identifier.id);
50 if (lvalue !== null) {
51 state.add(lvalue.identifier.id);
52 }
53 }
54 break;
55 }
56 case 'Destructure': {
57 if (state.has(value.value.identifier.id)) {
58 for (const lvalue of eachPatternOperand(value.lvalue.pattern)) {
59 if (isStableType(lvalue.identifier)) {
60 continue;
61 }
62 state.add(lvalue.identifier.id);
63 }
64 if (lvalue !== null) {
65 state.add(lvalue.identifier.id);
66 }
67 }
68 break;
69 }
70 case 'PropertyLoad': {
71 if (
72 lvalue !== null &&
73 state.has(value.object.identifier.id) &&
74 !isStableType(lvalue.identifier)
75 ) {
76 state.add(lvalue.identifier.id);
77 }
78 break;
79 }
80 case 'ComputedLoad': {
81 if (
82 lvalue !== null &&
83 (state.has(value.object.identifier.id) ||
84 state.has(value.property.identifier.id))
85 ) {
86 state.add(lvalue.identifier.id);
87 }
88 break;
89 }
90 }
91 }
92
93 override visitScope(
94 scopeBlock: ReactiveScopeBlock,
95 state: ReactiveIdentifiers,
96 ): void {
97 this.traverseScope(scopeBlock, state);
98 for (const dep of scopeBlock.scope.dependencies) {
99 const isReactive = state.has(dep.identifier.id);
100 if (!isReactive) {
101 scopeBlock.scope.dependencies.delete(dep);
102 }
103 }
104 if (scopeBlock.scope.dependencies.size !== 0) {
105 /**
106 * If any of a scope's dependencies are reactive, then all of its
107 * outputs will re-evaluate whenever those dependencies change.
108 * Mark all of the outputs as reactive to reflect the fact that
109 * they may change in practice based on a reactive input.
110 */
111 for (const [, declaration] of scopeBlock.scope.declarations) {
112 state.add(declaration.identifier.id);
113 }
114 for (const reassignment of scopeBlock.scope.reassignments) {
115 state.add(reassignment.id);
116 }
117 }
118 }
119 }