@samitouri / QOS-React / commits / 9f3bbb05ab

[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: 8581edb8be706c0f4ce506caf7cd9b0f5a145bc8 Pull Request resolved: https://github.com/facebook/react/pull/30466

Lauren Tan committed Jul 25, 2024 at 15:18 UTC 9f3bbb05ab29d531e343ddb2e85d89943669b6e7
5 files changed +100 -76
compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts
+7 -11
@@ -196,6 +196,7 @@ export function createNewFunctionNode(
196 }
197
198 function insertNewOutlinedFunctionNode(
199 + program: NodePath<t.Program>,
200 originalFn: BabelFn,
201 compiledFn: CodegenFunction,
202 ): NodePath<t.Function> {
@@ -216,14 +217,6 @@ function insertNewOutlinedFunctionNode(
217 */
218 case 'ArrowFunctionExpression':
219 case 'FunctionExpression': {
219 - CompilerError.invariant(
220 - originalFn.parentPath.isVariableDeclarator() &&
221 - originalFn.parentPath.parentPath.isVariableDeclaration(),
222 - {
223 - reason: 'Expected a variable declarator parent',
224 - loc: originalFn.node.loc ?? null,
225 - },
226 - );
220 const fn: t.FunctionDeclaration = {
221 type: 'FunctionDeclaration',
222 id: compiledFn.id,
@@ -233,8 +226,7 @@ function insertNewOutlinedFunctionNode(
226 params: compiledFn.params,
227 body: compiledFn.body,
228 };
236 - const varDecl = originalFn.parentPath.parentPath;
237 - const insertedFuncDecl = varDecl.insertAfter(fn)[0]!;
229 + const insertedFuncDecl = program.pushContainer('body', [fn])[0]!;
230 CompilerError.invariant(insertedFuncDecl.isFunctionDeclaration(), {
231 reason: 'Expected inserted function declaration',
232 description: `Got: ${insertedFuncDecl}`,
@@ -468,7 +460,11 @@ export function compileProgram(
460 reason: 'Unexpected nested outlined functions',
461 loc: outlined.fn.loc,
462 });
471 - const fn = insertNewOutlinedFunctionNode(current.fn, outlined.fn);
463 + const fn = insertNewOutlinedFunctionNode(
464 + program,
465 + current.fn,
466 + outlined.fn,
467 + );
468 fn.skip();
469 ALREADY_COMPILED.add(fn.node);
470 if (outlined.type !== null) {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.bug-outlining-in-react-memo.expect.md deleted
-62
@@ -1,62 +0,0 @@
1 -
2 -## Input
3 -
4 -```javascript
5 -function Component(props) {
6 - return <View {...props} />;
7 -}
8 -
9 -const View = React.memo(({items}) => {
10 - return (
11 - <ul>
12 - {items.map(item => (
13 - <li key={item.id}>{item.name}</li>
14 - ))}
15 - </ul>
16 - );
17 -});
18 -
19 -export const FIXTURE_ENTRYPOINT = {
20 - fn: Component,
21 - params: [
22 - {
23 - items: [
24 - {id: 2, name: 'foo'},
25 - {id: 3, name: 'bar'},
26 - ],
27 - },
28 - ],
29 -};
30 -
31 -```
32 -
33 -
34 -## Error
35 -
36 -```
37 - 3 | }
38 - 4 |
39 -> 5 | const View = React.memo(({items}) => {
40 - | ^^^^^^^^^^^^^^
41 -> 6 | return (
42 - | ^^^^^^^^^^
43 -> 7 | <ul>
44 - | ^^^^^^^^^^
45 -> 8 | {items.map(item => (
46 - | ^^^^^^^^^^
47 -> 9 | <li key={item.id}>{item.name}</li>
48 - | ^^^^^^^^^^
49 -> 10 | ))}
50 - | ^^^^^^^^^^
51 -> 11 | </ul>
52 - | ^^^^^^^^^^
53 -> 12 | );
54 - | ^^^^^^^^^^
55 -> 13 | });
56 - | ^^ Invariant: Expected a variable declarator parent (5:13)
57 - 14 |
58 - 15 | export const FIXTURE_ENTRYPOINT = {
59 - 16 | fn: Component,
60 -```
61 -
62 -
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/outlining-in-func-expr.expect.md
+3 -3
@@ -50,9 +50,6 @@ const Component2 = (props) => {
50 }
51 return t1;
52 };
53 -function _temp(item) {
54 - return <li key={item.id}>{item.name}</li>;
55 -}
53
54 export const FIXTURE_ENTRYPOINT = {
55 fn: Component2,
@@ -65,6 +62,9 @@ export const FIXTURE_ENTRYPOINT = {
62 },
63 ],
64 };
65 +function _temp(item) {
66 + return <li key={item.id}>{item.name}</li>;
67 +}
68
69 ```
70
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/outlining-in-react-memo.expect.md new
+90
@@ -0,0 +1,90 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + return <View {...props} />;
7 +}
8 +
9 +const View = React.memo(({items}) => {
10 + return (
11 + <ul>
12 + {items.map(item => (
13 + <li key={item.id}>{item.name}</li>
14 + ))}
15 + </ul>
16 + );
17 +});
18 +
19 +export const FIXTURE_ENTRYPOINT = {
20 + fn: Component,
21 + params: [
22 + {
23 + items: [
24 + {id: 2, name: 'foo'},
25 + {id: 3, name: 'bar'},
26 + ],
27 + },
28 + ],
29 +};
30 +
31 +```
32 +
33 +## Code
34 +
35 +```javascript
36 +import { c as _c } from "react/compiler-runtime";
37 +function Component(props) {
38 + const $ = _c(2);
39 + let t0;
40 + if ($[0] !== props) {
41 + t0 = <View {...props} />;
42 + $[0] = props;
43 + $[1] = t0;
44 + } else {
45 + t0 = $[1];
46 + }
47 + return t0;
48 +}
49 +
50 +const View = React.memo((t0) => {
51 + const $ = _c(4);
52 + const { items } = t0;
53 + let t1;
54 + if ($[0] !== items) {
55 + t1 = items.map(_temp);
56 + $[0] = items;
57 + $[1] = t1;
58 + } else {
59 + t1 = $[1];
60 + }
61 + let t2;
62 + if ($[2] !== t1) {
63 + t2 = <ul>{t1}</ul>;
64 + $[2] = t1;
65 + $[3] = t2;
66 + } else {
67 + t2 = $[3];
68 + }
69 + return t2;
70 +});
71 +
72 +export const FIXTURE_ENTRYPOINT = {
73 + fn: Component,
74 + params: [
75 + {
76 + items: [
77 + { id: 2, name: "foo" },
78 + { id: 3, name: "bar" },
79 + ],
80 + },
81 + ],
82 +};
83 +function _temp(item) {
84 + return <li key={item.id}>{item.name}</li>;
85 +}
86 +
87 +```
88 +
89 +### Eval output
90 +(kind: ok) <ul><li>foo</li><li>bar</li></ul>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/outlining-in-react-memo.js renamed