[compiler] Make inserting outlined functions more resilient

To handle more cases, always append the synthetic outlined function as a
new child of the module rather than make assumptions about the original
function. This should handle whatever case where the original function
expression may be a child of a variety of parents

ghstack-source-id: 8581edb8be
Pull Request resolved: https://github.com/facebook/react/pull/30466
This commit is contained in:
Lauren Tan
2024-07-25 15:38:19 -04:00
parent d529a43212
commit 9f3bbb05ab
5 changed files with 100 additions and 76 deletions
@@ -196,6 +196,7 @@ export function createNewFunctionNode(
}
function insertNewOutlinedFunctionNode(
program: NodePath<t.Program>,
originalFn: BabelFn,
compiledFn: CodegenFunction,
): NodePath<t.Function> {
@@ -216,14 +217,6 @@ function insertNewOutlinedFunctionNode(
*/
case 'ArrowFunctionExpression':
case 'FunctionExpression': {
CompilerError.invariant(
originalFn.parentPath.isVariableDeclarator() &&
originalFn.parentPath.parentPath.isVariableDeclaration(),
{
reason: 'Expected a variable declarator parent',
loc: originalFn.node.loc ?? null,
},
);
const fn: t.FunctionDeclaration = {
type: 'FunctionDeclaration',
id: compiledFn.id,
@@ -233,8 +226,7 @@ function insertNewOutlinedFunctionNode(
params: compiledFn.params,
body: compiledFn.body,
};
const varDecl = originalFn.parentPath.parentPath;
const insertedFuncDecl = varDecl.insertAfter(fn)[0]!;
const insertedFuncDecl = program.pushContainer('body', [fn])[0]!;
CompilerError.invariant(insertedFuncDecl.isFunctionDeclaration(), {
reason: 'Expected inserted function declaration',
description: `Got: ${insertedFuncDecl}`,
@@ -468,7 +460,11 @@ export function compileProgram(
reason: 'Unexpected nested outlined functions',
loc: outlined.fn.loc,
});
const fn = insertNewOutlinedFunctionNode(current.fn, outlined.fn);
const fn = insertNewOutlinedFunctionNode(
program,
current.fn,
outlined.fn,
);
fn.skip();
ALREADY_COMPILED.add(fn.node);
if (outlined.type !== null) {
@@ -1,62 +0,0 @@
## Input
```javascript
function Component(props) {
return <View {...props} />;
}
const View = React.memo(({items}) => {
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
});
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [
{
items: [
{id: 2, name: 'foo'},
{id: 3, name: 'bar'},
],
},
],
};
```
## Error
```
3 | }
4 |
> 5 | const View = React.memo(({items}) => {
| ^^^^^^^^^^^^^^
> 6 | return (
| ^^^^^^^^^^
> 7 | <ul>
| ^^^^^^^^^^
> 8 | {items.map(item => (
| ^^^^^^^^^^
> 9 | <li key={item.id}>{item.name}</li>
| ^^^^^^^^^^
> 10 | ))}
| ^^^^^^^^^^
> 11 | </ul>
| ^^^^^^^^^^
> 12 | );
| ^^^^^^^^^^
> 13 | });
| ^^ Invariant: Expected a variable declarator parent (5:13)
14 |
15 | export const FIXTURE_ENTRYPOINT = {
16 | fn: Component,
```
@@ -50,9 +50,6 @@ const Component2 = (props) => {
}
return t1;
};
function _temp(item) {
return <li key={item.id}>{item.name}</li>;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component2,
@@ -65,6 +62,9 @@ export const FIXTURE_ENTRYPOINT = {
},
],
};
function _temp(item) {
return <li key={item.id}>{item.name}</li>;
}
```
@@ -0,0 +1,90 @@
## Input
```javascript
function Component(props) {
return <View {...props} />;
}
const View = React.memo(({items}) => {
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
});
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [
{
items: [
{id: 2, name: 'foo'},
{id: 3, name: 'bar'},
],
},
],
};
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
function Component(props) {
const $ = _c(2);
let t0;
if ($[0] !== props) {
t0 = <View {...props} />;
$[0] = props;
$[1] = t0;
} else {
t0 = $[1];
}
return t0;
}
const View = React.memo((t0) => {
const $ = _c(4);
const { items } = t0;
let t1;
if ($[0] !== items) {
t1 = items.map(_temp);
$[0] = items;
$[1] = t1;
} else {
t1 = $[1];
}
let t2;
if ($[2] !== t1) {
t2 = <ul>{t1}</ul>;
$[2] = t1;
$[3] = t2;
} else {
t2 = $[3];
}
return t2;
});
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [
{
items: [
{ id: 2, name: "foo" },
{ id: 3, name: "bar" },
],
},
],
};
function _temp(item) {
return <li key={item.id}>{item.name}</li>;
}
```
### Eval output
(kind: ok) <ul><li>foo</li><li>bar</li></ul>