[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.
This commit is contained in:
Sathya Gunasekaran
2024-11-06 17:50:13 +00:00
committed by GitHub
parent 2df8f61885
commit 09197bb786
9 changed files with 675 additions and 23 deletions
@@ -222,6 +222,8 @@ function collectProps(
const attributes: Array<OutlinedJsxAttribute> = [];
const jsxIds = new Set(instructions.map(i => i.lvalue.identifier.id));
const seen: Set<string> = new Set();
let id = 1;
for (const instr of instructions) {
const {value} = instr;
@@ -230,21 +232,17 @@ function collectProps(
return null;
}
/*
* TODO(gsn): Handle attributes that have same value across
* the outlined jsx instructions.
*/
if (seen.has(at.name)) {
return null;
}
if (at.kind === 'JsxAttribute') {
seen.add(at.name);
let newName = at.name;
while (seen.has(newName)) {
newName = `${at.name}${id++}`;
}
attributes.push({
originalName: at.name,
newName: at.name,
newName,
place: at.place,
});
seen.add(newName);
}
}
@@ -0,0 +1,166 @@
## Input
```javascript
// @enableJsxOutlining
function Component({arr}) {
const x = useX();
return (
<>
{arr.map((i, id) => {
return (
<Bar key={id} x={x}>
<Baz i={i + 'i'}></Baz>
<Foo k={i + 'j'}></Foo>
</Bar>
);
})}
</>
);
}
function Bar({x, children}) {
return (
<>
{x}
{children}
</>
);
}
function Baz({i}) {
return i;
}
function Foo({k}) {
return k;
}
function useX() {
return 'x';
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{arr: ['foo', 'bar']}],
};
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @enableJsxOutlining
function Component(t0) {
const $ = _c(7);
const { arr } = t0;
const x = useX();
let t1;
if ($[0] !== arr || $[1] !== x) {
let t2;
if ($[3] !== x) {
t2 = (i, id) => {
const T0 = _temp;
return <T0 i={i + "i"} k={i + "j"} key={id} x={x} />;
};
$[3] = x;
$[4] = t2;
} else {
t2 = $[4];
}
t1 = arr.map(t2);
$[0] = arr;
$[1] = x;
$[2] = t1;
} else {
t1 = $[2];
}
let t2;
if ($[5] !== t1) {
t2 = <>{t1}</>;
$[5] = t1;
$[6] = t2;
} else {
t2 = $[6];
}
return t2;
}
function _temp(t0) {
const $ = _c(8);
const { i: i, k: k, x: x } = t0;
let t1;
if ($[0] !== i) {
t1 = <Baz i={i} />;
$[0] = i;
$[1] = t1;
} else {
t1 = $[1];
}
let t2;
if ($[2] !== k) {
t2 = <Foo k={k} />;
$[2] = k;
$[3] = t2;
} else {
t2 = $[3];
}
let t3;
if ($[4] !== t1 || $[5] !== t2 || $[6] !== x) {
t3 = (
<Bar x={x}>
{t1}
{t2}
</Bar>
);
$[4] = t1;
$[5] = t2;
$[6] = x;
$[7] = t3;
} else {
t3 = $[7];
}
return t3;
}
function Bar(t0) {
const $ = _c(3);
const { x, children } = t0;
let t1;
if ($[0] !== children || $[1] !== x) {
t1 = (
<>
{x}
{children}
</>
);
$[0] = children;
$[1] = x;
$[2] = t1;
} else {
t1 = $[2];
}
return t1;
}
function Baz(t0) {
const { i } = t0;
return i;
}
function Foo(t0) {
const { k } = t0;
return k;
}
function useX() {
return "x";
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ arr: ["foo", "bar"] }],
};
```
### Eval output
(kind: ok) xfooifoojxbaribarj
@@ -0,0 +1,41 @@
// @enableJsxOutlining
function Component({arr}) {
const x = useX();
return (
<>
{arr.map((i, id) => {
return (
<Bar key={id} x={x}>
<Baz i={i + 'i'}></Baz>
<Foo k={i + 'j'}></Foo>
</Bar>
);
})}
</>
);
}
function Bar({x, children}) {
return (
<>
{x}
{children}
</>
);
}
function Baz({i}) {
return i;
}
function Foo({k}) {
return k;
}
function useX() {
return 'x';
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{arr: ['foo', 'bar']}],
};
@@ -0,0 +1,177 @@
## Input
```javascript
// @enableJsxOutlining
function Component({arr}) {
const x = useX();
return (
<>
{arr.map((i, id) => {
return (
<Bar key={id} x={x}>
<Foo k={i + 'i'}></Foo>
<Foo k={i + 'j'}></Foo>
<Baz k1={i + 'j'}></Baz>
</Bar>
);
})}
</>
);
}
function Bar({x, children}) {
return (
<>
{x}
{children}
</>
);
}
function Baz({k1}) {
return k1;
}
function Foo({k}) {
return k;
}
function useX() {
return 'x';
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{arr: ['foo', 'bar']}],
};
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @enableJsxOutlining
function Component(t0) {
const $ = _c(7);
const { arr } = t0;
const x = useX();
let t1;
if ($[0] !== arr || $[1] !== x) {
let t2;
if ($[3] !== x) {
t2 = (i, id) => {
const T0 = _temp;
return <T0 k={i + "i"} k1={i + "j"} k12={i + "j"} key={id} x={x} />;
};
$[3] = x;
$[4] = t2;
} else {
t2 = $[4];
}
t1 = arr.map(t2);
$[0] = arr;
$[1] = x;
$[2] = t1;
} else {
t1 = $[2];
}
let t2;
if ($[5] !== t1) {
t2 = <>{t1}</>;
$[5] = t1;
$[6] = t2;
} else {
t2 = $[6];
}
return t2;
}
function _temp(t0) {
const $ = _c(11);
const { k: k, k1: k1, k12: k12, x: x } = t0;
let t1;
if ($[0] !== k) {
t1 = <Foo k={k} />;
$[0] = k;
$[1] = t1;
} else {
t1 = $[1];
}
let t2;
if ($[2] !== k1) {
t2 = <Foo k={k1} />;
$[2] = k1;
$[3] = t2;
} else {
t2 = $[3];
}
let t3;
if ($[4] !== k12) {
t3 = <Baz k1={k12} />;
$[4] = k12;
$[5] = t3;
} else {
t3 = $[5];
}
let t4;
if ($[6] !== t1 || $[7] !== t2 || $[8] !== t3 || $[9] !== x) {
t4 = (
<Bar x={x}>
{t1}
{t2}
{t3}
</Bar>
);
$[6] = t1;
$[7] = t2;
$[8] = t3;
$[9] = x;
$[10] = t4;
} else {
t4 = $[10];
}
return t4;
}
function Bar(t0) {
const $ = _c(3);
const { x, children } = t0;
let t1;
if ($[0] !== children || $[1] !== x) {
t1 = (
<>
{x}
{children}
</>
);
$[0] = children;
$[1] = x;
$[2] = t1;
} else {
t1 = $[2];
}
return t1;
}
function Baz(t0) {
const { k1 } = t0;
return k1;
}
function Foo(t0) {
const { k } = t0;
return k;
}
function useX() {
return "x";
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ arr: ["foo", "bar"] }],
};
```
### Eval output
(kind: ok) xfooifoojfoojxbaribarjbarj
@@ -0,0 +1,42 @@
// @enableJsxOutlining
function Component({arr}) {
const x = useX();
return (
<>
{arr.map((i, id) => {
return (
<Bar key={id} x={x}>
<Foo k={i + 'i'}></Foo>
<Foo k={i + 'j'}></Foo>
<Baz k1={i + 'j'}></Baz>
</Bar>
);
})}
</>
);
}
function Bar({x, children}) {
return (
<>
{x}
{children}
</>
);
}
function Baz({k1}) {
return k1;
}
function Foo({k}) {
return k;
}
function useX() {
return 'x';
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{arr: ['foo', 'bar']}],
};
@@ -0,0 +1,157 @@
## Input
```javascript
// @enableJsxOutlining
function Component({arr}) {
const x = useX();
return (
<>
{arr.map((i, id) => {
return (
<Bar key={id} x={x}>
<Foo k={i + 'i'}></Foo>
<Foo k={i + 'j'}></Foo>
</Bar>
);
})}
</>
);
}
function Bar({x, children}) {
return (
<>
{x}
{children}
</>
);
}
function Foo({k}) {
return k;
}
function useX() {
return 'x';
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{arr: ['foo', 'bar']}],
};
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @enableJsxOutlining
function Component(t0) {
const $ = _c(7);
const { arr } = t0;
const x = useX();
let t1;
if ($[0] !== arr || $[1] !== x) {
let t2;
if ($[3] !== x) {
t2 = (i, id) => {
const T0 = _temp;
return <T0 k={i + "i"} k1={i + "j"} key={id} x={x} />;
};
$[3] = x;
$[4] = t2;
} else {
t2 = $[4];
}
t1 = arr.map(t2);
$[0] = arr;
$[1] = x;
$[2] = t1;
} else {
t1 = $[2];
}
let t2;
if ($[5] !== t1) {
t2 = <>{t1}</>;
$[5] = t1;
$[6] = t2;
} else {
t2 = $[6];
}
return t2;
}
function _temp(t0) {
const $ = _c(8);
const { k: k, k1: k1, x: x } = t0;
let t1;
if ($[0] !== k) {
t1 = <Foo k={k} />;
$[0] = k;
$[1] = t1;
} else {
t1 = $[1];
}
let t2;
if ($[2] !== k1) {
t2 = <Foo k={k1} />;
$[2] = k1;
$[3] = t2;
} else {
t2 = $[3];
}
let t3;
if ($[4] !== t1 || $[5] !== t2 || $[6] !== x) {
t3 = (
<Bar x={x}>
{t1}
{t2}
</Bar>
);
$[4] = t1;
$[5] = t2;
$[6] = x;
$[7] = t3;
} else {
t3 = $[7];
}
return t3;
}
function Bar(t0) {
const $ = _c(3);
const { x, children } = t0;
let t1;
if ($[0] !== children || $[1] !== x) {
t1 = (
<>
{x}
{children}
</>
);
$[0] = children;
$[1] = x;
$[2] = t1;
} else {
t1 = $[2];
}
return t1;
}
function Foo(t0) {
const { k } = t0;
return k;
}
function useX() {
return "x";
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ arr: ["foo", "bar"] }],
};
```
### Eval output
(kind: ok) xfooifoojxbaribarj
@@ -0,0 +1,37 @@
// @enableJsxOutlining
function Component({arr}) {
const x = useX();
return (
<>
{arr.map((i, id) => {
return (
<Bar key={id} x={x}>
<Foo k={i + 'i'}></Foo>
<Foo k={i + 'j'}></Foo>
</Bar>
);
})}
</>
);
}
function Bar({x, children}) {
return (
<>
{x}
{children}
</>
);
}
function Foo({k}) {
return k;
}
function useX() {
return 'x';
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{arr: ['foo', 'bar']}],
};
@@ -31,8 +31,8 @@ function Baz({i}) {
return i;
}
function Foo({k}) {
return k;
function Foo({i}) {
return i;
}
function useX() {
@@ -58,12 +58,10 @@ function Component(t0) {
if ($[0] !== arr || $[1] !== x) {
let t2;
if ($[3] !== x) {
t2 = (i, id) => (
<Bar key={id} x={x}>
<Baz i={i} />
<Foo i={i} />
</Bar>
);
t2 = (i, id) => {
const T0 = _temp;
return <T0 i={i} i1={i} key={id} x={x} />;
};
$[3] = x;
$[4] = t2;
} else {
@@ -86,6 +84,42 @@ function Component(t0) {
}
return t2;
}
function _temp(t0) {
const $ = _c(8);
const { i: i, i1: i1, x: x } = t0;
let t1;
if ($[0] !== i) {
t1 = <Baz i={i} />;
$[0] = i;
$[1] = t1;
} else {
t1 = $[1];
}
let t2;
if ($[2] !== i1) {
t2 = <Foo i={i1} />;
$[2] = i1;
$[3] = t2;
} else {
t2 = $[3];
}
let t3;
if ($[4] !== t1 || $[5] !== t2 || $[6] !== x) {
t3 = (
<Bar x={x}>
{t1}
{t2}
</Bar>
);
$[4] = t1;
$[5] = t2;
$[6] = x;
$[7] = t3;
} else {
t3 = $[7];
}
return t3;
}
function Bar(t0) {
const $ = _c(3);
@@ -113,8 +147,8 @@ function Baz(t0) {
}
function Foo(t0) {
const { k } = t0;
return k;
const { i } = t0;
return i;
}
function useX() {
@@ -129,4 +163,4 @@ export const FIXTURE_ENTRYPOINT = {
```
### Eval output
(kind: ok) xfooxbar
(kind: ok) xfoofooxbarbar
@@ -27,8 +27,8 @@ function Baz({i}) {
return i;
}
function Foo({k}) {
return k;
function Foo({i}) {
return i;
}
function useX() {