Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[compiler] Recompute values every time #29657

Merged
merged 6 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,11 @@ function codegenReactiveScope(
): void {
const cacheStoreStatements: Array<t.Statement> = [];
const cacheLoadStatements: Array<t.Statement> = [];
const cacheLoads: Array<{
name: t.Identifier;
index: number;
value: t.Expression;
}> = [];
const changeExpressions: Array<t.Expression> = [];
const changeExpressionComments: Array<string> = [];
const outputComments: Array<string> = [];
Expand Down Expand Up @@ -488,6 +493,10 @@ function codegenReactiveScope(
} else {
changeExpressions.push(comparison);
}
/*
* Adding directly to cacheStoreStatements rather than cacheLoads, because there
* is no corresponding cacheLoadStatement for dependencies
*/
cacheStoreStatements.push(
t.expressionStatement(
t.assignmentExpression(
Expand Down Expand Up @@ -523,32 +532,7 @@ function codegenReactiveScope(
t.variableDeclaration("let", [t.variableDeclarator(name)])
);
}
cacheStoreStatements.push(
t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(
t.identifier(cx.synthesizeName("$")),
t.numericLiteral(index),
true
),
wrapCacheDep(cx, name)
)
)
);
cacheLoadStatements.push(
t.expressionStatement(
t.assignmentExpression(
"=",
name,
t.memberExpression(
t.identifier(cx.synthesizeName("$")),
t.numericLiteral(index),
true
)
)
)
);
cacheLoads.push({ name, index, value: wrapCacheDep(cx, name) });
cx.declare(identifier);
}
for (const reassignment of scope.reassignments) {
Expand All @@ -558,34 +542,9 @@ function codegenReactiveScope(
}
const name = convertIdentifier(reassignment);
outputComments.push(name.name);

cacheStoreStatements.push(
t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(
t.identifier(cx.synthesizeName("$")),
t.numericLiteral(index),
true
),
wrapCacheDep(cx, name)
)
)
);
cacheLoadStatements.push(
t.expressionStatement(
t.assignmentExpression(
"=",
name,
t.memberExpression(
t.identifier(cx.synthesizeName("$")),
t.numericLiteral(index),
true
)
)
)
);
cacheLoads.push({ name, index, value: wrapCacheDep(cx, name) });
}

