Infer reactive scope dependencies
This builds upon @josephsavona's prior PR #817 to add support for inferring reactive scope dependencies for all instructions and terminals. Still TODO (probably in follow up PRs): - [ ] fix duplicate/different identity scope issue (see this [test fixture](https://github.com/facebook/react-forget/blob/5f3b260aaa3e936fb013716df3ef4e7676b7e9ea/forget/src/__tests__/fixtures/hir/overlapping-scopes-while.expect.md)) - [ ] also collect outputs of each scope - [ ] add new tree visitor that only visits, consider renaming the current `visitTree` to `mapTree` or similar Co-authored-by: Joe Savona <joesavonafb.com>
Lauren Tan committed
Nov 29, 2022 at 17:07 UTC
b3a26f1de27e38b285a16bd013d285857344390a
81 files changed
+872
-25
compiler/forget/packages/playground/components/Editor/Output.tsx
+11
@@ -26,6 +26,7 @@ const {
26
eliminateRedundantPhi,
27
inferReferenceEffects,
28
inferMutableRanges,
29
+ inferReactiveScopeDependencies,
30
leaveSSA,
31
lower,
32
printHIR,
@@ -48,6 +49,7 @@ type CompilerOutput = {
49
eliminateRedundantPhiOutput: string;
50
inferReferenceEffectsOutput: string;
51
inferMutableRangesOutput: string;
52
+ inferReactiveScopeDependenciesOutput: string;
53
leaveSSAOutput: string;
54
codegenOutput: string;
55
sourceMapUrl: string | null;
@@ -81,6 +83,9 @@ function compile(source: string): CompilerOutput | CompilerError {
83
inferMutableRanges(ir);
84
const inferMutableRangesOutput = printHIR(ir.body);
85
86
+ inferReactiveScopeDependencies(ir);
87
+ const inferReactiveScopeDependenciesOutput = printHIR(ir.body);
88
+
89
leaveSSA(ir);
90
const leaveSSAOutput = printHIR(ir.body);
91
@@ -110,6 +115,7 @@ function compile(source: string): CompilerOutput | CompilerError {
115
eliminateRedundantPhiOutput,
116
inferReferenceEffectsOutput,
117
inferMutableRangesOutput,
118
+ inferReactiveScopeDependenciesOutput,
119
leaveSSAOutput,
120
codegenOutput,
121
sourceMapUrl,
@@ -165,6 +171,11 @@ function Output({ store, setTabsOpen, tabsOpen }: Props) {
171
output={compilerOutput.inferMutableRangesOutput}
172
></TextTabContent>
173
),
174
+ InferReactiveScopeDependencies: (
175
+ <TextTabContent
176
+ output={compilerOutput.inferReactiveScopeDependenciesOutput}
177
+ ></TextTabContent>
178
+ ),
179
LeaveSSA: (
180
<TextTabContent
181
output={compilerOutput.leaveSSAOutput}
compiler/forget/packages/playground/components/TabbedWindow.tsx
+2
-2
@@ -5,8 +5,7 @@
5
* LICENSE file in the root directory of this source tree.
6
*/
7
8
-import clsx from "clsx";
9
-import React, { useCallback, useState } from "react";
8
+import React, { useCallback } from "react";
9
10
export type TabTypes =
11
| "HIR"
@@ -14,6 +13,7 @@ export type TabTypes =
13
| "EliminateRedundantPhi"
14
| "InferReferenceEffects"
15
| "InferMutableRanges"
16
+ | "InferReactiveScopeDependencies"
17
| "LeaveSSA"
18
| "JS"
19
| "SourceMap"
compiler/forget/src/Common/utils.ts
+17
@@ -64,3 +64,20 @@ export function hasOwnProperty<T>(obj: T, key: PropertyKey): key is keyof T {
64
export function assertExhaustive(_: never, errorMsg: string): never {
65
throw new Error(errorMsg);
66
}
67
+
68
+/**
69
+ * Modifies @param array in place, retaining only the items where the predicate returns true.
70
+ */
71
+export function retainWhere<T>(
72
+ array: Array<T>,
73
+ predicate: (item: T) => boolean
74
+) {
75
+ let writeIndex = 0;
76
+ for (let readIndex = 0; readIndex < array.length; readIndex++) {
77
+ const item = array[readIndex];
78
+ if (predicate(item) === true) {
79
+ array[writeIndex++] = item;
80
+ }
81
+ }
82
+ array.length = writeIndex;
83
+}
compiler/forget/src/HIR/HIR.ts
+2
@@ -63,6 +63,7 @@ export type HIRFunction = {
63
body: HIR;
64
generator: boolean;
65
async: boolean;
66
+ extra?: string;
67
};
68
69
/**
@@ -327,6 +328,7 @@ export enum Effect {
328
export type ReactiveScope = {
329
id: ScopeId;
330
range: MutableRange;
331
+ dependencies: Set<Place>;
332
};
333
334
/**
compiler/forget/src/HIR/InferReactiveScopeDependencies.ts
new
+195
@@ -0,0 +1,195 @@
1
+/**
2
+ * Copyright (c) Facebook, Inc. and its 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 { assertExhaustive, retainWhere } from "../Common/utils";
9
+import {
10
+ HIRFunction,
11
+ Identifier,
12
+ Instruction,
13
+ InstructionId,
14
+ InstructionValue,
15
+ makeInstructionId,
16
+ Place,
17
+ ReactiveScope,
18
+} from "./HIR";
19
+import { BlockTerminal, Visitor, visitTree } from "./HIRTreeVisitor";
20
+import { printPlace } from "./PrintHIR";
21
+import { eachInstructionValueOperand } from "./visitors";
22
+
23
+export function inferReactiveScopeDependencies(fn: HIRFunction) {
24
+ // TODO: visitTree is more of a transformer visitor, consider adding a new tree visitor
25
+ // that only visits and replacing this usage
26
+ const visitor = new ScopeDependenciesVisitor(fn);
27
+ visitTree(fn, visitor);
28
+
29
+ const output = [];
30
+ for (const scope of visitor.allScopes) {
31
+ if (scope.dependencies.size > 0) {
32
+ output.push(
33
+ `scope${scope.id} [${scope.range.start}:${
34
+ scope.range.end
35
+ }]:\n${Array.from(scope.dependencies)
36
+ .map((p) => " - " + printPlace(p))
37
+ .join("\n")}`
38
+ );
39
+ }
40
+ }
41
+ fn.extra = output.join("\n");
42
+}
43
+
44
+function instructionInScope(instrId: InstructionId, scope: ReactiveScope) {
45
+ return instrId >= scope.range.start && instrId < scope.range.end;
46
+}
47
+
48
+class ScopeDependenciesVisitor
49
+ implements Visitor<void, InstructionValue, void, void>
50
+{
51
+ #identifiers: Map<Identifier, InstructionId> = new Map();
52
+ // Scopes that are currently active at this point in the traversal
53
+ #activeScopes: Set<ReactiveScope> = new Set();
54
+ // All scopes encountered during the traversal
55
+ allScopes: Set<ReactiveScope> = new Set();
56
+
57
+ get #lastActiveScope(): ReactiveScope | null {
58
+ const scopes = [...this.#activeScopes];
59
+ return scopes[scopes.length - 1];
60
+ }
61
+
62
+ constructor(fn: HIRFunction) {
63
+ if (fn.id !== null) {
64
+ this.#identifiers.set(fn.id, makeInstructionId(0));
65
+ }
66
+ for (const param of fn.params) {
67
+ this.#identifiers.set(param.identifier, makeInstructionId(0));
68
+ }
69
+ }
70
+
71
+ #recordActiveScope(scope: ReactiveScope) {
72
+ this.#activeScopes.add(scope);
73
+ this.allScopes.add(scope);
74
+ }
75
+
76
+ /**
77
+ * Prune any scopes that are out of range
78
+ */
79
+ #visitId(id: InstructionId): void {
80
+ const scopes = [...this.#activeScopes];
81
+ retainWhere(scopes, (pending) => pending.range.end > id);
82
+ this.#activeScopes = new Set(scopes);
83
+ }
84
+
85
+ /**
86
+ * Adds a dependency on the terminal operand to the last active scope
87
+ */
88
+ #addTerminalDependency(operand: Place): void {
89
+ const activeScope = this.#lastActiveScope;
90
+ if (activeScope != null) {
91
+ const identId = this.#identifiers.get(operand.identifier);
92
+ if (identId !== undefined && identId < activeScope.range.start) {
93
+ activeScope.dependencies.add(operand);
94
+ }
95
+ }
96
+ }
97
+
98
+ visitTerminal(
99
+ terminal: BlockTerminal<void, InstructionValue, void, void>
100
+ ): void {
101
+ switch (terminal.kind) {
102
+ case "if":
103
+ case "switch":
104
+ case "while": {
105
+ for (const operand of eachInstructionValueOperand(terminal.test)) {
106
+ this.#addTerminalDependency(operand);
107
+ }
108
+ break;
109
+ }
110
+ case "return":
111
+ case "throw": {
112
+ if (terminal.value != null) {
113
+ for (const operand of eachInstructionValueOperand(terminal.value)) {
114
+ this.#addTerminalDependency(operand);
115
+ }
116
+ }
117
+ break;
118
+ }
119
+ case "continue":
120
+ case "break": {
121
+ break;
122
+ }
123
+ default:
124
+ assertExhaustive(terminal, `unhandled terminal ${terminal}`);
125
+ }
126
+ }
127
+
128
+ visitTerminalId(id: InstructionId): void {
129
+ this.#visitId(id);
130
+ }
131
+
132
+ /**
133
+ * We don't need to map to a different representation, so just return the value directly
134
+ */
135
+ visitValue(value: InstructionValue, id: InstructionId): InstructionValue {
136
+ return value;
137
+ }
138
+
139
+ visitInstruction(instr: Instruction, _value: InstructionValue): void {
140
+ const { lvalue, value } = instr;
141
+ if (lvalue !== null && lvalue.place.memberPath === null) {
142
+ if (!this.#identifiers.has(lvalue.place.identifier)) {
143
+ this.#identifiers.set(lvalue.place.identifier, instr.id);
144
+ }
145
+ }
146
+
147
+ const activeScopes: Set<ReactiveScope> = new Set();
148
+ const dependencies: Array<Place> = [];
149
+ if (lvalue != null) {
150
+ if (
151
+ lvalue.place.identifier.scope !== null &&
152
+ lvalue.place.memberPath === null &&
153
+ instructionInScope(instr.id, lvalue.place.identifier.scope)
154
+ ) {
155
+ activeScopes.add(lvalue.place.identifier.scope);
156
+ } else {
157
+ dependencies.push(lvalue.place);
158
+ }
159
+ }
160
+ for (const operand of eachInstructionValueOperand(value)) {
161
+ if (
162
+ operand.identifier.scope !== null &&
163
+ instructionInScope(instr.id, operand.identifier.scope)
164
+ ) {
165
+ activeScopes.add(operand.identifier.scope);
166
+ } else {
167
+ dependencies.push(operand);
168
+ }
169
+ }
170
+
171
+ for (const scope of activeScopes) {
172
+ this.#recordActiveScope(scope);
173
+ }
174
+
175
+ if (dependencies.length > 0) {
176
+ const scope = this.#lastActiveScope;
177
+ if (scope != null) {
178
+ for (const dep of dependencies) {
179
+ const identId = this.#identifiers.get(dep.identifier);
180
+ if (identId !== undefined && identId < scope.range.start) {
181
+ scope.dependencies.add(dep);
182
+ }
183
+ }
184
+ }
185
+ }
186
+
187
+ this.#visitId(instr.id);
188
+ }
189
+
190
+ enterBlock(): void {}
191
+ visitImplicitTerminal(): void | null {}
192
+ visitCase(test: InstructionValue, block: void): void {}
193
+ appendBlock(block: void, item: void, label?: string | undefined): void {}
194
+ leaveBlock(block: void): void {}
195
+}
compiler/forget/src/HIR/InferReactiveScopeVariables.ts
+1
@@ -112,6 +112,7 @@ export function inferReactiveScopeVariables(fn: HIRFunction) {
112
scope = {
113
id: makeScopeId(scopes.size),
114
range: identifier.mutableRange,
115
+ dependencies: new Set(),
116
};
117
scopes.set(groupIdentifier, scope);
118
} else {
compiler/forget/src/HIR/InferReactiveScopes.ts
+1
-14
@@ -6,6 +6,7 @@
6
*/
7
8
import invariant from "invariant";
9
+import { retainWhere } from "../Common/utils";
10
import DisjointSet from "./DisjointSet";
11
import {
12
HIRFunction,
@@ -395,17 +396,3 @@ function getInstructionScope(instr: Instruction): ReactiveScope | null {
396
}
397
return null;
398
}
398
-
399
-/**
400
- * Modifies @param array, retaining only the items where the predicate returns true.
401
- */
402
-function retainWhere<T>(array: Array<T>, predicate: (item: T) => boolean) {
403
- let writeIndex = 0;
404
- for (let readIndex = 0; readIndex < array.length; readIndex++) {
405
- const item = array[readIndex];
406
- if (predicate(item) === true) {
407
- array[writeIndex++] = item;
408
- }
409
- }
410
- array.length = writeIndex;
411
-}
compiler/forget/src/HIR/Pipeline.ts
+5
@@ -15,6 +15,7 @@ import inferReferenceEffects from "../HIR/InferReferenceEffects";
15
import leaveSSA from "../HIR/LeaveSSA";
16
import codegen from "./Codegen";
17
import { HIRFunction } from "./HIR";
18
+import { inferReactiveScopeDependencies } from "./InferReactiveScopeDependencies";
19
import { inferReactiveScopes } from "./InferReactiveScopes";
20
import { inferReactiveScopeVariables } from "./InferReactiveScopeVariables";
21
@@ -24,6 +25,7 @@ export type CompilerFlags = {
25
inferMutableRanges: boolean;
26
inferReactiveScopeVariables: boolean;
27
inferReactiveScopes: boolean;
28
+ inferReactiveScopeDependencies: boolean;
29
leaveSSA: boolean;
30
codegen: boolean;
31
};
@@ -55,6 +57,9 @@ export default function (
57
if (flags.inferReactiveScopes) {
58
inferReactiveScopes(ir);
59
}
60
+ if (flags.inferReactiveScopeDependencies) {
61
+ inferReactiveScopeDependencies(ir);
62
+ }
63
if (flags.leaveSSA) {
64
leaveSSA(ir);
65
}
compiler/forget/src/HIR/PrintHIR.ts
+11
-1
@@ -10,6 +10,7 @@ import { assertExhaustive } from "../Common/utils";
10
import {
11
GotoVariant,
12
HIR,
13
+ HIRFunction,
14
Identifier,
15
Instruction,
16
InstructionKind,
@@ -26,6 +27,15 @@ export type Options = {
27
indent: number;
28
};
29
30
+export function printFunction(fn: HIRFunction): string {
31
+ const output = [];
32
+ output.push(printHIR(fn.body));
33
+ if (fn.extra !== undefined) {
34
+ output.push(fn.extra);
35
+ }
36
+ return output.join("\n");
37
+}
38
+
39
export default function printHIR(
40
ir: HIR,
41
options: Options | null = null
@@ -111,7 +121,7 @@ function printPhi(phi: Phi): string {
121
return items.join("");
122
}
123
114
-function printTerminal(terminal: Terminal): Array<string> | string {
124
+export function printTerminal(terminal: Terminal): Array<string> | string {
125
let value;
126
switch (terminal.kind) {
127
case "if": {
compiler/forget/src/__tests__/fixtures/hir/alias-while.expect.md
+2
@@ -51,6 +51,7 @@ bb2:
51
[13] read b$14_@0
52
[14] read c$16_@0
53
[15] Return freeze a$12_@0
54
+
55
```
56
57
### CFG
@@ -125,6 +126,7 @@ function foo$0(cond$1) {
126
```
127
bb0:
128
[1] Return
129
+
130
```
131
132
### CFG
compiler/forget/src/__tests__/fixtures/hir/assignment-variations.expect.md
+11
@@ -28,6 +28,15 @@ bb0:
28
[6] Const mutate $10_@5 = 1
29
[7] Reassign mutate x$11_@6 = Binary read x$9_@4 >>> read $10_@5
30
[8] Return
31
+scope2 [3:4]:
32
+ - read x$5_@0
33
+ - read $6_@1
34
+scope4 [5:6]:
35
+ - read x$7_@2
36
+ - read $8_@3
37
+scope6 [7:8]:
38
+ - read x$9_@4
39
+ - read $10_@5
40
```
41
42
### CFG
@@ -72,6 +81,8 @@ bb0:
81
[3] Const mutate $6_@2 = 2
82
[4] Reassign mutate a$4_@1.b.c[0:5] = Binary read a$4_@1.b.c * read $6_@2
83
[5] Return
84
+scope2 [3:4]:
85
+ - mutate a$4_@1.b.c
86
```
87
88
### CFG
compiler/forget/src/__tests__/fixtures/hir/call.expect.md
+7
@@ -20,6 +20,7 @@ function Component(props) {
20
```
21
bb0:
22
[1] Return
23
+
24
```
25
26
### CFG
@@ -54,6 +55,12 @@ bb0:
55
[7] Const mutate $14_@3 = "div"
56
[8] Const mutate $15_@4 = JSX <read $14_@3 a={read a$10_@0} b={freeze b$11_@0} ></read $14_@3>
57
[9] Return read $15_@4
58
+scope2 [5:6]:
59
+ - read $12_@1
60
+scope4 [8:9]:
61
+ - read $14_@3
62
+ - read a$10_@0
63
+ - freeze b$11_@0
64
```
65
66
### CFG
compiler/forget/src/__tests__/fixtures/hir/complex-while.expect.md
+1
@@ -34,6 +34,7 @@ bb7:
34
bb1:
35
predecessor blocks: bb6 bb4 bb0
36
[6] Return
37
+
38
```
39
40
### CFG
compiler/forget/src/__tests__/fixtures/hir/component.expect.md
+4
@@ -88,6 +88,10 @@ bb2:
88
[32] Const mutate $59_@17 = "\n "
89
[33] Const mutate $60_@18 = JSX <read $53_@11>{read $54_@12}{read $57_@15}{read $58_@16}{freeze renderedItems$29_@2}{read $59_@17}</read $53_@11>
90
[34] Return read $60_@18
91
+scope0 [1:2]:
92
+ - read props$26.items
93
+scope1 [2:3]:
94
+ - read props$26.maxItems
95
```
96
97
### CFG
compiler/forget/src/__tests__/fixtures/hir/conditional-break.expect.md
+23
@@ -89,6 +89,10 @@ bb1:
89
predecessor blocks: bb0
90
[6] Call mutate a_DEBUG$5_@0.push(read props$4.d)
91
[7] Return freeze a_DEBUG$5_@0
92
+scope0 [1:7]:
93
+ - read props$4.a
94
+ - read props$4.b
95
+ - read props$4.d
96
```
97
98
### CFG
@@ -152,6 +156,11 @@ bb1:
156
predecessor blocks: bb2 bb0
157
[6] Call mutate a$4_@0.push(read props$3.d)
158
[7] Return freeze a$4_@0
159
+scope0 [1:7]:
160
+ - read props$3.a
161
+ - read props$3.c
162
+ - read props$3.b
163
+ - read props$3.d
164
```
165
166
### CFG
@@ -217,6 +226,11 @@ bb1:
226
predecessor blocks: bb0
227
[7] Call mutate a$5_@0.push(read props$4.d)
228
[8] Return freeze a$5_@0
229
+scope0 [1:8]:
230
+ - read props$4.a
231
+ - read props$4.c
232
+ - read props$4.b
233
+ - read props$4.d
234
```
235
236
### CFG
@@ -282,6 +296,11 @@ bb1:
296
predecessor blocks: bb0
297
[6] Call mutate a$4_@0.push(read props$3.d)
298
[7] Return freeze a$4_@0
299
+scope0 [1:7]:
300
+ - read props$3.a
301
+ - read props$3.c
302
+ - read props$3.b
303
+ - read props$3.d
304
```
305
306
### CFG
@@ -346,6 +365,10 @@ bb1:
365
predecessor blocks: bb0 bb2
366
[6] Call mutate a$4_@0.push(read props$3.d)
367
[7] Return freeze a$4_@0
368
+scope0 [1:7]:
369
+ - read props$3.a
370
+ - read props$3.d
371
+ - read props$3.c
372
```
373
374
### CFG
compiler/forget/src/__tests__/fixtures/hir/conditional-on-mutable.expect.md
+12
@@ -53,6 +53,13 @@ bb3:
53
predecessor blocks: bb4 bb1
54
[9] Const mutate $16_@2 = JSX <read Foo$4 a={freeze a$7_@0} b={freeze b$8_@0} ></read Foo$4>
55
[10] Return read $16_@2
56
+scope0 [1:9]:
57
+ - read props$6.p0
58
+ - read props$6.p2
59
+ - read props$6.p1
60
+scope2 [9:10]:
61
+ - freeze a$7_@0
62
+ - freeze b$8_@0
63
```
64
65
### CFG
@@ -140,6 +147,9 @@ bb3:
147
predecessor blocks: bb4 bb1
148
[10] Const mutate $19_@2 = JSX <read Foo$6 a={freeze a$9_@0} b={freeze b$10_@0} ></read Foo$6>
149
[11] Return read $19_@2
150
+scope2 [10:11]:
151
+ - freeze a$9_@0
152
+ - freeze b$10_@0
153
```
154
155
### CFG
@@ -210,6 +220,7 @@ function Component$0(props$1) {
220
```
221
bb0:
222
[1] Return
223
+
224
```
225
226
### CFG
@@ -236,6 +247,7 @@ function Foo$0() {}
247
```
248
bb0:
249
[1] Return
250
+
251
```
252
253
### CFG
compiler/forget/src/__tests__/fixtures/hir/constructor.expect.md
+7
@@ -20,6 +20,7 @@ function Component(props) {
20
```
21
bb0:
22
[1] Return
23
+
24
```
25
26
### CFG
@@ -54,6 +55,12 @@ bb0:
55
[7] Const mutate $14_@3 = "div"
56
[8] Const mutate $15_@4 = JSX <read $14_@3 a={read a$10_@0} b={freeze b$11_@0} ></read $14_@3>
57
[9] Return read $15_@4
58
+scope2 [5:6]:
59
+ - read $12_@1
60
+scope4 [8:9]:
61
+ - read $14_@3
62
+ - read a$10_@0
63
+ - freeze b$11_@0
64
```
65
66
### CFG
compiler/forget/src/__tests__/fixtures/hir/dependencies-outputs.expect.md
new
+118
@@ -0,0 +1,118 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function foo(a, b) {
6
+ const x = [];
7
+ x.push(a);
8
+ <div>{x}</div>;
9
+
10
+ const y = [];
11
+ if (x.length) {
12
+ y.push(x);
13
+ }
14
+ if (b) {
15
+ y.push(b);
16
+ }
17
+}
18
+
19
+```
20
+
21
+## HIR
22
+
23
+```
24
+bb0:
25
+ [1] Const mutate x$8_@0[1:3] = Array []
26
+ [2] Call mutate x$8_@0.push(read a$6)
27
+ [3] Const mutate $9_@1 = "div"
28
+ [4] JSX <read $9_@1>{freeze x$8_@0}</read $9_@1>
29
+ [5] Const mutate y$10_@2[5:12] = Array []
30
+ [6] If (read x$8_@0.length) then:bb2 else:bb1
31
+bb2:
32
+ predecessor blocks: bb0
33
+ [7] Call mutate y$10_@2.push(read x$8_@0)
34
+ [8] Goto bb1
35
+bb1:
36
+ predecessor blocks: bb2 bb0
37
+ [9] If (read b$7) then:bb4 else:bb3
38
+bb4:
39
+ predecessor blocks: bb1
40
+ [10] Call mutate y$10_@2.push(read b$7)
41
+ [11] Goto bb3
42
+bb3:
43
+ predecessor blocks: bb4 bb1
44
+ [12] Return
45
+scope0 [1:3]:
46
+ - read a$6
47
+scope1 [3:4]:
48
+ - freeze x$8_@0
49
+scope2 [5:12]:
50
+ - read x$8_@0
51
+ - read x$8_@0.length
52
+ - read b$7
53
+ - read b$7
54
+```
55
+
56
+### CFG
57
+
58
+```mermaid
59
+flowchart TB
60
+ %% Basic Blocks
61
+ subgraph bb0
62
+ bb0_instrs["
63
+ [1] Const mutate x$8_@0[1:3] = Array []
64
+ [2] Call mutate x$8_@0.push(read a$6)
65
+ [3] Const mutate $9_@1 = 'div'
66
+ [4] JSX <read $9_@1>{freeze x$8_@0}</read $9_@1>
67
+ [5] Const mutate y$10_@2[5:12] = Array []
68
+ "]
69
+ bb0_instrs --> bb0_terminal(["If (read x$8_@0.length)"])
70
+ end
71
+ subgraph bb2
72
+ bb2_instrs["
73
+ [7] Call mutate y$10_@2.push(read x$8_@0)
74
+ "]
75
+ bb2_instrs --> bb2_terminal(["Goto"])
76
+ end
77
+ subgraph bb1
78
+ bb1_terminal(["If (read b$7)"])
79
+ end
80
+ subgraph bb4
81
+ bb4_instrs["
82
+ [10] Call mutate y$10_@2.push(read b$7)
83
+ "]
84
+ bb4_instrs --> bb4_terminal(["Goto"])
85
+ end
86
+ subgraph bb3
87
+ bb3_terminal(["Return"])
88
+ end
89
+
90
+ %% Jumps
91
+ bb0_terminal -- "then" --> bb2
92
+ bb0_terminal -- "else" --> bb1
93
+ bb2_terminal --> bb1
94
+ bb1_terminal -- "then" --> bb4
95
+ bb1_terminal -- "else" --> bb3
96
+ bb4_terminal --> bb3
97
+
98
+```
99
+
100
+## Code
101
+
102
+```javascript
103
+function foo$0(a$1, b$2) {
104
+ const x$3 = [];
105
+ x$3.push(a$1);
106
+ <div>{x$3}</div>;
107
+ const y$5 = [];
108
+ bb1: if (x$3.length) {
109
+ y$5.push(x$3);
110
+ }
111
+
112
+ bb3: if (b$2) {
113
+ y$5.push(b$2);
114
+ }
115
+}
116
+
117
+```
118
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/dependencies-outputs.js
new
+13
@@ -0,0 +1,13 @@
1
+function foo(a, b) {
2
+ const x = [];
3
+ x.push(a);
4
+ <div>{x}</div>;
5
+
6
+ const y = [];
7
+ if (x.length) {
8
+ y.push(x);
9
+ }
10
+ if (b) {
11
+ y.push(b);
12
+ }
13
+}
compiler/forget/src/__tests__/fixtures/hir/dependencies.expect.md
new
+116
@@ -0,0 +1,116 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function foo(x, y, z) {
6
+ const items = [z];
7
+ items.push(x);
8
+
9
+ const items2 = [];
10
+ if (x) {
11
+ items2.push(y);
12
+ }
13
+
14
+ if (y) {
15
+ items.push(x);
16
+ }
17
+
18
+ return items2;
19
+}
20
+
21
+```
22
+
23
+## HIR
24
+
25
+```
26
+bb0:
27
+ [1] Const mutate items$9_@0[1:10] = Array [read z$8]
28
+ [2] Call mutate items$9_@0.push(read x$6)
29
+ [3] Const mutate items2$10_@1[3:7] = Array []
30
+ [4] If (read x$6) then:bb2 else:bb1
31
+bb2:
32
+ predecessor blocks: bb0
33
+ [5] Call mutate items2$10_@1.push(read y$7)
34
+ [6] Goto bb1
35
+bb1:
36
+ predecessor blocks: bb2 bb0
37
+ [7] If (read y$7) then:bb4 else:bb3
38
+bb4:
39
+ predecessor blocks: bb1
40
+ [8] Call mutate items$9_@0.push(read x$6)
41
+ [9] Goto bb3
42
+bb3:
43
+ predecessor blocks: bb4 bb1
44
+ [10] Return freeze items2$10_@1
45
+scope0 [1:10]:
46
+ - read z$8
47
+ - read x$6
48
+ - read x$6
49
+ - read y$7
50
+scope1 [3:7]:
51
+ - read y$7
52
+ - read x$6
53
+```
54
+
55
+### CFG
56
+
57
+```mermaid
58
+flowchart TB
59
+ %% Basic Blocks
60
+ subgraph bb0
61
+ bb0_instrs["
62
+ [1] Const mutate items$9_@0[1:10] = Array [read z$8]
63
+ [2] Call mutate items$9_@0.push(read x$6)
64
+ [3] Const mutate items2$10_@1[3:7] = Array []
65
+ "]
66
+ bb0_instrs --> bb0_terminal(["If (read x$6)"])
67
+ end
68
+ subgraph bb2
69
+ bb2_instrs["
70
+ [5] Call mutate items2$10_@1.push(read y$7)
71
+ "]
72
+ bb2_instrs --> bb2_terminal(["Goto"])
73
+ end
74
+ subgraph bb1
75
+ bb1_terminal(["If (read y$7)"])
76
+ end
77
+ subgraph bb4
78
+ bb4_instrs["
79
+ [8] Call mutate items$9_@0.push(read x$6)
80
+ "]
81
+ bb4_instrs --> bb4_terminal(["Goto"])
82
+ end
83
+ subgraph bb3
84
+ bb3_terminal(["Return freeze items2$10_@1"])
85
+ end
86
+
87
+ %% Jumps
88
+ bb0_terminal -- "then" --> bb2
89
+ bb0_terminal -- "else" --> bb1
90
+ bb2_terminal --> bb1
91
+ bb1_terminal -- "then" --> bb4
92
+ bb1_terminal -- "else" --> bb3
93
+ bb4_terminal --> bb3
94
+
95
+```
96
+
97
+## Code
98
+
99
+```javascript
100
+function foo$0(x$1, y$2, z$3) {
101
+ const items$4 = [z$3];
102
+ items$4.push(x$1);
103
+ const items2$5 = [];
104
+ bb1: if (x$1) {
105
+ items2$5.push(y$2);
106
+ }
107
+
108
+ bb3: if (y$2) {
109
+ items$4.push(x$1);
110
+ }
111
+
112
+ return items2$5;
113
+}
114
+
115
+```
116
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/dependencies.js
new
+15
@@ -0,0 +1,15 @@
1
+function foo(x, y, z) {
2
+ const items = [z];
3
+ items.push(x);
4
+
5
+ const items2 = [];
6
+ if (x) {
7
+ items2.push(y);
8
+ }
9
+
10
+ if (y) {
11
+ items.push(x);
12
+ }
13
+
14
+ return items2;
15
+}
compiler/forget/src/__tests__/fixtures/hir/extend-scopes-if.expect.md
+4
@@ -46,6 +46,10 @@ bb7:
46
predecessor blocks: bb1
47
[10] Const mutate $13_@2 = null
48
[11] Return read $13_@2
49
+scope0 [1:8]:
50
+ - read c$9
51
+ - read b$8
52
+ - read a$7
53
```
54
55
### CFG
compiler/forget/src/__tests__/fixtures/hir/frozen-after-alias.expect.md
+3
@@ -23,6 +23,7 @@ bb0:
23
[3] Call read useFreeze$3(freeze a$5_@0)
24
[4] Call mutate foo$4_@1(read b$6_@0)
25
[5] Return
26
+
27
```
28
29
### CFG
@@ -60,6 +61,7 @@ function Component$0() {
61
```
62
bb0:
63
[1] Return
64
+
65
```
66
67
### CFG
@@ -86,6 +88,7 @@ function useFreeze$0() {}
88
```
89
bb0:
90
[1] Return
91
+
92
```
93
94
### CFG
compiler/forget/src/__tests__/fixtures/hir/hook-call.expect.md
+11
@@ -24,6 +24,7 @@ function Component(props) {
24
```
25
bb0:
26
[1] Return
27
+
28
```
29
30
### CFG
@@ -50,6 +51,7 @@ function useFreeze$0() {}
51
```
52
bb0:
53
[1] Return
54
+
55
```
56
57
### CFG
@@ -83,6 +85,15 @@ bb0:
85
[6] Const mutate $15_@5 = "\n "
86
[7] Const mutate $16_@6 = JSX <read Component$0>{read $13_@3}{read x$11_@0}{read $14_@4}{read y$12_@1}{read $15_@5}</read Component$0>
87
[8] Return read $16_@6
88
+scope1 [2:3]:
89
+ - freeze x$11_@0
90
+scope6 [7:8]:
91
+ - read Component$0
92
+ - read $13_@3
93
+ - read x$11_@0
94
+ - read $14_@4
95
+ - read y$12_@1
96
+ - read $15_@5
97
```
98
99
### CFG
compiler/forget/src/__tests__/fixtures/hir/hooks-freeze-arguments.expect.md
+3
@@ -24,6 +24,7 @@ bb0:
24
[3] Call read useFreeze$2(read a$4_@0)
25
[4] Call mutate call$3_@1(read a$4_@0)
26
[5] Return read a$4_@0
27
+
28
```
29
30
### CFG
@@ -62,6 +63,7 @@ function Component$0() {
63
```
64
bb0:
65
[1] Return
66
+
67
```
68
69
### CFG
@@ -88,6 +90,7 @@ function useFreeze$0(x$1) {}
90
```
91
bb0:
92
[1] Return
93
+
94
```
95
96
### CFG
compiler/forget/src/__tests__/fixtures/hir/hooks-freeze-possibly-mutable-arguments.expect.md
+7
@@ -45,6 +45,11 @@ bb1:
45
[10] Call read useFreeze$5(read a$14_@1)
46
[11] Call mutate call$6_@3(read a$14_@1)
47
[12] Return read a$14_@1
48
+scope0 [1:2]:
49
+ - read props$7.cond
50
+scope1 [2:9]:
51
+ - read props$7.x
52
+ - read cond$8_@0
53
```
54
55
### CFG
@@ -115,6 +120,7 @@ function Component$0(props$1) {
120
```
121
bb0:
122
[1] Return
123
+
124
```
125
126
### CFG
@@ -141,6 +147,7 @@ function useFreeze$0(x$1) {}
147
```
148
bb0:
149
[1] Return
150
+
151
```
152
153
### CFG
compiler/forget/src/__tests__/fixtures/hir/independent-across-if.expect.md
+7
@@ -38,6 +38,7 @@ function Component(props) {
38
```
39
bb0:
40
[1] Return
41
+
42
```
43
44
### CFG
@@ -64,6 +65,7 @@ function compute$0() {}
65
```
66
bb0:
67
[1] Return
68
+
69
```
70
71
### CFG
@@ -90,6 +92,7 @@ function mutate$0() {}
92
```
93
bb0:
94
[1] Return
95
+
96
```
97
98
### CFG
@@ -116,6 +119,7 @@ function foo$0() {}
119
```
120
bb0:
121
[1] Return
122
+
123
```
124
125
### CFG
@@ -153,6 +157,9 @@ bb1:
157
predecessor blocks: bb2 bb0
158
[7] Const mutate $14_@1 = JSX <read Foo$6 a={freeze a$9_@0} b={freeze b$10_@0} ></read Foo$6>
159
[8] Return read $14_@1
160
+scope1 [7:8]:
161
+ - freeze a$9_@0
162
+ - freeze b$10_@0
163
```
164
165
### CFG
compiler/forget/src/__tests__/fixtures/hir/independent.expect.md
+6
@@ -32,6 +32,9 @@ bb0:
32
[2] Const mutate b$9_@0[0:3] = Call mutate compute$3_@0(read props$7.b)
33
[3] Const mutate $10_@1 = JSX <read Foo$5 a={freeze a$8_@0} b={freeze b$9_@0} ></read Foo$5>
34
[4] Return read $10_@1
35
+scope1 [3:4]:
36
+ - freeze a$8_@0
37
+ - freeze b$9_@0
38
```
39
40
### CFG
@@ -67,6 +70,7 @@ function Component$0(props$1) {
70
```
71
bb0:
72
[1] Return
73
+
74
```
75
76
### CFG
@@ -93,6 +97,7 @@ function compute$0() {}
97
```
98
bb0:
99
[1] Return
100
+
101
```
102
103
### CFG
@@ -119,6 +124,7 @@ function foo$0() {}
124
```
125
bb0:
126
[1] Return
127
+
128
```
129
130
### CFG
compiler/forget/src/__tests__/fixtures/hir/interdependent-across-if.expect.md
+6
@@ -32,6 +32,7 @@ function Component(props) {
32
```
33
bb0:
34
[1] Return
35
+
36
```
37
38
### CFG
@@ -58,6 +59,7 @@ function compute$0() {}
59
```
60
bb0:
61
[1] Return
62
+
63
```
64
65
### CFG
@@ -84,6 +86,7 @@ function foo$0() {}
86
```
87
bb0:
88
[1] Return
89
+
90
```
91
92
### CFG
@@ -120,6 +123,9 @@ bb1:
123
predecessor blocks: bb2 bb0
124
[6] Const mutate $14_@1 = JSX <read Foo$6 a={freeze a$9_@0} b={freeze b$10_@0} ></read Foo$6>
125
[7] Return read $14_@1
126
+scope1 [6:7]:
127
+ - freeze a$9_@0
128
+ - freeze b$10_@0
129
```
130
131
### CFG
compiler/forget/src/__tests__/fixtures/hir/interdependent.expect.md
+6
@@ -33,6 +33,9 @@ bb0:
33
[3] Call mutate foo$5_@0(mutate a$9_@0, mutate b$10_@0)
34
[4] Const mutate $11_@1 = JSX <read Foo$6 a={freeze a$9_@0} b={freeze b$10_@0} ></read Foo$6>
35
[5] Return read $11_@1
36
+scope1 [4:5]:
37
+ - freeze a$9_@0
38
+ - freeze b$10_@0
39
```
40
41
### CFG
@@ -70,6 +73,7 @@ function Component$0(props$1) {
73
```
74
bb0:
75
[1] Return
76
+
77
```
78
79
### CFG
@@ -96,6 +100,7 @@ function compute$0() {}
100
```
101
bb0:
102
[1] Return
103
+
104
```
105
106
### CFG
@@ -122,6 +127,7 @@ function foo$0() {}
127
```
128
bb0:
129
[1] Return
130
+
131
```
132
133
### CFG
compiler/forget/src/__tests__/fixtures/hir/inverted-if.expect.md
+4
@@ -30,6 +30,10 @@ bb5:
30
bb1:
31
predecessor blocks: bb5 bb3 bb0
32
[6] Return
33
+scope0 [1:6]:
34
+ - read c$7
35
+ - read b$6
36
+ - read a$5
37
```
38
39
### CFG
compiler/forget/src/__tests__/fixtures/hir/jsx-fragment.expect.md
+14
@@ -31,6 +31,20 @@ bb0:
31
[10] Const mutate $23_@9 = "\n "
32
[11] Const mutate $24_@10 = JsxFragment [read $14_@0, read props$13.greeting, read $15_@1, read $16_@2, read $22_@8, read $23_@9]
33
[12] Return read $24_@10
34
+scope6 [7:8]:
35
+ - read $19_@5
36
+scope8 [9:10]:
37
+ - read $17_@3
38
+ - read $18_@4
39
+ - read $20_@6
40
+ - read $21_@7
41
+scope10 [11:12]:
42
+ - read $14_@0
43
+ - read props$13.greeting
44
+ - read $15_@1
45
+ - read $16_@2
46
+ - read $22_@8
47
+ - read $23_@9
48
```
49
50
### CFG
compiler/forget/src/__tests__/fixtures/hir/logical-expression.expect.md
+5
@@ -37,6 +37,7 @@ bb1:
37
predecessor blocks: bb2 bb3
38
$8_@0[0:7]: phi(bb2: $6_@0, bb3: $7_@0)
39
[7] Return freeze $8_@0
40
+
41
```
42
43
### CFG
@@ -104,6 +105,7 @@ bb1:
105
predecessor blocks: bb2 bb3
106
$8_@0[0:7]: phi(bb2: $6_@0, bb3: $7_@0)
107
[7] Return freeze $8_@0
108
+
109
```
110
111
### CFG
@@ -173,6 +175,7 @@ bb1:
175
predecessor blocks: bb2 bb3
176
$14_@0[0:9]: phi(bb2: $12_@0, bb3: $13_@0)
177
[9] Return freeze $14_@0
178
+
179
```
180
181
### CFG
@@ -229,6 +232,7 @@ function QuestionQuestion$0(props$1) {
232
```
233
bb0:
234
[1] Return
235
+
236
```
237
238
### CFG
@@ -255,6 +259,7 @@ function f$0() {}
259
```
260
bb0:
261
[1] Return
262
+
263
```
264
265
### CFG
compiler/forget/src/__tests__/fixtures/hir/mutable-lifetime-loops.expect.md
+3
@@ -44,6 +44,7 @@ function Component(props) {
44
```
45
bb0:
46
[1] Return
47
+
48
```
49
50
### CFG
@@ -70,6 +71,7 @@ function mutate$0(x$1, y$2) {}
71
```
72
bb0:
73
[1] Return
74
+
75
```
76
77
### CFG
@@ -142,6 +144,7 @@ bb13:
144
[21] Const mutate $34_@2 = null
145
[22] Call mutate mutate$7_@0(mutate d$33_@0, read $34_@2)
146
[23] Return
147
+
148
```
149
150
### CFG
compiler/forget/src/__tests__/fixtures/hir/mutable-lifetime-with-aliasing.expect.md
+3
@@ -40,6 +40,7 @@ function Component(props) {
40
```
41
bb0:
42
[1] Return
43
+
44
```
45
46
### CFG
@@ -90,6 +91,8 @@ bb9:
91
[13] Const mutate $17_@4 = null
92
[14] Call mutate mutate$8_@3(mutate x$15_@3, read $17_@4)
93
[15] Return
94
+scope1 [2:3]:
95
+ - read a$11_@0
96
```
97
98
### CFG
compiler/forget/src/__tests__/fixtures/hir/mutable-liverange-loop.expect.md
+3
@@ -39,6 +39,7 @@ function Component(props) {
39
```
40
bb0:
41
[1] Return
42
+
43
```
44
45
### CFG
@@ -65,6 +66,7 @@ function mutate$0() {}
66
```
67
bb0:
68
[1] Return
69
+
70
```
71
72
### CFG
@@ -124,6 +126,7 @@ bb13:
126
[16] Const mutate $28_@3 = null
127
[17] Call mutate mutate$6_@0(mutate d$15_@0, read $28_@3)
128
[18] Return
129
+
130
```
131
132
### CFG
compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-interleaved-by-terminal.expect.md
+3
@@ -27,6 +27,9 @@ bb1:
27
[4] Call mutate y$10_@0.push(read a$6)
28
[5] Call mutate x$9_@0.push(read b$7)
29
[6] Return
30
+scope0 [1:6]:
31
+ - read a$6
32
+ - read b$7
33
```
34
35
### CFG
compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-interleaved.expect.md
+3
@@ -20,6 +20,9 @@ bb0:
20
[3] Call mutate x$7_@0.push(read a$5)
21
[4] Call mutate y$8_@0.push(read b$6)
22
[5] Return
23
+scope0 [1:5]:
24
+ - read a$5
25
+ - read b$6
26
```
27
28
### CFG
compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-shadowed.expect.md
+3
@@ -20,6 +20,9 @@ bb0:
20
[3] Call mutate y$8_@1.push(read b$6)
21
[4] Call mutate x$7_@0.push(read a$5)
22
[5] Return
23
+scope1 [2:4]:
24
+ - read b$6
25
+ - read a$5
26
```
27
28
### CFG
compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-shadowing-within-block.expect.md
+8
@@ -40,6 +40,14 @@ bb3:
40
bb1:
41
predecessor blocks: bb3 bb0
42
[11] Return freeze x$11_@0
43
+scope0 [1:11]:
44
+ - read a$8
45
+scope1 [3:7]:
46
+ - read c$10
47
+ - read b$9
48
+scope3 [8:9]:
49
+ - read $13_@2
50
+ - freeze y$12_@1
51
```
52
53
### CFG
compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-while.expect.md
+4
@@ -31,6 +31,10 @@ bb3:
31
bb2:
32
predecessor blocks: bb1
33
[8] Return
34
+scope0 [1:8]:
35
+ - read b$7
36
+ - read a$6
37
+ - read c$8
38
```
39
40
### CFG
compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-within-block.expect.md
+4
@@ -38,6 +38,10 @@ bb3:
38
bb1:
39
predecessor blocks: bb3 bb0
40
[9] Return freeze x$9_@0
41
+scope0 [1:9]:
42
+ - read c$8
43
+ - read b$7
44
+ - read a$6
45
```
46
47
### CFG
compiler/forget/src/__tests__/fixtures/hir/property-assignment.expect.md
+10
@@ -24,6 +24,16 @@ bb0:
24
[5] Call mutate x$7_@0.y.push(read props$6.p0)
25
[6] Const mutate $10_@3 = JSX <read Component$0 data={freeze x$7_@0} >{read child$9_@2}</read Component$0>
26
[7] Return read $10_@3
27
+scope1 [2:3]:
28
+ - mutate x$7_@0.y
29
+scope2 [4:5]:
30
+ - read Component$0
31
+ - freeze y$8_@1
32
+ - read props$6.p0
33
+scope3 [6:7]:
34
+ - read Component$0
35
+ - freeze x$7_@0
36
+ - read child$9_@2
37
```
38
39
### CFG
compiler/forget/src/__tests__/fixtures/hir/reactive-scope-grouping.expect.md
+2
@@ -24,6 +24,8 @@ bb0:
24
[4] Call mutate y$5_@1.push(mutate z$6_@1)
25
[5] Reassign mutate x$4_@0.y[1:6] = read y$5_@1
26
[6] Return freeze x$4_@0
27
+scope1 [2:5]:
28
+ - mutate x$4_@0.y
29
```
30
31
### CFG
compiler/forget/src/__tests__/fixtures/hir/reactive-scopes.expect.md
+9
@@ -35,6 +35,15 @@ bb1:
35
[8] Const mutate $13_@3 = "div"
36
[9] Const mutate $15_@4 = JSX <read $13_@3>{freeze x$10_@0}</read $13_@3>
37
[10] Return read $15_@4
38
+scope0 [1:8]:
39
+ - read b$9
40
+ - read b$9
41
+scope2 [3:4]:
42
+ - read a$8.length
43
+ - read $11_@1
44
+scope4 [9:10]:
45
+ - read $13_@3
46
+ - freeze x$10_@0
47
```
48
49
### CFG
compiler/forget/src/__tests__/fixtures/hir/reassignment-conditional.expect.md
+10
@@ -38,6 +38,16 @@ bb1:
38
[8] Call read y$8_@0.push(read props$6.p2)
39
[9] Const mutate $15_@2 = JSX <read Component$0 x={read x$11_@0} y={read y$8_@0} ></read Component$0>
40
[10] Return read $15_@2
41
+scope0 [1:7]:
42
+ - read props$6.p0
43
+ - read props$6.p1
44
+scope1 [7:8]:
45
+ - read Component$0
46
+ - read y$8_@0.push
47
+ - read props$6.p2
48
+scope2 [9:10]:
49
+ - read Component$0
50
+ - read y$8_@0
51
```
52
53
### CFG
compiler/forget/src/__tests__/fixtures/hir/reassignment-separate-scopes.expect.md
+16
@@ -67,6 +67,22 @@ bb3:
67
[18] Const mutate $28_@8 = "\n "
68
[19] Const mutate $31_@9 = JSX <read $25_@5>{read $26_@6}{read y$19_@2}{read $27_@7}{freeze x$30_@4}{read $28_@8}</read $25_@5>
69
[20] Return read $31_@9
70
+scope0 [1:5]:
71
+ - read a$13
72
+ - read a$13
73
+scope2 [6:7]:
74
+ - read $17_@1
75
+ - freeze x$16_@0
76
+scope4 [9:14]:
77
+ - read c$15
78
+ - read b$14
79
+ - read b$14
80
+scope9 [19:20]:
81
+ - read $25_@5
82
+ - read $26_@6
83
+ - read y$19_@2
84
+ - read $27_@7
85
+ - read $28_@8
86
```
87
88
### CFG
compiler/forget/src/__tests__/fixtures/hir/reassignment.expect.md
+10
@@ -29,6 +29,16 @@ bb0:
29
[6] Call mutate y$8_@0.push(read props$6.p1)
30
[7] Const mutate $11_@3 = JSX <read Component$0 x={read x$9_@1} y={freeze y$8_@0} ></read Component$0>
31
[8] Return read $11_@3
32
+scope0 [1:7]:
33
+ - read props$6.p0
34
+scope2 [5:6]:
35
+ - read Component$0
36
+ - freeze x$9_@1
37
+ - read props$6.p1
38
+scope3 [7:8]:
39
+ - read Component$0
40
+ - read x$9_@1
41
+ - freeze y$8_@0
42
```
43
44
### CFG
compiler/forget/src/__tests__/fixtures/hir/reverse-postorder.expect.md
+7
@@ -76,6 +76,13 @@ bb1:
76
x$16_@4[7:17]: phi(bb8: x$11_@4, bb6: x$12_@4, bb4: x$13_@4, bb12: x$14_@4, bb13: x$15_@4)
77
[18] read x$16_@4
78
[19] Return
79
+scope4 [7:17]:
80
+ - read props$6.v2
81
+ - read props$6.v1
82
+ - read props$6.v0
83
+ - read props$6.test
84
+ - read props$6.b
85
+ - read props$6.c
86
```
87
88
### CFG
compiler/forget/src/__tests__/fixtures/hir/simple-alias.expect.md
+2
@@ -21,6 +21,7 @@ function foo() {
21
```
22
bb0:
23
[1] Return
24
+
25
```
26
27
### CFG
@@ -54,6 +55,7 @@ bb0:
55
[6] Reassign mutate c$10_@1[0:8] = read a$8_@1
56
[7] Call mutate mutate$4_@1(mutate a$8_@1, mutate b$9_@1)
57
[8] Return freeze c$10_@1
58
+
59
```
60
61
### CFG
compiler/forget/src/__tests__/fixtures/hir/simple.expect.md
+9
@@ -27,6 +27,15 @@ bb1:
27
[6] Const mutate $13_@3 = Binary read y$9 * read $12_@2
28
[7] Const mutate $14_@4 = Array [read $13_@3]
29
[8] Return freeze $14_@4
30
+scope1 [3:4]:
31
+ - read foo$0
32
+ - read $10_@0
33
+ - read y$9
34
+scope3 [6:7]:
35
+ - read y$9
36
+ - read $12_@2
37
+scope4 [7:8]:
38
+ - read $13_@3
39
```
40
41
### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-arrayexpression.expect.md
+3
@@ -19,6 +19,9 @@ bb0:
19
[2] Const mutate b$7_@1 = 2
20
[3] Const mutate x$8_@2 = Array [read a$6_@0, read b$7_@1]
21
[4] Return freeze x$8_@2
22
+scope2 [3:4]:
23
+ - read a$6_@0
24
+ - read b$7_@1
25
```
26
27
### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-call-jsx-2.expect.md
+7
@@ -23,6 +23,7 @@ function Component(props) {
23
```
24
bb0:
25
[1] Return
26
+
27
```
28
29
### CFG
@@ -64,6 +65,12 @@ bb1:
65
[10] Const mutate $19_@3 = "div"
66
[11] Const mutate $20_@4 = JSX <read $19_@3 a={freeze a$11_@0} b={freeze b$12_@0} ></read $19_@3>
67
[12] Return read $20_@4
68
+scope2 [7:8]:
69
+ - read $14_@1
70
+scope4 [11:12]:
71
+ - read $19_@3
72
+ - freeze a$11_@0
73
+ - freeze b$12_@0
74
```
75
76
### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-call-jsx.expect.md
+7
@@ -20,6 +20,7 @@ function Component(props) {
20
```
21
bb0:
22
[1] Return
23
+
24
```
25
26
### CFG
@@ -54,6 +55,12 @@ bb0:
55
[7] Const mutate $14_@3 = "div"
56
[8] Const mutate $15_@4 = JSX <read $14_@3 a={read a$10_@0} b={freeze b$11_@0} ></read $14_@3>
57
[9] Return read $15_@4
58
+scope2 [5:6]:
59
+ - read $12_@1
60
+scope4 [8:9]:
61
+ - read $14_@3
62
+ - read a$10_@0
63
+ - freeze b$11_@0
64
```
65
66
### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-complex-multiple-if.expect.md
+6
@@ -45,6 +45,12 @@ bb3:
45
x$16_@0[1:14]: phi(bb4: x$15_@0, bb1: x$17_@0)
46
[13] Reassign mutate y$18_@0[1:14] = read x$16_@0
47
[14] Return
48
+scope3 [4:5]:
49
+ - read y$8_@1
50
+ - read $9_@2
51
+scope5 [9:10]:
52
+ - read y$8_@1
53
+ - read $12_@4
54
```
55
56
### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-complex-single-if.expect.md
+3
@@ -32,6 +32,9 @@ bb1:
32
x$10_@0[1:9]: phi(bb2: x$9_@0, bb0: x$5_@0)
33
[8] Reassign mutate y$11_@0[1:9] = read x$10_@0
34
[9] Return
35
+scope3 [4:5]:
36
+ - read y$6_@1
37
+ - read $7_@2
38
```
39
40
### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-for-of.expect.md
+1
@@ -38,6 +38,7 @@ bb4:
38
bb2:
39
predecessor blocks: bb1
40
[9] Return freeze items$5_@0
41
+
42
```
43
44
### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-for.expect.md
+1
@@ -32,6 +32,7 @@ bb4:
32
bb2:
33
predecessor blocks: bb1
34
[9] Return read x$7_@0
35
+
36
```
37
38
### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-if-else.expect.md
+1
@@ -33,6 +33,7 @@ bb3:
33
bb1:
34
predecessor blocks: bb2 bb3
35
[8] Return
36
+
37
```
38
39
### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-nested-loops-no-reassign.expect.md
+2
@@ -52,6 +52,8 @@ bb5:
52
bb2:
53
predecessor blocks: bb1
54
[13] Return read x$9_@0
55
+scope1 [8:9]:
56
+ - read x$9_@0
57
```
58
59
### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-newexpression.expect.md
+2
@@ -18,6 +18,7 @@ function Component(props) {
18
```
19
bb0:
20
[1] Return
21
+
22
```
23
24
### CFG
@@ -47,6 +48,7 @@ bb0:
48
[2] Const mutate b$8_@0[0:4] = Object { }
49
[3] Let mutate c$9_@0[0:4] = New mutate Foo$5_@0(mutate a$7_@0, mutate b$8_@0)
50
[4] Return freeze c$9_@0
51
+
52
```
53
54
### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-objectexpression-phi.expect.md
+2
@@ -41,6 +41,8 @@ bb1:
41
y$13_@0[1:10]: phi(bb2: y$7_@0, bb3: y$11_@0)
42
[10] Let mutate t$14_@4 = Object { x: read x$12_@0, y: read y$13_@0 }
43
[11] Return freeze t$14_@4
44
+scope3 [4:5]:
45
+ - read $8_@2
46
```
47
48
### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-objectexpression.expect.md
+3
@@ -19,6 +19,9 @@ bb0:
19
[2] Const mutate b$7_@1 = 2
20
[3] Const mutate x$8_@2 = Object { a: read a$6_@0, b: read b$7_@1 }
21
[4] Return freeze x$8_@2
22
+scope2 [3:4]:
23
+ - read a$6_@0
24
+ - read b$7_@1
25
```
26
27
### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-property-call.expect.md
+2
@@ -20,6 +20,8 @@ bb0:
20
[3] Const mutate $6_@1[2:5] = Array []
21
[4] Call mutate y$5_@1.x.push(mutate $6_@1)
22
[5] Return freeze y$5_@1
23
+scope1 [2:5]:
24
+ - read x$4_@0
25
```
26
27
### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-property.expect.md
+2
@@ -19,6 +19,8 @@ bb0:
19
[2] Const mutate y$4_@1[2:4] = Object { }
20
[3] Reassign mutate y$4_@1.x[2:4] = read x$3_@0
21
[4] Return freeze y$4_@1
22
+scope1 [2:4]:
23
+ - read x$3_@0
24
```
25
26
### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-return.expect.md
+2
@@ -29,6 +29,8 @@ bb1:
29
predecessor blocks: bb2 bb0
30
x$8_@0[1:7]: phi(bb2: x$7_@0, bb0: x$4_@0)
31
[7] Return read x$8_@0
32
+scope2 [3:4]:
33
+ - read $5_@1
34
```
35
36
### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-shadowing.expect.md
+2
@@ -22,6 +22,7 @@ function Foo(cond) {
22
```
23
bb0:
24
[1] Return
25
+
26
```
27
28
### CFG
@@ -63,6 +64,7 @@ bb1:
64
str$10_@0[1:8]: phi(bb2: str$6_@0, bb3: str$8_@0)
65
[8] Call mutate log$4_@2(read str$10_@0)
66
[9] Return
67
+
68
```
69
70
### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-simple-phi.expect.md
+5
@@ -37,6 +37,11 @@ bb1:
37
y$10_@3[5:10]: phi(bb2: y$8_@3, bb3: y$9_@3)
38
[9] Let mutate x$11_@3[5:10] = read y$10_@3
39
[10] Return
40
+scope2 [3:4]:
41
+ - read y$5_@0
42
+ - read $6_@1
43
+scope3 [5:10]:
44
+ - read $7_@2
45
```
46
47
### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-simple.expect.md
+1
@@ -16,6 +16,7 @@ bb0:
16
[1] Let mutate x$3_@0 = 1
17
[2] Let mutate y$4_@1 = 2
18
[3] Return
19
+
20
```
21
22
### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-single-if.expect.md
+3
@@ -27,6 +27,9 @@ bb2:
27
bb1:
28
predecessor blocks: bb2 bb0
29
[6] Return
30
+scope2 [4:5]:
31
+ - read x$4_@0
32
+ - read y$5_@1
33
```
34
35
### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-switch.expect.md
+13
@@ -57,6 +57,19 @@ bb1:
57
x$21_@6[8:17]: phi(bb5: x$16_@6, bb3: x$18_@6, bb2: x$20_@6)
58
[16] Let mutate y$22_@6[8:17] = read x$21_@6
59
[17] Return
60
+scope2 [3:4]:
61
+ - read x$10_@0
62
+ - read $11_@1
63
+scope4 [5:6]:
64
+ - read x$10_@0
65
+ - read $13_@3
66
+scope6 [8:17]:
67
+ - read x$10_@0
68
+ - read x$10_@0
69
+scope7 [10:11]:
70
+ - read x$10_@0
71
+scope5 [7:8]:
72
+ - read x$10_@0
73
```
74
75
### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-throw.expect.md
+2
@@ -28,6 +28,8 @@ bb1:
28
predecessor blocks: bb2 bb0
29
x$8_@0[1:7]: phi(bb2: x$7_@0, bb0: x$4_@0)
30
[7] Throw read x$8_@0
31
+scope2 [3:4]:
32
+ - read $5_@1
33
```
34
35
### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-while-no-reassign.expect.md
+5
@@ -32,6 +32,11 @@ bb3:
32
bb2:
33
predecessor blocks: bb1
34
[9] Return read x$5_@0
35
+scope2 [4:9]:
36
+ - read x$5_@0
37
+ - read $6_@1
38
+scope3 [6:7]:
39
+ - read x$5_@0
40
```
41
42
### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-while.expect.md
+2
@@ -33,6 +33,8 @@ bb3:
33
bb2:
34
predecessor blocks: bb1
35
[9] Return read x$7_@0
36
+scope2 [4:9]:
37
+ - read $6_@1
38
```
39
40
### CFG
compiler/forget/src/__tests__/fixtures/hir/switch-non-final-default.expect.md
+10
@@ -58,6 +58,16 @@ bb1:
58
[13] Call read y$20_@0.push(read props$9.p4)
59
[14] Const mutate $22_@5 = JSX <read Component$0 data={freeze y$20_@0} >{read child$19_@4}</read Component$0>
60
[15] Return read $22_@5
61
+scope0 [1:12]:
62
+ - read props$9.p2
63
+ - read props$9.p0
64
+scope4 [12:13]:
65
+ - read Component$0
66
+ - freeze x$10_@0
67
+ - read props$9.p4
68
+scope5 [14:15]:
69
+ - read Component$0
70
+ - read child$19_@4
71
```
72
73
### CFG
compiler/forget/src/__tests__/fixtures/hir/switch-with-fallthrough.expect.md
+1
@@ -78,6 +78,7 @@ bb2:
78
bb1:
79
predecessor blocks: bb9 bb0 bb5 bb2
80
[21] Return
81
+
82
```
83
84
### CFG
compiler/forget/src/__tests__/fixtures/hir/switch.expect.md
+11
@@ -53,6 +53,17 @@ bb1:
53
[13] Call read y$20_@0.push(read props$8.p4)
54
[14] Const mutate $23_@5 = JSX <read Component$0 data={read y$20_@0} >{read child$19_@4}</read Component$0>
55
[15] Return read $23_@5
56
+scope0 [1:12]:
57
+ - read props$8.p2
58
+ - read props$8.p3
59
+ - read props$8.p0
60
+scope4 [12:13]:
61
+ - read Component$0
62
+ - freeze x$9_@0
63
+ - read props$8.p4
64
+scope5 [14:15]:
65
+ - read Component$0
66
+ - read child$19_@4
67
```
68
69
### CFG
compiler/forget/src/__tests__/fixtures/hir/while-break.expect.md
+1
@@ -22,6 +22,7 @@ bb1:
22
bb2:
23
predecessor blocks: bb1
24
[3] Return read b$4
25
+
26
```
27
28
### CFG
compiler/forget/src/__tests__/fixtures/hir/while-conditional-continue.expect.md
+1
@@ -37,6 +37,7 @@ bb2:
37
predecessor blocks: bb1
38
[7] Call read d$8()
39
[8] Return
40
+
41
```
42
43
### CFG
compiler/forget/src/__tests__/hir-test.ts
+4
-2
@@ -15,7 +15,7 @@ import invariant from "invariant";
15
import path from "path";
16
import prettier from "prettier";
17
import run from "../HIR/Pipeline";
18
-import printHIR from "../HIR/PrintHIR";
18
+import { printFunction } from "../HIR/PrintHIR";
19
import visualizeHIRMermaid from "../HIR/VisualizeHIRMermaid";
20
import generateTestsFromFixtures from "./test-utils/generateTestsFromFixtures";
21
@@ -62,12 +62,13 @@ describe("React Forget (HIR version)", () => {
62
inferMutableRanges: true,
63
inferReactiveScopeVariables: true,
64
inferReactiveScopes: true,
65
+ inferReactiveScopeDependencies: true,
66
leaveSSA: false,
67
codegen: false,
68
});
69
70
// Print the HIR before leaving SSA.
70
- const textHIR = printHIR(ir.body);
71
+ const textHIR = printFunction(ir);
72
const visualization = visualizeHIRMermaid(ir);
73
74
const { ast } = run(nodePath, {
@@ -76,6 +77,7 @@ describe("React Forget (HIR version)", () => {
77
inferMutableRanges: true,
78
inferReactiveScopeVariables: true,
79
inferReactiveScopes: true,
80
+ inferReactiveScopeDependencies: true,
81
leaveSSA: true,
82
codegen: true,
83
});
compiler/forget/src/index.ts
+8
-6
@@ -21,18 +21,19 @@ export * from "./Diagnostic";
21
export * from "./Logger";
22
export { NoUseBeforeDefineRule } from "./Validation";
23
24
+import { parse } from "@babel/parser";
25
+import traverse, { NodePath } from "@babel/traverse";
26
+import * as t from "@babel/types";
27
import { lower } from "./HIR/BuildHIR";
28
+import codegen from "./HIR/Codegen";
29
import { eliminateRedundantPhi } from "./HIR/EliminateRedundantPhi";
30
import enterSSA from "./HIR/EnterSSA";
31
+import { Environment } from "./HIR/HIRBuilder";
32
import { inferMutableRanges } from "./HIR/InferMutableLifetimes";
33
+import { inferReactiveScopeDependencies } from "./HIR/InferReactiveScopeDependencies";
34
import inferReferenceEffects from "./HIR/InferReferenceEffects";
29
-import printHIR from "./HIR/PrintHIR";
30
-import { Environment } from "./HIR/HIRBuilder";
35
import leaveSSA from "./HIR/LeaveSSA";
32
-import traverse, { NodePath } from "@babel/traverse";
33
-import * as t from "@babel/types";
34
-import { parse } from "@babel/parser";
35
-import codegen from "./HIR/Codegen";
36
+import printHIR from "./HIR/PrintHIR";
37
38
function parseFunctions(
39
source: string
@@ -62,6 +63,7 @@ export const HIR = {
63
enterSSA,
64
inferMutableRanges,
65
inferReferenceEffects,
66
+ inferReactiveScopeDependencies,
67
printHIR,
68
Environment,
69
leaveSSA,