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.
This commit is contained in:
Joe Savona
2023-12-20 13:52:42 -08:00
parent a753a326ad
commit fcc2182641
11 changed files with 39 additions and 41 deletions
@@ -42,6 +42,9 @@ import { buildReactiveFunction } from "./BuildReactiveFunction";
import { SINGLE_CHILD_FBT_TAGS } from "./MemoizeFbtOperandsInSameScope";
import { ReactiveFunctionVisitor, visitReactiveFunction } from "./visitors";
export const MEMO_CACHE_SENTINEL = "react.memo_cache_sentinel";
export const EARLY_RETURN_SENTINEL = "react.early_return_sentinel";
export type CodegenFunction = {
type: "CodegenFunction";
id: t.Identifier | null;
@@ -443,7 +446,7 @@ function codegenReactiveScope(
),
t.callExpression(
t.memberExpression(t.identifier("Symbol"), t.identifier("for")),
[t.stringLiteral("react.memo_cache_sentinel")]
[t.stringLiteral(MEMO_CACHE_SENTINEL)]
)
);
}
@@ -516,7 +519,7 @@ function codegenReactiveScope(
t.identifier(scope.earlyReturnValue.value.name!),
t.callExpression(
t.memberExpression(t.identifier("Symbol"), t.identifier("for")),
[t.stringLiteral("react.memo_cache_sentinel")]
[t.stringLiteral(EARLY_RETURN_SENTINEL)]
)
),
t.blockStatement([
@@ -19,6 +19,7 @@ import {
makeType,
} from "../HIR";
import { createTemporaryPlace } from "../HIR/HIRBuilder";
import { EARLY_RETURN_SENTINEL } from "./CodegenReactiveFunction";
import { ReactiveFunctionTransform, Transformed } from "./visitors";
/**
@@ -191,7 +192,7 @@ class Transform extends ReactiveFunctionTransform<State> {
lvalue: { ...argTemp },
value: {
kind: "Primitive",
value: "react.memo_cache_sentinel",
value: EARLY_RETURN_SENTINEL,
loc,
},
},
@@ -78,7 +78,7 @@ function ComponentA(props) {
let a_DEBUG;
let t37;
if ($[0] !== props.a || $[1] !== props.b || $[2] !== props.d) {
t37 = Symbol.for("react.memo_cache_sentinel");
t37 = Symbol.for("react.early_return_sentinel");
bb7: {
a_DEBUG = [];
a_DEBUG.push(props.a);
@@ -98,7 +98,7 @@ function ComponentA(props) {
a_DEBUG = $[3];
t37 = $[4];
}
if (t37 !== Symbol.for("react.memo_cache_sentinel")) {
if (t37 !== Symbol.for("react.early_return_sentinel")) {
return t37;
}
return a_DEBUG;
@@ -134,7 +134,7 @@ function ComponentC(props) {
let a;
let t47;
if ($[0] !== props) {
t47 = Symbol.for("react.memo_cache_sentinel");
t47 = Symbol.for("react.early_return_sentinel");
bb7: {
a = [];
a.push(props.a);
@@ -153,7 +153,7 @@ function ComponentC(props) {
a = $[1];
t47 = $[2];
}
if (t47 !== Symbol.for("react.memo_cache_sentinel")) {
if (t47 !== Symbol.for("react.early_return_sentinel")) {
return t47;
}
return a;
@@ -167,7 +167,7 @@ function ComponentD(props) {
let a;
let t47;
if ($[0] !== props) {
t47 = Symbol.for("react.memo_cache_sentinel");
t47 = Symbol.for("react.early_return_sentinel");
bb7: {
a = [];
a.push(props.a);
@@ -186,7 +186,7 @@ function ComponentD(props) {
a = $[1];
t47 = $[2];
}
if (t47 !== Symbol.for("react.memo_cache_sentinel")) {
if (t47 !== Symbol.for("react.early_return_sentinel")) {
return t47;
}
return a;
@@ -35,7 +35,7 @@ function Component(props) {
const $ = useMemoCache(5);
let t53;
if ($[0] !== props) {
t53 = Symbol.for("react.memo_cache_sentinel");
t53 = Symbol.for("react.early_return_sentinel");
bb11: {
const x = [];
if (props.cond) {
@@ -74,7 +74,7 @@ function Component(props) {
} else {
t53 = $[1];
}
if (t53 !== Symbol.for("react.memo_cache_sentinel")) {
if (t53 !== Symbol.for("react.early_return_sentinel")) {
return t53;
}
}
@@ -15,17 +15,15 @@ import { makeArray } from "shared-runtime";
*
* We have to use a distinct sentinel for the early return value.
*
* Here the fixture will always take the "else" branch and never early return, and we should see that
* "recreate x" is only logged once, the first time we execute.
* Here the fixture will always take the "else" branch and never early return. Logging (not included)
* confirms that the scope for `x` only executes once, on the first render of the component.
*/
let ENABLE_FEATURE = false;
function Component(props) {
let x = [];
console.log("recreate x");
if (ENABLE_FEATURE) {
x.push(42);
console.log("early return");
return x;
} else {
console.log("fallthrough");
@@ -66,32 +64,30 @@ import { makeArray } from "shared-runtime";
*
* We have to use a distinct sentinel for the early return value.
*
* Here the fixture will always take the "else" branch and never early return, and we should see that
* "recreate x" is only logged once, the first time we execute.
* Here the fixture will always take the "else" branch and never early return. Logging (not included)
* confirms that the scope for `x` only executes once, on the first render of the component.
*/
let ENABLE_FEATURE = false;
function Component(props) {
const $ = useMemoCache(3);
let t53;
let t37;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t53 = Symbol.for("react.memo_cache_sentinel");
t37 = Symbol.for("react.early_return_sentinel");
bb8: {
const x = [];
console.log("recreate x");
if (ENABLE_FEATURE) {
x.push(42);
console.log("early return");
t53 = x;
t37 = x;
break bb8;
}
}
$[0] = t53;
$[0] = t37;
} else {
t53 = $[0];
t37 = $[0];
}
if (t53 !== Symbol.for("react.memo_cache_sentinel")) {
return t53;
if (t37 !== Symbol.for("react.early_return_sentinel")) {
return t37;
}
console.log("fallthrough");
@@ -132,4 +128,4 @@ export const FIXTURE_ENTRYPOINT = {
[3.14]
[42]
[3.14]
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']
logs: ['fallthrough','fallthrough','fallthrough','fallthrough','fallthrough','fallthrough','fallthrough','fallthrough']
@@ -11,17 +11,15 @@ import { makeArray } from "shared-runtime";
*
* We have to use a distinct sentinel for the early return value.
*
* Here the fixture will always take the "else" branch and never early return, and we should see that
* "recreate x" is only logged once, the first time we execute.
* Here the fixture will always take the "else" branch and never early return. Logging (not included)
* confirms that the scope for `x` only executes once, on the first render of the component.
*/
let ENABLE_FEATURE = false;
function Component(props) {
let x = [];
console.log("recreate x");
if (ENABLE_FEATURE) {
x.push(42);
console.log("early return");
return x;
} else {
console.log("fallthrough");
@@ -49,7 +49,7 @@ function Component(props) {
const $ = useMemoCache(4);
let t33;
if ($[0] !== props) {
t33 = Symbol.for("react.memo_cache_sentinel");
t33 = Symbol.for("react.early_return_sentinel");
bb8: {
const x = [];
if (props.cond) {
@@ -74,7 +74,7 @@ function Component(props) {
} else {
t33 = $[1];
}
if (t33 !== Symbol.for("react.memo_cache_sentinel")) {
if (t33 !== Symbol.for("react.early_return_sentinel")) {
return t33;
}
}
@@ -35,7 +35,7 @@ function Component(props) {
let y;
let t46;
if ($[0] !== props) {
t46 = Symbol.for("react.memo_cache_sentinel");
t46 = Symbol.for("react.early_return_sentinel");
bb11: {
const x = [];
if (props.cond) {
@@ -64,7 +64,7 @@ function Component(props) {
y = $[1];
t46 = $[2];
}
if (t46 !== Symbol.for("react.memo_cache_sentinel")) {
if (t46 !== Symbol.for("react.early_return_sentinel")) {
return t46;
}
return y;
@@ -34,7 +34,7 @@ function Component(props) {
const $ = useMemoCache(3);
let t49;
if ($[0] !== props.y || $[1] !== props.e) {
t49 = Symbol.for("react.memo_cache_sentinel");
t49 = Symbol.for("react.early_return_sentinel");
bb18: {
try {
const y = [];
@@ -56,7 +56,7 @@ function Component(props) {
} else {
t49 = $[2];
}
if (t49 !== Symbol.for("react.memo_cache_sentinel")) {
if (t49 !== Symbol.for("react.early_return_sentinel")) {
return t49;
}
}
@@ -35,7 +35,7 @@ function Component(props) {
const $ = useMemoCache(1);
let t36;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t36 = Symbol.for("react.memo_cache_sentinel");
t36 = Symbol.for("react.early_return_sentinel");
bb11: {
const x = [];
try {
@@ -54,7 +54,7 @@ function Component(props) {
} else {
t36 = $[0];
}
if (t36 !== Symbol.for("react.memo_cache_sentinel")) {
if (t36 !== Symbol.for("react.early_return_sentinel")) {
return t36;
}
}
@@ -37,7 +37,7 @@ function Component(props) {
let x;
let t43;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t43 = Symbol.for("react.memo_cache_sentinel");
t43 = Symbol.for("react.early_return_sentinel");
bb25: {
x = [];
try {
@@ -59,7 +59,7 @@ function Component(props) {
x = $[0];
t43 = $[1];
}
if (t43 !== Symbol.for("react.memo_cache_sentinel")) {
if (t43 !== Symbol.for("react.early_return_sentinel")) {
return t43;
}
return x;