let testCondition = (changeExpressions as Array<t.Expression>).reduce(
(acc: t.Expression | null, ident: t.Expression) => {
if (acc == null) {
Expand Down Expand Up @@ -632,67 +591,116 @@ function codegenReactiveScope(
);
}
let computationBlock = codegenBlock(cx, block);

let memoStatement;
const memoBlock = t.blockStatement(cacheLoadStatements);
if (
cx.env.config.enableChangeDetectionForDebugging != null &&
changeExpressions.length > 0
) {
const detectionFunction =
cx.env.config.enableChangeDetectionForDebugging.importSpecifierName;
const cacheLoadOldValueStatements: Array<t.Statement> = [];
const changeDetectionStatements: Array<t.Statement> = [];
const oldVarDeclarationStatements: Array<t.Statement> = [];
memoBlock.body.forEach((stmt) => {
if (
stmt.type === "ExpressionStatement" &&
stmt.expression.type === "AssignmentExpression" &&
stmt.expression.left.type === "Identifier"
) {
const name = stmt.expression.left.name;
const loadName = cx.synthesizeName(`old$${name}`);
oldVarDeclarationStatements.push(
t.variableDeclaration("let", [
t.variableDeclarator(t.identifier(loadName)),
const idempotenceDetectionStatements: Array<t.Statement> = [];

for (const { name, index, value } of cacheLoads) {
const loadName = cx.synthesizeName(`old$${name.name}`);
const slot = t.memberExpression(
t.identifier(cx.synthesizeName("$")),
t.numericLiteral(index),
true
);
cacheStoreStatements.push(
t.expressionStatement(t.assignmentExpression("=", slot, value))
);
cacheLoadOldValueStatements.push(
t.variableDeclaration("let", [
t.variableDeclarator(t.identifier(loadName), slot),
])
);
changeDetectionStatements.push(
t.expressionStatement(
t.callExpression(t.identifier(detectionFunction), [
t.identifier(loadName),
name,
t.stringLiteral(name.name),
t.stringLiteral(cx.fnName),
t.stringLiteral("cached"),
])
);
stmt.expression.left = t.identifier(loadName);
changeDetectionStatements.push(
t.expressionStatement(
t.callExpression(t.identifier(detectionFunction), [
t.identifier(loadName),
t.identifier(name),
t.stringLiteral(name),
t.stringLiteral(cx.fnName),
])
)
);
changeDetectionStatements.push(
t.expressionStatement(
t.assignmentExpression(
"=",
t.identifier(name),
t.identifier(loadName)
)
)
);
}
});
)
);
idempotenceDetectionStatements.push(
t.expressionStatement(
t.callExpression(t.identifier(detectionFunction), [
slot,
name,
t.stringLiteral(name.name),
t.stringLiteral(cx.fnName),
t.stringLiteral("recomputed"),
])
)
);
idempotenceDetectionStatements.push(
t.expressionStatement(t.assignmentExpression("=", name, slot))
);
}
const condition = cx.synthesizeName("condition");
memoStatement = t.blockStatement([
...computationBlock.body,
t.variableDeclaration("let", [
t.variableDeclarator(t.identifier(condition), testCondition),
]),
t.ifStatement(
t.unaryExpression("!", testCondition),
t.unaryExpression("!", t.identifier(condition)),
t.blockStatement([
...oldVarDeclarationStatements,
...memoBlock.body,
...cacheLoadOldValueStatements,
...changeDetectionStatements,
])
),
...cacheStoreStatements,
t.ifStatement(
t.identifier(condition),
t.blockStatement([
...computationBlock.body,
...idempotenceDetectionStatements,
])
),
]);
} else {
for (const { name, index, value } of cacheLoads) {
cacheStoreStatements.push(
t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(
t.identifier(cx.synthesizeName("$")),
t.numericLiteral(index),
true
),
value
)
)
);
cacheLoadStatements.push(
t.expressionStatement(
t.assignmentExpression(
"=",
name,
t.memberExpression(
t.identifier(cx.synthesizeName("$")),
t.numericLiteral(index),
true
)
)
)
);
}
computationBlock.body.push(...cacheStoreStatements);

memoStatement = t.ifStatement(testCondition, computationBlock, memoBlock);
memoStatement = t.ifStatement(
testCondition,
computationBlock,
t.blockStatement(cacheLoadStatements)
);
}

if (cx.env.config.enableMemoizationComments) {
Expand Down Expand Up @@ -734,9 +742,9 @@ function codegenReactiveScope(
true
);
}
if (memoBlock.body.length > 0) {
if (cacheLoadStatements.length > 0) {
t.addComment(
memoBlock.body[0]!,
cacheLoadStatements[0]!,
"leading",
` Inputs did not change, use cached value`,
true
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@

## Input

```javascript
// @enableChangeDetectionForDebugging
function Component(props) {
let x = null;
if (props.cond) {
x = [];
x.push(props.value);
}
return x;
}

```

## Code

```javascript
import { $structuralCheck } from "react-compiler-runtime";
import { c as _c } from "react/compiler-runtime"; // @enableChangeDetectionForDebugging
function Component(props) {
const $ = _c(2);
let x = null;
if (props.cond) {
{
x = [];
x.push(props.value);
let condition = $[0] !== props.value;
if (!condition) {
let old$x = $[1];
$structuralCheck(old$x, x, "x", "Component", "cached");
}
$[0] = props.value;
$[1] = x;
if (condition) {
x = [];
x.push(props.value);
$structuralCheck($[1], x, "x", "Component", "recomputed");
x = $[1];
}
}
}
return x;
}

```

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// @enableChangeDetectionForDebugging
function Component(props) {
let x = null;
if (props.cond) {
x = [];
x.push(props.value);
}
return x;
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,29 +43,37 @@ function Component(props) {
let t0;
{
t0 = f(props.x);
if (!($[0] !== props.x)) {
let old$t0;
old$t0 = $[1];
$structuralCheck(old$t0, t0, "t0", "Component");
t0 = old$t0;
let condition = $[0] !== props.x;
if (!condition) {
let old$t0 = $[1];
$structuralCheck(old$t0, t0, "t0", "Component", "cached");
}
$[0] = props.x;
$[1] = t0;
if (condition) {
t0 = f(props.x);
$structuralCheck($[1], t0, "t0", "Component", "recomputed");
t0 = $[1];
}
}
const w = t0;
const z = useOther(w);
const [x] = useState(z);
let t1;
{
t1 = <div>{x}</div>;
if (!($[2] !== x)) {
let old$t1;
old$t1 = $[3];
$structuralCheck(old$t1, t1, "t1", "Component");
t1 = old$t1;
let condition = $[2] !== x;
if (!condition) {
let old$t1 = $[3];
$structuralCheck(old$t1, t1, "t1", "Component", "cached");
}
$[2] = x;
$[3] = t1;
if (condition) {
t1 = <div>{x}</div>;
$structuralCheck($[3], t1, "t1", "Component", "recomputed");
t1 = $[3];
}
}
return t1;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,18 @@ function Component(props) {
let t1;
{
t1 = <div>{x}</div>;
if (!($[1] !== x)) {
let old$t1;
old$t1 = $[2];
$structuralCheck(old$t1, t1, "t1", "Component");
t1 = old$t1;
let condition = $[1] !== x;
if (!condition) {
let old$t1 = $[2];
$structuralCheck(old$t1, t1, "t1", "Component", "cached");
}
$[1] = x;
$[2] = t1;
if (condition) {
t1 = <div>{x}</div>;
$structuralCheck($[2], t1, "t1", "Component", "recomputed");
t1 = $[2];
}
}
return t1;
}
Expand Down
Loading