@samitouri / QOS-React-2 / commits / 09197bb786

[compiler] Outline jsx with duplicate attributes (#31441)

Previously, we would skip outlining jsx expressions that had duplicate jsx attributes as we would not rename them causing incorrect compilation. In this PR, we add outlining support for duplicate jsx attributes by renaming them.

Sathya Gunasekaran committed Nov 6, 2024 at 17:50 UTC 09197bb786344d2ede1286e7f3ec4e21b18a58f2
9 files changed +675 -23
compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineJsx.ts
+8 -10
@@ -222,6 +222,8 @@ function collectProps(
222 const attributes: Array<OutlinedJsxAttribute> = [];
223 const jsxIds = new Set(instructions.map(i => i.lvalue.identifier.id));
224 const seen: Set<string> = new Set();
225 + let id = 1;
226 +
227 for (const instr of instructions) {
228 const {value} = instr;
229
@@ -230,21 +232,17 @@ function collectProps(
232 return null;
233 }
234
233 - /*
234 - * TODO(gsn): Handle attributes that have same value across
235 - * the outlined jsx instructions.
236 - */
237 - if (seen.has(at.name)) {
238 - return null;
239 - }
240 -
235 if (at.kind === 'JsxAttribute') {
242 - seen.add(at.name);
236 + let newName = at.name;
237 + while (seen.has(newName)) {
238 + newName = `${at.name}${id++}`;
239 + }
240 attributes.push({
241 originalName: at.name,
245 - newName: at.name,
242 + newName,
243 place: at.place,
244 });
245 + seen.add(newName);
246 }
247 }
248
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-outlining-dup-key-diff-value.expect.md new
+166
@@ -0,0 +1,166 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @enableJsxOutlining
6 +function Component({arr}) {
7 + const x = useX();
8 + return (
9 + <>
10 + {arr.map((i, id) => {
11 + return (
12 + <Bar key={id} x={x}>
13 + <Baz i={i + 'i'}></Baz>
14 + <Foo k={i + 'j'}></Foo>
15 + </Bar>
16 + );
17 + })}
18 + </>
19 + );
20 +}
21 +function Bar({x, children}) {
22 + return (
23 + <>
24 + {x}
25 + {children}
26 + </>
27 + );
28 +}
29 +
30 +function Baz({i}) {
31 + return i;
32 +}
33 +
34 +function Foo({k}) {
35 + return k;
36 +}
37 +
38 +function useX() {
39 + return 'x';
40 +}
41 +
42 +export const FIXTURE_ENTRYPOINT = {
43 + fn: Component,
44 + params: [{arr: ['foo', 'bar']}],
45 +};
46 +
47 +```
48 +
49 +## Code
50 +
51 +```javascript
52 +import { c as _c } from "react/compiler-runtime"; // @enableJsxOutlining
53 +function Component(t0) {
54 + const $ = _c(7);
55 + const { arr } = t0;
56 + const x = useX();
57 + let t1;
58 + if ($[0] !== arr || $[1] !== x) {
59 + let t2;
60 + if ($[3] !== x) {
61 + t2 = (i, id) => {
62 + const T0 = _temp;
63 + return <T0 i={i + "i"} k={i + "j"} key={id} x={x} />;
64 + };
65 + $[3] = x;
66 + $[4] = t2;
67 + } else {
68 + t2 = $[4];
69 + }
70 + t1 = arr.map(t2);
71 + $[0] = arr;
72 + $[1] = x;
73 + $[2] = t1;
74 + } else {
75 + t1 = $[2];
76 + }
77 + let t2;
78 + if ($[5] !== t1) {
79 + t2 = <>{t1}</>;
80 + $[5] = t1;
81 + $[6] = t2;
82 + } else {
83 + t2 = $[6];
84 + }
85 + return t2;
86 +}
87 +function _temp(t0) {
88 + const $ = _c(8);
89 + const { i: i, k: k, x: x } = t0;
90 + let t1;
91 + if ($[0] !== i) {
92 + t1 = <Baz i={i} />;
93 + $[0] = i;
94 + $[1] = t1;
95 + } else {
96 + t1 = $[1];
97 + }
98 + let t2;
99 + if ($[2] !== k) {
100 + t2 = <Foo k={k} />;
101 + $[2] = k;
102 + $[3] = t2;
103 + } else {
104 + t2 = $[3];
105 + }
106 + let t3;
107 + if ($[4] !== t1 || $[5] !== t2 || $[6] !== x) {
108 + t3 = (
109 + <Bar x={x}>
110 + {t1}
111 + {t2}
112 + </Bar>
113 + );
114 + $[4] = t1;
115 + $[5] = t2;
116 + $[6] = x;
117 + $[7] = t3;
118 + } else {
119 + t3 = $[7];
120 + }
121 + return t3;
122 +}
123 +
124 +function Bar(t0) {
125 + const $ = _c(3);
126 + const { x, children } = t0;
127 + let t1;
128 + if ($[0] !== children || $[1] !== x) {
129 + t1 = (
130 + <>
131 + {x}
132 + {children}
133 + </>
134 + );
135 + $[0] = children;
136 + $[1] = x;
137 + $[2] = t1;
138 + } else {
139 + t1 = $[2];
140 + }
141 + return t1;
142 +}
143 +
144 +function Baz(t0) {
145 + const { i } = t0;
146 + return i;
147 +}
148 +
149 +function Foo(t0) {
150 + const { k } = t0;
151 + return k;
152 +}
153 +
154 +function useX() {
155 + return "x";
156 +}
157 +
158 +export const FIXTURE_ENTRYPOINT = {
159 + fn: Component,
160 + params: [{ arr: ["foo", "bar"] }],
161 +};
162 +
163 +```
164 +
165 +### Eval output
166 +(kind: ok) xfooifoojxbaribarj
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-outlining-dup-key-diff-value.js new
+41
@@ -0,0 +1,41 @@
1 +// @enableJsxOutlining
2 +function Component({arr}) {
3 + const x = useX();
4 + return (
5 + <>
6 + {arr.map((i, id) => {
7 + return (
8 + <Bar key={id} x={x}>
9 + <Baz i={i + 'i'}></Baz>
10 + <Foo k={i + 'j'}></Foo>
11 + </Bar>
12 + );
13 + })}
14 + </>
15 + );
16 +}
17 +function Bar({x, children}) {
18 + return (
19 + <>
20 + {x}
21 + {children}
22 + </>
23 + );
24 +}
25 +
26 +function Baz({i}) {
27 + return i;
28 +}
29 +
30 +function Foo({k}) {
31 + return k;
32 +}
33 +
34 +function useX() {
35 + return 'x';
36 +}
37 +
38 +export const FIXTURE_ENTRYPOINT = {
39 + fn: Component,
40 + params: [{arr: ['foo', 'bar']}],
41 +};
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-outlining-dupe-attr-after-rename.expect.md new
+177
@@ -0,0 +1,177 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @enableJsxOutlining
6 +function Component({arr}) {
7 + const x = useX();
8 + return (
9 + <>
10 + {arr.map((i, id) => {
11 + return (
12 + <Bar key={id} x={x}>
13 + <Foo k={i + 'i'}></Foo>
14 + <Foo k={i + 'j'}></Foo>
15 + <Baz k1={i + 'j'}></Baz>
16 + </Bar>
17 + );
18 + })}
19 + </>
20 + );
21 +}
22 +function Bar({x, children}) {
23 + return (
24 + <>
25 + {x}
26 + {children}
27 + </>
28 + );
29 +}
30 +
31 +function Baz({k1}) {
32 + return k1;
33 +}
34 +
35 +function Foo({k}) {
36 + return k;
37 +}
38 +
39 +function useX() {
40 + return 'x';
41 +}
42 +
43 +export const FIXTURE_ENTRYPOINT = {
44 + fn: Component,
45 + params: [{arr: ['foo', 'bar']}],
46 +};
47 +
48 +```
49 +
50 +## Code
51 +
52 +```javascript
53 +import { c as _c } from "react/compiler-runtime"; // @enableJsxOutlining
54 +function Component(t0) {
55 + const $ = _c(7);
56 + const { arr } = t0;
57 + const x = useX();
58 + let t1;
59 + if ($[0] !== arr || $[1] !== x) {
60 + let t2;
61 + if ($[3] !== x) {
62 + t2 = (i, id) => {
63 + const T0 = _temp;
64 + return <T0 k={i + "i"} k1={i + "j"} k12={i + "j"} key={id} x={x} />;
65 + };
66 + $[3] = x;
67 + $[4] = t2;
68 + } else {
69 + t2 = $[4];
70 + }
71 + t1 = arr.map(t2);
72 + $[0] = arr;
73 + $[1] = x;
74 + $[2] = t1;
75 + } else {
76 + t1 = $[2];
77 + }
78 + let t2;
79 + if ($[5] !== t1) {
80 + t2 = <>{t1}</>;
81 + $[5] = t1;
82 + $[6] = t2;
83 + } else {
84 + t2 = $[6];
85 + }
86 + return t2;
87 +}
88 +function _temp(t0) {
89 + const $ = _c(11);
90 + const { k: k, k1: k1, k12: k12, x: x } = t0;
91 + let t1;
92 + if ($[0] !== k) {
93 + t1 = <Foo k={k} />;
94 + $[0] = k;
95 + $[1] = t1;
96 + } else {
97 + t1 = $[1];
98 + }
99 + let t2;
100 + if ($[2] !== k1) {
101 + t2 = <Foo k={k1} />;
102 + $[2] = k1;
103 + $[3] = t2;
104 + } else {
105 + t2 = $[3];
106 + }
107 + let t3;
108 + if ($[4] !== k12) {
109 + t3 = <Baz k1={k12} />;
110 + $[4] = k12;
111 + $[5] = t3;
112 + } else {
113 + t3 = $[5];
114 + }
115 + let t4;
116 + if ($[6] !== t1 || $[7] !== t2 || $[8] !== t3 || $[9] !== x) {
117 + t4 = (
118 + <Bar x={x}>
119 + {t1}
120 + {t2}
121 + {t3}
122 + </Bar>
123 + );
124 + $[6] = t1;
125 + $[7] = t2;
126 + $[8] = t3;
127 + $[9] = x;
128 + $[10] = t4;
129 + } else {
130 + t4 = $[10];
131 + }
132 + return t4;
133 +}
134 +
135 +function Bar(t0) {
136 + const $ = _c(3);
137 + const { x, children } = t0;
138 + let t1;
139 + if ($[0] !== children || $[1] !== x) {
140 + t1 = (
141 + <>
142 + {x}
143 + {children}
144 + </>
145 + );
146 + $[0] = children;
147 + $[1] = x;
148 + $[2] = t1;
149 + } else {
150 + t1 = $[2];
151 + }
152 + return t1;
153 +}
154 +
155 +function Baz(t0) {
156 + const { k1 } = t0;
157 + return k1;
158 +}
159 +
160 +function Foo(t0) {
161 + const { k } = t0;
162 + return k;
163 +}
164 +
165 +function useX() {
166 + return "x";
167 +}
168 +
169 +export const FIXTURE_ENTRYPOINT = {
170 + fn: Component,
171 + params: [{ arr: ["foo", "bar"] }],
172 +};
173 +
174 +```
175 +
176 +### Eval output
177 +(kind: ok) xfooifoojfoojxbaribarjbarj
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-outlining-dupe-attr-after-rename.js new
+42
@@ -0,0 +1,42 @@
1 +// @enableJsxOutlining
2 +function Component({arr}) {
3 + const x = useX();
4 + return (
5 + <>
6 + {arr.map((i, id) => {
7 + return (
8 + <Bar key={id} x={x}>
9 + <Foo k={i + 'i'}></Foo>
10 + <Foo k={i + 'j'}></Foo>
11 + <Baz k1={i + 'j'}></Baz>
12 + </Bar>
13 + );
14 + })}
15 + </>
16 + );
17 +}
18 +function Bar({x, children}) {
19 + return (
20 + <>
21 + {x}
22 + {children}
23 + </>
24 + );
25 +}
26 +
27 +function Baz({k1}) {
28 + return k1;
29 +}
30 +
31 +function Foo({k}) {
32 + return k;
33 +}
34 +
35 +function useX() {
36 + return 'x';
37 +}
38 +
39 +export const FIXTURE_ENTRYPOINT = {
40 + fn: Component,
41 + params: [{arr: ['foo', 'bar']}],
42 +};
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-outlining-dupe-key-dupe-component.expect.md new
+157
@@ -0,0 +1,157 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @enableJsxOutlining
6 +function Component({arr}) {
7 + const x = useX();
8 + return (
9 + <>
10 + {arr.map((i, id) => {
11 + return (
12 + <Bar key={id} x={x}>
13 + <Foo k={i + 'i'}></Foo>
14 + <Foo k={i + 'j'}></Foo>
15 + </Bar>
16 + );
17 + })}
18 + </>
19 + );
20 +}
21 +function Bar({x, children}) {
22 + return (
23 + <>
24 + {x}
25 + {children}
26 + </>
27 + );
28 +}
29 +
30 +function Foo({k}) {
31 + return k;
32 +}
33 +
34 +function useX() {
35 + return 'x';
36 +}
37 +
38 +export const FIXTURE_ENTRYPOINT = {
39 + fn: Component,
40 + params: [{arr: ['foo', 'bar']}],
41 +};
42 +
43 +```
44 +
45 +## Code
46 +
47 +```javascript
48 +import { c as _c } from "react/compiler-runtime"; // @enableJsxOutlining
49 +function Component(t0) {
50 + const $ = _c(7);
51 + const { arr } = t0;
52 + const x = useX();
53 + let t1;
54 + if ($[0] !== arr || $[1] !== x) {
55 + let t2;
56 + if ($[3] !== x) {
57 + t2 = (i, id) => {
58 + const T0 = _temp;
59 + return <T0 k={i + "i"} k1={i + "j"} key={id} x={x} />;
60 + };
61 + $[3] = x;
62 + $[4] = t2;
63 + } else {
64 + t2 = $[4];
65 + }
66 + t1 = arr.map(t2);
67 + $[0] = arr;
68 + $[1] = x;
69 + $[2] = t1;
70 + } else {
71 + t1 = $[2];
72 + }
73 + let t2;
74 + if ($[5] !== t1) {
75 + t2 = <>{t1}</>;
76 + $[5] = t1;
77 + $[6] = t2;
78 + } else {
79 + t2 = $[6];
80 + }
81 + return t2;
82 +}
83 +function _temp(t0) {
84 + const $ = _c(8);
85 + const { k: k, k1: k1, x: x } = t0;
86 + let t1;
87 + if ($[0] !== k) {
88 + t1 = <Foo k={k} />;
89 + $[0] = k;
90 + $[1] = t1;
91 + } else {
92 + t1 = $[1];
93 + }
94 + let t2;
95 + if ($[2] !== k1) {
96 + t2 = <Foo k={k1} />;
97 + $[2] = k1;
98 + $[3] = t2;
99 + } else {
100 + t2 = $[3];
101 + }
102 + let t3;
103 + if ($[4] !== t1 || $[5] !== t2 || $[6] !== x) {
104 + t3 = (
105 + <Bar x={x}>
106 + {t1}
107 + {t2}
108 + </Bar>
109 + );
110 + $[4] = t1;
111 + $[5] = t2;
112 + $[6] = x;
113 + $[7] = t3;
114 + } else {
115 + t3 = $[7];
116 + }
117 + return t3;
118 +}
119 +
120 +function Bar(t0) {
121 + const $ = _c(3);
122 + const { x, children } = t0;
123 + let t1;
124 + if ($[0] !== children || $[1] !== x) {
125 + t1 = (
126 + <>
127 + {x}
128 + {children}
129 + </>
130 + );
131 + $[0] = children;
132 + $[1] = x;
133 + $[2] = t1;
134 + } else {
135 + t1 = $[2];
136 + }
137 + return t1;
138 +}
139 +
140 +function Foo(t0) {
141 + const { k } = t0;
142 + return k;
143 +}
144 +
145 +function useX() {
146 + return "x";
147 +}
148 +
149 +export const FIXTURE_ENTRYPOINT = {
150 + fn: Component,
151 + params: [{ arr: ["foo", "bar"] }],
152 +};
153 +
154 +```
155 +
156 +### Eval output
157 +(kind: ok) xfooifoojxbaribarj
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-outlining-dupe-key-dupe-component.js new
+37
@@ -0,0 +1,37 @@
1 +// @enableJsxOutlining
2 +function Component({arr}) {
3 + const x = useX();
4 + return (
5 + <>
6 + {arr.map((i, id) => {
7 + return (
8 + <Bar key={id} x={x}>
9 + <Foo k={i + 'i'}></Foo>
10 + <Foo k={i + 'j'}></Foo>
11 + </Bar>
12 + );
13 + })}
14 + </>
15 + );
16 +}
17 +function Bar({x, children}) {
18 + return (
19 + <>
20 + {x}
21 + {children}
22 + </>
23 + );
24 +}
25 +
26 +function Foo({k}) {
27 + return k;
28 +}
29 +
30 +function useX() {
31 + return 'x';
32 +}
33 +
34 +export const FIXTURE_ENTRYPOINT = {
35 + fn: Component,
36 + params: [{arr: ['foo', 'bar']}],
37 +};
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-outlining-duplicate-prop.expect.md renamed
+45 -11
@@ -31,8 +31,8 @@ function Baz({i}) {
31 return i;
32 }
33
34 -function Foo({k}) {
35 - return k;
34 +function Foo({i}) {
35 + return i;
36 }
37
38 function useX() {
@@ -58,12 +58,10 @@ function Component(t0) {
58 if ($[0] !== arr || $[1] !== x) {
59 let t2;
60 if ($[3] !== x) {
61 - t2 = (i, id) => (
62 - <Bar key={id} x={x}>
63 - <Baz i={i} />
64 - <Foo i={i} />
65 - </Bar>
66 - );
61 + t2 = (i, id) => {
62 + const T0 = _temp;
63 + return <T0 i={i} i1={i} key={id} x={x} />;
64 + };
65 $[3] = x;
66 $[4] = t2;
67 } else {
@@ -86,6 +84,42 @@ function Component(t0) {
84 }
85 return t2;
86 }
87 +function _temp(t0) {
88 + const $ = _c(8);
89 + const { i: i, i1: i1, x: x } = t0;
90 + let t1;
91 + if ($[0] !== i) {
92 + t1 = <Baz i={i} />;
93 + $[0] = i;
94 + $[1] = t1;
95 + } else {
96 + t1 = $[1];
97 + }
98 + let t2;
99 + if ($[2] !== i1) {
100 + t2 = <Foo i={i1} />;
101 + $[2] = i1;
102 + $[3] = t2;
103 + } else {
104 + t2 = $[3];
105 + }
106 + let t3;
107 + if ($[4] !== t1 || $[5] !== t2 || $[6] !== x) {
108 + t3 = (
109 + <Bar x={x}>
110 + {t1}
111 + {t2}
112 + </Bar>
113 + );
114 + $[4] = t1;
115 + $[5] = t2;
116 + $[6] = x;
117 + $[7] = t3;
118 + } else {
119 + t3 = $[7];
120 + }
121 + return t3;
122 +}
123
124 function Bar(t0) {
125 const $ = _c(3);
@@ -113,8 +147,8 @@ function Baz(t0) {
147 }
148
149 function Foo(t0) {
116 - const { k } = t0;
117 - return k;
150 + const { i } = t0;
151 + return i;
152 }
153
154 function useX() {
@@ -129,4 +163,4 @@ export const FIXTURE_ENTRYPOINT = {
163 ```
164
165 ### Eval output
132 -(kind: ok) xfooxbar
\ No newline at end of file
166 +(kind: ok) xfoofooxbarbar
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-outlining-duplicate-prop.js renamed
+2 -2
@@ -27,8 +27,8 @@ function Baz({i}) {
27 return i;
28 }
29
30 -function Foo({k}) {
31 - return k;
30 +function Foo({i}) {
31 + return i;
32 }
33
34 function useX() {