Handle scopes with only early return and no decls/deps/reassigns
Fixes the case from the previous PR by using a different sentinel for uninitialized cache values and early returns. I confirmed with console.log that the reactive scope for `x` only evaluates on the first execution, after which we figure out that we don't need to execute it again.
Joe Savona committed
Dec 20, 2023 at 13:52 UTC
fcc2182641b628cd6fcf46d2ea59c2cc05f7ddb8
11 files changed
+39
-41
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+5
-2
@@ -42,6 +42,9 @@ import { buildReactiveFunction } from "./BuildReactiveFunction";
42
import { SINGLE_CHILD_FBT_TAGS } from "./MemoizeFbtOperandsInSameScope";
43
import { ReactiveFunctionVisitor, visitReactiveFunction } from "./visitors";
44
45
+export const MEMO_CACHE_SENTINEL = "react.memo_cache_sentinel";
46
+export const EARLY_RETURN_SENTINEL = "react.early_return_sentinel";
47
+
48
export type CodegenFunction = {
49
type: "CodegenFunction";
50
id: t.Identifier | null;
@@ -443,7 +446,7 @@ function codegenReactiveScope(
446
),
447
t.callExpression(
448
t.memberExpression(t.identifier("Symbol"), t.identifier("for")),
446
- [t.stringLiteral("react.memo_cache_sentinel")]
449
+ [t.stringLiteral(MEMO_CACHE_SENTINEL)]
450
)
451
);
452
}
@@ -516,7 +519,7 @@ function codegenReactiveScope(
519
t.identifier(scope.earlyReturnValue.value.name!),
520
t.callExpression(
521
t.memberExpression(t.identifier("Symbol"), t.identifier("for")),
519
- [t.stringLiteral("react.memo_cache_sentinel")]
522
+ [t.stringLiteral(EARLY_RETURN_SENTINEL)]
523
)
524
),
525
t.blockStatement([
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/PropagateEarlyReturns.ts
+2
-1
@@ -19,6 +19,7 @@ import {
19
makeType,
20
} from "../HIR";
21
import { createTemporaryPlace } from "../HIR/HIRBuilder";
22
+import { EARLY_RETURN_SENTINEL } from "./CodegenReactiveFunction";
23
import { ReactiveFunctionTransform, Transformed } from "./visitors";
24
25
/**
@@ -191,7 +192,7 @@ class Transform extends ReactiveFunctionTransform<State> {
192
lvalue: { ...argTemp },
193
value: {
194
kind: "Primitive",
194
- value: "react.memo_cache_sentinel",
195
+ value: EARLY_RETURN_SENTINEL,
196
loc,
197
},
198
},
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/conditional-early-return.expect.md
+6
-6
@@ -78,7 +78,7 @@ function ComponentA(props) {
78
let a_DEBUG;
79
let t37;
80
if ($[0] !== props.a || $[1] !== props.b || $[2] !== props.d) {
81
- t37 = Symbol.for("react.memo_cache_sentinel");
81
+ t37 = Symbol.for("react.early_return_sentinel");
82
bb7: {
83
a_DEBUG = [];
84
a_DEBUG.push(props.a);
@@ -98,7 +98,7 @@ function ComponentA(props) {
98
a_DEBUG = $[3];
99
t37 = $[4];
100
}
101
- if (t37 !== Symbol.for("react.memo_cache_sentinel")) {
101
+ if (t37 !== Symbol.for("react.early_return_sentinel")) {
102
return t37;
103
}
104
return a_DEBUG;
@@ -134,7 +134,7 @@ function ComponentC(props) {
134
let a;
135
let t47;
136
if ($[0] !== props) {
137
- t47 = Symbol.for("react.memo_cache_sentinel");
137
+ t47 = Symbol.for("react.early_return_sentinel");
138
bb7: {
139
a = [];
140
a.push(props.a);
@@ -153,7 +153,7 @@ function ComponentC(props) {
153
a = $[1];
154
t47 = $[2];
155
}
156
- if (t47 !== Symbol.for("react.memo_cache_sentinel")) {
156
+ if (t47 !== Symbol.for("react.early_return_sentinel")) {
157
return t47;
158
}
159
return a;
@@ -167,7 +167,7 @@ function ComponentD(props) {
167
let a;
168
let t47;
169
if ($[0] !== props) {
170
- t47 = Symbol.for("react.memo_cache_sentinel");
170
+ t47 = Symbol.for("react.early_return_sentinel");
171
bb7: {
172
a = [];
173
a.push(props.a);
@@ -186,7 +186,7 @@ function ComponentD(props) {
186
a = $[1];
187
t47 = $[2];
188
}
189
- if (t47 !== Symbol.for("react.memo_cache_sentinel")) {
189
+ if (t47 !== Symbol.for("react.early_return_sentinel")) {
190
return t47;
191
}
192
return a;
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/early-return-nested-early-return-within-reactive-scope.expect.md
+2
-2
@@ -35,7 +35,7 @@ function Component(props) {
35
const $ = useMemoCache(5);
36
let t53;
37
if ($[0] !== props) {
38
- t53 = Symbol.for("react.memo_cache_sentinel");
38
+ t53 = Symbol.for("react.early_return_sentinel");
39
bb11: {
40
const x = [];
41
if (props.cond) {
@@ -74,7 +74,7 @@ function Component(props) {
74
} else {
75
t53 = $[1];
76
}
77
- if (t53 !== Symbol.for("react.memo_cache_sentinel")) {
77
+ if (t53 !== Symbol.for("react.early_return_sentinel")) {
78
return t53;
79
}
80
}
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/early-return-no-declarations-reassignments-dependencies.expect.md
+12
-16
@@ -15,17 +15,15 @@ import { makeArray } from "shared-runtime";
15
*
16
* We have to use a distinct sentinel for the early return value.
17
*
18
- * Here the fixture will always take the "else" branch and never early return, and we should see that
19
- * "recreate x" is only logged once, the first time we execute.
18
+ * Here the fixture will always take the "else" branch and never early return. Logging (not included)
19
+ * confirms that the scope for `x` only executes once, on the first render of the component.
20
*/
21
let ENABLE_FEATURE = false;
22
23
function Component(props) {
24
let x = [];
25
- console.log("recreate x");
25
if (ENABLE_FEATURE) {
26
x.push(42);
28
- console.log("early return");
27
return x;
28
} else {
29
console.log("fallthrough");
@@ -66,32 +64,30 @@ import { makeArray } from "shared-runtime";
64
*
65
* We have to use a distinct sentinel for the early return value.
66
*
69
- * Here the fixture will always take the "else" branch and never early return, and we should see that
70
- * "recreate x" is only logged once, the first time we execute.
67
+ * Here the fixture will always take the "else" branch and never early return. Logging (not included)
68
+ * confirms that the scope for `x` only executes once, on the first render of the component.
69
*/
70
let ENABLE_FEATURE = false;
71
72
function Component(props) {
73
const $ = useMemoCache(3);
76
- let t53;
74
+ let t37;
75
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
78
- t53 = Symbol.for("react.memo_cache_sentinel");
76
+ t37 = Symbol.for("react.early_return_sentinel");
77
bb8: {
78
const x = [];
81
- console.log("recreate x");
79
if (ENABLE_FEATURE) {
80
x.push(42);
84
- console.log("early return");
85
- t53 = x;
81
+ t37 = x;
82
break bb8;
83
}
84
}
89
- $[0] = t53;
85
+ $[0] = t37;
86
} else {
91
- t53 = $[0];
87
+ t37 = $[0];
88
}
93
- if (t53 !== Symbol.for("react.memo_cache_sentinel")) {
94
- return t53;
89
+ if (t37 !== Symbol.for("react.early_return_sentinel")) {
90
+ return t37;
91
}
92
93
console.log("fallthrough");
@@ -132,4 +128,4 @@ export const FIXTURE_ENTRYPOINT = {
128
[3.14]
129
[42]
130
[3.14]
135
-logs: ['recreate x','fallthrough','recreate x','fallthrough','recreate x','fallthrough','recreate x','fallthrough','recreate x','fallthrough','recreate x','fallthrough','recreate x','fallthrough','recreate x','fallthrough']
\ No newline at end of file
131
+logs: ['fallthrough','fallthrough','fallthrough','fallthrough','fallthrough','fallthrough','fallthrough','fallthrough']
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/early-return-no-declarations-reassignments-dependencies.js
+2
-4
@@ -11,17 +11,15 @@ import { makeArray } from "shared-runtime";
11
*
12
* We have to use a distinct sentinel for the early return value.
13
*
14
- * Here the fixture will always take the "else" branch and never early return, and we should see that
15
- * "recreate x" is only logged once, the first time we execute.
14
+ * Here the fixture will always take the "else" branch and never early return. Logging (not included)
15
+ * confirms that the scope for `x` only executes once, on the first render of the component.
16
*/
17
let ENABLE_FEATURE = false;
18
19
function Component(props) {
20
let x = [];
21
- console.log("recreate x");
21
if (ENABLE_FEATURE) {
22
x.push(42);
24
- console.log("early return");
23
return x;
24
} else {
25
console.log("fallthrough");
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/early-return-within-reactive-scope.expect.md
+2
-2
@@ -49,7 +49,7 @@ function Component(props) {
49
const $ = useMemoCache(4);
50
let t33;
51
if ($[0] !== props) {
52
- t33 = Symbol.for("react.memo_cache_sentinel");
52
+ t33 = Symbol.for("react.early_return_sentinel");
53
bb8: {
54
const x = [];
55
if (props.cond) {
@@ -74,7 +74,7 @@ function Component(props) {
74
} else {
75
t33 = $[1];
76
}
77
- if (t33 !== Symbol.for("react.memo_cache_sentinel")) {
77
+ if (t33 !== Symbol.for("react.early_return_sentinel")) {
78
return t33;
79
}
80
}
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/partial-early-return-within-reactive-scope.expect.md
+2
-2
@@ -35,7 +35,7 @@ function Component(props) {
35
let y;
36
let t46;
37
if ($[0] !== props) {
38
- t46 = Symbol.for("react.memo_cache_sentinel");
38
+ t46 = Symbol.for("react.early_return_sentinel");
39
bb11: {
40
const x = [];
41
if (props.cond) {
@@ -64,7 +64,7 @@ function Component(props) {
64
y = $[1];
65
t46 = $[2];
66
}
67
- if (t46 !== Symbol.for("react.memo_cache_sentinel")) {
67
+ if (t46 !== Symbol.for("react.early_return_sentinel")) {
68
return t46;
69
}
70
return y;
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-try-value-modified-in-catch.expect.md
+2
-2
@@ -34,7 +34,7 @@ function Component(props) {
34
const $ = useMemoCache(3);
35
let t49;
36
if ($[0] !== props.y || $[1] !== props.e) {
37
- t49 = Symbol.for("react.memo_cache_sentinel");
37
+ t49 = Symbol.for("react.early_return_sentinel");
38
bb18: {
39
try {
40
const y = [];
@@ -56,7 +56,7 @@ function Component(props) {
56
} else {
57
t49 = $[2];
58
}
59
- if (t49 !== Symbol.for("react.memo_cache_sentinel")) {
59
+ if (t49 !== Symbol.for("react.early_return_sentinel")) {
60
return t49;
61
}
62
}
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-with-catch-param.expect.md
+2
-2
@@ -35,7 +35,7 @@ function Component(props) {
35
const $ = useMemoCache(1);
36
let t36;
37
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
38
- t36 = Symbol.for("react.memo_cache_sentinel");
38
+ t36 = Symbol.for("react.early_return_sentinel");
39
bb11: {
40
const x = [];
41
try {
@@ -54,7 +54,7 @@ function Component(props) {
54
} else {
55
t36 = $[0];
56
}
57
- if (t36 !== Symbol.for("react.memo_cache_sentinel")) {
57
+ if (t36 !== Symbol.for("react.early_return_sentinel")) {
58
return t36;
59
}
60
}
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-with-return.expect.md
+2
-2
@@ -37,7 +37,7 @@ function Component(props) {
37
let x;
38
let t43;
39
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
40
- t43 = Symbol.for("react.memo_cache_sentinel");
40
+ t43 = Symbol.for("react.early_return_sentinel");
41
bb25: {
42
x = [];
43
try {
@@ -59,7 +59,7 @@ function Component(props) {
59
x = $[0];
60
t43 = $[1];
61
}
62
- if (t43 !== Symbol.for("react.memo_cache_sentinel")) {
62
+ if (t43 !== Symbol.for("react.early_return_sentinel")) {
63
return t43;
64
}
65
return x;