5
* LICENSE file in the root directory of this source tree.
6
*/
7
8
-import { IdentifierId, ReactiveFunction, ReactiveScopeBlock } from "../HIR";
8
+import {
9
+ IdentifierId,
10
+ ReactiveFunction,
11
+ ReactiveInstruction,
12
+ ReactiveScopeBlock,
13
+ isSetStateType,
14
+} from "../HIR";
15
+import { eachPatternOperand } from "../HIR/visitors";
16
import { collectReactiveIdentifiers } from "./CollectReactiveIdentifiers";
17
import { ReactiveFunctionVisitor, visitReactiveFunction } from "./visitors";
18
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 (isSetStateType(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
+ !isSetStateType(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(
27
- scope: ReactiveScopeBlock,
94
+ scopeBlock: ReactiveScopeBlock,
95
state: ReactiveIdentifiers
96
): void {
30
- this.traverseScope(scope, state);
31
- for (const dep of scope.scope.dependencies) {
97
+ this.traverseScope(scopeBlock, state);
98
+ for (const dep of scopeBlock.scope.dependencies) {
99
const isReactive = state.has(dep.identifier.id);
100
if (!isReactive) {
34
- scope.scope.dependencies.delete(dep);
101
+ scopeBlock.scope.dependencies.delete(dep);
102
}
103
}
37
- if (scope.scope.dependencies.size === 0) {
38
- // If a scope has no dependencies, then its declarations are all non-reactive
39
- for (const [, declaration] of scope.scope.declarations) {
40
- state.delete(declaration.identifier.id);
41
- }
42
- } else {
43
- // otherwise, all the scope's declarations are reactive
44
- for (const [, declaration] of scope.scope.declarations) {
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
}