@samitouri / QOS-React-2 / commits / fcc2182641

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;