The library for web and native user interfaces.
MemoizeFbtOperandsInSameScope operates on HIR
Moves this pass to operate against HIRFunction instead of ReactiveFunction, no logic changes.
Joe Savona committed
Mar 6, 2024 at 14:02 UTC
ebbada309db9094cae1c0a2a4e5faffae0422318
2 files changed
+80
-86
compiler/packages/babel-plugin-react-forget/src/Entrypoint/Pipeline.ts
+7
-7
@@ -211,6 +211,13 @@ function* runWithEnvironment(
211
value: hir,
212
});
213
214
+ memoizeFbtOperandsInSameScope(hir);
215
+ yield log({
216
+ kind: "hir",
217
+ name: "MemoizeFbtOperandsInSameScope",
218
+ value: hir,
219
+ });
220
+
221
const reactiveFunction = buildReactiveFunction(hir);
222
yield log({
223
kind: "reactive",
@@ -225,13 +232,6 @@ function* runWithEnvironment(
232
value: reactiveFunction,
233
});
234
228
- memoizeFbtOperandsInSameScope(reactiveFunction);
229
- yield log({
230
- kind: "reactive",
231
- name: "MemoizeFbtOperandsInSameScope",
232
- value: reactiveFunction,
233
- });
234
-
235
alignReactiveScopesToBlockScopes(reactiveFunction);
236
yield log({
237
kind: "reactive",
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/MemoizeFbtOperandsInSameScope.ts
+73
-79
@@ -6,18 +6,13 @@
6
*/
7
8
import {
9
+ HIRFunction,
10
IdentifierId,
11
makeInstructionId,
12
Place,
12
- ReactiveFunction,
13
- ReactiveInstruction,
13
ReactiveValue,
14
} from "../HIR";
16
-import {
17
- eachReactiveValueOperand,
18
- ReactiveFunctionVisitor,
19
- visitReactiveFunction,
20
-} from "./visitors";
15
+import { eachReactiveValueOperand } from "./visitors";
16
17
/*
18
* This pass supports the `fbt` translation system (https://facebook.github.io/fbt/).
@@ -33,12 +28,12 @@ import {
28
* Note that this still allows the props/arguments of `<fbt:param>`/`fbt.param()`
29
* to be independently memoized
30
*/
36
-export function memoizeFbtOperandsInSameScope(fn: ReactiveFunction): void {
37
- const transform = new Transform();
31
+export function memoizeFbtOperandsInSameScope(fn: HIRFunction): void {
32
+ const fbtValues: Set<IdentifierId> = new Set();
33
while (true) {
39
- let size = transform.fbtValues.size;
40
- visitReactiveFunction(fn, transform, undefined);
41
- if (size === transform.fbtValues.size) {
34
+ let size = fbtValues.size;
35
+ visit(fn, fbtValues);
36
+ if (size === fbtValues.size) {
37
break;
38
}
39
}
@@ -47,80 +42,79 @@ export function memoizeFbtOperandsInSameScope(fn: ReactiveFunction): void {
42
export const FBT_TAGS: Set<string> = new Set(["fbt", "fbt:param"]);
43
export const SINGLE_CHILD_FBT_TAGS: Set<string> = new Set(["fbt:param"]);
44
50
-class Transform extends ReactiveFunctionVisitor<void> {
51
- /*
52
- * Values that represent *potential* references of `fbt` as a JSX tag name
53
- * or as a callee.
54
- */
55
- fbtValues: Set<IdentifierId> = new Set();
56
-
57
- override visitInstruction(
58
- instruction: ReactiveInstruction,
59
- _state: void
60
- ): void {
61
- const { lvalue, value } = instruction;
62
- if (lvalue === null) {
63
- return;
64
- }
65
- if (
66
- value.kind === "Primitive" &&
67
- typeof value.value === "string" &&
68
- FBT_TAGS.has(value.value)
69
- ) {
70
- /*
71
- * We don't distinguish between tag names and strings, so record
72
- * all `fbt` string literals in case they are used as a jsx tag.
73
- */
74
- this.fbtValues.add(lvalue.identifier.id);
75
- } else if (value.kind === "LoadGlobal" && FBT_TAGS.has(value.name)) {
76
- // Record references to `fbt` as a global
77
- this.fbtValues.add(lvalue.identifier.id);
78
- } else if (isFbtCallExpression(this.fbtValues, value)) {
79
- const fbtScope = lvalue.identifier.scope;
80
- if (fbtScope === null) {
81
- return;
82
- }
83
-
84
- /*
85
- * if the JSX element's tag was `fbt`, mark all its operands
86
- * to ensure that they end up in the same scope as the jsx element
87
- * itself.
88
- */
89
- for (const operand of eachReactiveValueOperand(value)) {
90
- operand.identifier.scope = fbtScope;
91
-
92
- // Expand the jsx element's range to account for its operands
93
- fbtScope.range.start = makeInstructionId(
94
- Math.min(fbtScope.range.start, operand.identifier.mutableRange.start)
95
- );
96
- }
97
- } else if (
98
- isFbtJsxExpression(this.fbtValues, value) ||
99
- isFbtJsxChild(this.fbtValues, lvalue, value)
100
- ) {
101
- const fbtScope = lvalue.identifier.scope;
102
- if (fbtScope === null) {
45
+function visit(fn: HIRFunction, fbtValues: Set<IdentifierId>): void {
46
+ for (const [, block] of fn.body.blocks) {
47
+ for (const instruction of block.instructions) {
48
+ const { lvalue, value } = instruction;
49
+ if (lvalue === null) {
50
return;
51
}
52
+ if (
53
+ value.kind === "Primitive" &&
54
+ typeof value.value === "string" &&
55
+ FBT_TAGS.has(value.value)
56
+ ) {
57
+ /*
58
+ * We don't distinguish between tag names and strings, so record
59
+ * all `fbt` string literals in case they are used as a jsx tag.
60
+ */
61
+ fbtValues.add(lvalue.identifier.id);
62
+ } else if (value.kind === "LoadGlobal" && FBT_TAGS.has(value.name)) {
63
+ // Record references to `fbt` as a global
64
+ fbtValues.add(lvalue.identifier.id);
65
+ } else if (isFbtCallExpression(fbtValues, value)) {
66
+ const fbtScope = lvalue.identifier.scope;
67
+ if (fbtScope === null) {
68
+ return;
69
+ }
70
106
- /*
107
- * if the JSX element's tag was `fbt`, mark all its operands
108
- * to ensure that they end up in the same scope as the jsx element
109
- * itself.
110
- */
111
- for (const operand of eachReactiveValueOperand(value)) {
112
- operand.identifier.scope = fbtScope;
71
+ /*
72
+ * if the JSX element's tag was `fbt`, mark all its operands
73
+ * to ensure that they end up in the same scope as the jsx element
74
+ * itself.
75
+ */
76
+ for (const operand of eachReactiveValueOperand(value)) {
77
+ operand.identifier.scope = fbtScope;
78
114
- // Expand the jsx element's range to account for its operands
115
- fbtScope.range.start = makeInstructionId(
116
- Math.min(fbtScope.range.start, operand.identifier.mutableRange.start)
117
- );
79
+ // Expand the jsx element's range to account for its operands
80
+ fbtScope.range.start = makeInstructionId(
81
+ Math.min(
82
+ fbtScope.range.start,
83
+ operand.identifier.mutableRange.start
84
+ )
85
+ );
86
+ }
87
+ } else if (
88
+ isFbtJsxExpression(fbtValues, value) ||
89
+ isFbtJsxChild(fbtValues, lvalue, value)
90
+ ) {
91
+ const fbtScope = lvalue.identifier.scope;
92
+ if (fbtScope === null) {
93
+ return;
94
+ }
95
96
/*
120
- * NOTE: we add the operands as fbt values so that they are also
121
- * grouped with this expression
97
+ * if the JSX element's tag was `fbt`, mark all its operands
98
+ * to ensure that they end up in the same scope as the jsx element
99
+ * itself.
100
*/
123
- this.fbtValues.add(operand.identifier.id);
101
+ for (const operand of eachReactiveValueOperand(value)) {
102
+ operand.identifier.scope = fbtScope;
103
+
104
+ // Expand the jsx element's range to account for its operands
105
+ fbtScope.range.start = makeInstructionId(
106
+ Math.min(
107
+ fbtScope.range.start,
108
+ operand.identifier.mutableRange.start
109
+ )
110
+ );
111
+
112
+ /*
113
+ * NOTE: we add the operands as fbt values so that they are also
114
+ * grouped with this expression
115
+ */
116
+ fbtValues.add(operand.identifier.id);
117
+ }
118
}
119
}
120
}