mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[compiler] Support for context variable loop iterators
Summary:
Fixing a compiler todo
ghstack-source-id: c4d9226b17
Pull Request resolved: https://github.com/facebook/react/pull/31709
This commit is contained in:
+26
-27
@@ -1354,20 +1354,6 @@ function codegenForInit(
|
||||
init: ReactiveValue,
|
||||
): t.Expression | t.VariableDeclaration | null {
|
||||
if (init.kind === 'SequenceExpression') {
|
||||
for (const instr of init.instructions) {
|
||||
if (instr.value.kind === 'DeclareContext') {
|
||||
CompilerError.throwTodo({
|
||||
reason: `Support for loops where the index variable is a context variable`,
|
||||
loc: instr.loc,
|
||||
description:
|
||||
instr.value.lvalue.place.identifier.name != null
|
||||
? `\`${instr.value.lvalue.place.identifier.name.value}\` is a context variable`
|
||||
: null,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const body = codegenBlock(
|
||||
cx,
|
||||
init.instructions.map(instruction => ({
|
||||
@@ -1378,20 +1364,33 @@ function codegenForInit(
|
||||
const declarators: Array<t.VariableDeclarator> = [];
|
||||
let kind: 'let' | 'const' = 'const';
|
||||
body.forEach(instr => {
|
||||
CompilerError.invariant(
|
||||
instr.type === 'VariableDeclaration' &&
|
||||
(instr.kind === 'let' || instr.kind === 'const'),
|
||||
{
|
||||
reason: 'Expected a variable declaration',
|
||||
loc: init.loc,
|
||||
description: `Got ${instr.type}`,
|
||||
suggestions: null,
|
||||
},
|
||||
);
|
||||
if (instr.kind === 'let') {
|
||||
kind = 'let';
|
||||
let top: undefined | t.VariableDeclarator = undefined;
|
||||
if (
|
||||
instr.type === 'ExpressionStatement' &&
|
||||
instr.expression.type === 'AssignmentExpression' &&
|
||||
instr.expression.operator === '=' &&
|
||||
instr.expression.left.type === 'Identifier' &&
|
||||
(top = declarators.at(-1))?.id.type === 'Identifier' &&
|
||||
top?.id.name === instr.expression.left.name &&
|
||||
top?.init == null
|
||||
) {
|
||||
top.init = instr.expression.right;
|
||||
} else {
|
||||
CompilerError.invariant(
|
||||
instr.type === 'VariableDeclaration' &&
|
||||
(instr.kind === 'let' || instr.kind === 'const'),
|
||||
{
|
||||
reason: 'Expected a variable declaration',
|
||||
loc: init.loc,
|
||||
description: `Got ${instr.type}`,
|
||||
suggestions: null,
|
||||
},
|
||||
);
|
||||
if (instr.kind === 'let') {
|
||||
kind = 'let';
|
||||
}
|
||||
declarators.push(...instr.declarations);
|
||||
}
|
||||
declarators.push(...instr.declarations);
|
||||
});
|
||||
CompilerError.invariant(declarators.length > 0, {
|
||||
reason: 'Expected a variable declaration',
|
||||
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component() {
|
||||
const data = useData();
|
||||
const items = [];
|
||||
// NOTE: `i` is a context variable because it's reassigned and also referenced
|
||||
// within a closure, the `onClick` handler of each item
|
||||
for (let i = MIN; i <= MAX; i += INCREMENT) {
|
||||
items.push(<Stringify key={i} onClick={() => data.set(i)} />);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
4 | // NOTE: `i` is a context variable because it's reassigned and also referenced
|
||||
5 | // within a closure, the `onClick` handler of each item
|
||||
> 6 | for (let i = MIN; i <= MAX; i += INCREMENT) {
|
||||
| ^^^^^^^^^^^ Todo: Support for loops where the index variable is a context variable. `i` is a context variable (6:6)
|
||||
7 | items.push(<Stringify key={i} onClick={() => data.set(i)} />);
|
||||
8 | }
|
||||
9 | return items;
|
||||
```
|
||||
|
||||
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component() {
|
||||
const data = useData();
|
||||
const items = [];
|
||||
// NOTE: `i` is a context variable because it's reassigned and also referenced
|
||||
// within a closure, the `onClick` handler of each item
|
||||
for (let i = MIN; i <= MAX; i += INCREMENT) {
|
||||
items.push(<div key={i} onClick={() => data.set(i)} />);
|
||||
}
|
||||
return <>{items}</>;
|
||||
}
|
||||
|
||||
const MIN = 0;
|
||||
const MAX = 3;
|
||||
const INCREMENT = 1;
|
||||
|
||||
function useData() {
|
||||
return new Map();
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
params: [],
|
||||
fn: Component,
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Component() {
|
||||
const $ = _c(2);
|
||||
const data = useData();
|
||||
let t0;
|
||||
if ($[0] !== data) {
|
||||
const items = [];
|
||||
for (let i = MIN; i <= MAX; i = i + INCREMENT, i) {
|
||||
items.push(<div key={i} onClick={() => data.set(i)} />);
|
||||
}
|
||||
|
||||
t0 = <>{items}</>;
|
||||
$[0] = data;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
const MIN = 0;
|
||||
const MAX = 3;
|
||||
const INCREMENT = 1;
|
||||
|
||||
function useData() {
|
||||
const $ = _c(1);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = new Map();
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
params: [],
|
||||
fn: Component,
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div></div><div></div><div></div><div></div>
|
||||
+15
-2
@@ -4,7 +4,20 @@ function Component() {
|
||||
// NOTE: `i` is a context variable because it's reassigned and also referenced
|
||||
// within a closure, the `onClick` handler of each item
|
||||
for (let i = MIN; i <= MAX; i += INCREMENT) {
|
||||
items.push(<Stringify key={i} onClick={() => data.set(i)} />);
|
||||
items.push(<div key={i} onClick={() => data.set(i)} />);
|
||||
}
|
||||
return items;
|
||||
return <>{items}</>;
|
||||
}
|
||||
|
||||
const MIN = 0;
|
||||
const MAX = 3;
|
||||
const INCREMENT = 1;
|
||||
|
||||
function useData() {
|
||||
return new Map();
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
params: [],
|
||||
fn: Component,
|
||||
};
|
||||
Reference in New Issue
Block a user