@samitouri / QOS-React-2 / commits / b687fd27b5

compiler: Use types to decide which scopes are eligible for merging

In MergeReactiveScopesThatInvalidateTogether when deciding which scopes were eligible for mergin at all, we looked specifically at the instructions whose lvalue produces the declaration. So if a scope declaration was `t0`, we'd love for the instruction where `t0` was the lvalue and look at the instruction type to decide if it is eligible for merging. Here, we use the inferred type instead (now that the inferred types support the same set of types of instructions we looked at before). This allows us to find more cases where scopes can be merged. ghstack-source-id: 0e3e05f24ea0ac6e3c43046bc3e114f906747a04 Pull Request resolved: https://github.com/facebook/react/pull/29157

Joe Savona committed May 23, 2024 at 01:09 UTC b687fd27b524f7922b67b8a6b01dc80fc8d8a233
28 files changed +744 -489
compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/MergeReactiveScopesThatInvalidateTogether.ts
+3 -61
@@ -13,7 +13,6 @@ import {
13 Place,
14 ReactiveBlock,
15 ReactiveFunction,
16 - ReactiveInstruction,
16 ReactiveScope,
17 ReactiveScopeBlock,
18 ReactiveScopeDependencies,
@@ -515,64 +514,7 @@ function scopeIsEligibleForMerging(scopeBlock: ReactiveScopeBlock): boolean {
514 */
515 return true;
516 }
518 - const visitor = new DeclarationTypeVisitor(scopeBlock.scope);
519 - visitor.visitScope(scopeBlock, undefined);
520 - return visitor.alwaysInvalidatesOnInputChange;
521 -}
522 -
523 -class DeclarationTypeVisitor extends ReactiveFunctionVisitor<void> {
524 - scope: ReactiveScope;
525 - alwaysInvalidatesOnInputChange: boolean = false;
526 -
527 - constructor(scope: ReactiveScope) {
528 - super();
529 - this.scope = scope;
530 - }
531 -
532 - override visitScope(scopeBlock: ReactiveScopeBlock, state: void): void {
533 - if (scopeBlock.scope.id !== this.scope.id) {
534 - return;
535 - }
536 - this.traverseScope(scopeBlock, state);
537 - }
538 -
539 - override visitInstruction(
540 - instruction: ReactiveInstruction,
541 - state: void
542 - ): void {
543 - this.traverseInstruction(instruction, state);
544 - if (
545 - instruction.lvalue === null ||
546 - !this.scope.declarations.has(instruction.lvalue.identifier.id)
547 - ) {
548 - /*
549 - * no lvalue or this instruction isn't directly constructing a
550 - * scope output value, skip
551 - */
552 - log(
553 - ` skip instruction lvalue=${
554 - instruction.lvalue?.identifier.id
555 - } declaration?=${
556 - instruction.lvalue != null &&
557 - this.scope.declarations.has(instruction.lvalue.identifier.id)
558 - } scope=${printReactiveScopeSummary(this.scope)}`
559 - );
560 - return;
561 - }
562 - switch (instruction.value.kind) {
563 - case "FunctionExpression":
564 - case "ArrayExpression":
565 - case "JsxExpression":
566 - case "JsxFragment":
567 - case "ObjectExpression": {
568 - /*
569 - * These instruction types *always* allocate. If they execute
570 - * they will produce a new value, triggering downstream reactive
571 - * updates
572 - */
573 - this.alwaysInvalidatesOnInputChange = true;
574 - break;
575 - }
576 - }
577 - }
517 + return [...scopeBlock.scope.declarations].some(([, decl]) =>
518 + isAlwaysInvalidatingType(decl.identifier.type)
519 + );
520 }
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allocating-primitive-as-dep-nested-scope.expect.md
+59 -29
@@ -5,15 +5,33 @@
5 // bar(props.b) is an allocating expression that produces a primitive, which means
6 // that Forget should memoize it.
7 // Correctness:
8 +
9 +import { identity, mutate, setProperty } from "shared-runtime";
10 +
11 // - y depends on either bar(props.b) or bar(props.b) + 1
12 function AllocatingPrimitiveAsDepNested(props) {
13 let x = {};
14 mutate(x);
12 - let y = foo(bar(props.b) + 1);
13 - mutate(x, props.a);
15 + let y = identity(identity(props.b) + 1);
16 + setProperty(x, props.a);
17 return [x, y];
18 }
19
20 +export const FIXTURE_ENTRYPOINT = {
21 + fn: AllocatingPrimitiveAsDepNested,
22 + params: [{ a: 1, b: 2 }],
23 + sequentialRenders: [
24 + // change b
25 + { a: 1, b: 3 },
26 + // change b
27 + { a: 1, b: 4 },
28 + // change a
29 + { a: 2, b: 4 },
30 + // change a
31 + { a: 3, b: 4 },
32 + ],
33 +};
34 +
35 ```
36
37 ## Code
@@ -22,44 +40,56 @@ function AllocatingPrimitiveAsDepNested(props) {
40 import { c as _c } from "react/compiler-runtime"; // bar(props.b) is an allocating expression that produces a primitive, which means
41 // that Forget should memoize it.
42 // Correctness:
43 +
44 +import { identity, mutate, setProperty } from "shared-runtime";
45 +
46 // - y depends on either bar(props.b) or bar(props.b) + 1
47 function AllocatingPrimitiveAsDepNested(props) {
27 - const $ = _c(9);
28 - let x;
29 - let y;
48 + const $ = _c(5);
49 + let t0;
50 if ($[0] !== props.b || $[1] !== props.a) {
31 - x = {};
51 + const x = {};
52 mutate(x);
33 - const t0 = bar(props.b) + 1;
34 - let t1;
35 - if ($[4] !== t0) {
36 - t1 = foo(t0);
37 - $[4] = t0;
38 - $[5] = t1;
53 + const t1 = identity(props.b) + 1;
54 + let t2;
55 + if ($[3] !== t1) {
56 + t2 = identity(t1);
57 + $[3] = t1;
58 + $[4] = t2;
59 } else {
40 - t1 = $[5];
60 + t2 = $[4];
61 }
42 - y = t1;
43 - mutate(x, props.a);
62 + const y = t2;
63 + setProperty(x, props.a);
64 + t0 = [x, y];
65 $[0] = props.b;
66 $[1] = props.a;
46 - $[2] = x;
47 - $[3] = y;
48 - } else {
49 - x = $[2];
50 - y = $[3];
51 - }
52 - let t0;
53 - if ($[6] !== x || $[7] !== y) {
54 - t0 = [x, y];
55 - $[6] = x;
56 - $[7] = y;
57 - $[8] = t0;
67 + $[2] = t0;
68 } else {
59 - t0 = $[8];
69 + t0 = $[2];
70 }
71 return t0;
72 }
73
74 +export const FIXTURE_ENTRYPOINT = {
75 + fn: AllocatingPrimitiveAsDepNested,
76 + params: [{ a: 1, b: 2 }],
77 + sequentialRenders: [
78 + // change b
79 + { a: 1, b: 3 },
80 + // change b
81 + { a: 1, b: 4 },
82 + // change a
83 + { a: 2, b: 4 },
84 + // change a
85 + { a: 3, b: 4 },
86 + ],
87 +};
88 +
89 ```
65 -
\ No newline at end of file
90 +
91 +### Eval output
92 +(kind: ok) [{"wat0":"joe","wat1":1},4]
93 +[{"wat0":"joe","wat1":1},5]
94 +[{"wat0":"joe","wat1":2},5]
95 +[{"wat0":"joe","wat1":3},5]
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allocating-primitive-as-dep-nested-scope.js
+20 -2
@@ -1,11 +1,29 @@
1 // bar(props.b) is an allocating expression that produces a primitive, which means
2 // that Forget should memoize it.
3 // Correctness:
4 +
5 +import { identity, mutate, setProperty } from "shared-runtime";
6 +
7 // - y depends on either bar(props.b) or bar(props.b) + 1
8 function AllocatingPrimitiveAsDepNested(props) {
9 let x = {};
10 mutate(x);
8 - let y = foo(bar(props.b) + 1);
9 - mutate(x, props.a);
11 + let y = identity(identity(props.b) + 1);
12 + setProperty(x, props.a);
13 return [x, y];
14 }
15 +
16 +export const FIXTURE_ENTRYPOINT = {
17 + fn: AllocatingPrimitiveAsDepNested,
18 + params: [{ a: 1, b: 2 }],
19 + sequentialRenders: [
20 + // change b
21 + { a: 1, b: 3 },
22 + // change b
23 + { a: 1, b: 4 },
24 + // change a
25 + { a: 2, b: 4 },
26 + // change a
27 + { a: 3, b: 4 },
28 + ],
29 +};
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-access-assignment.expect.md
+41 -35
@@ -2,7 +2,7 @@
2 ## Input
3
4 ```javascript
5 -function foo(a, b, c) {
5 +function Component({ a, b, c }) {
6 const x = [a];
7 const y = [null, b];
8 const z = [[], [], [c]];
@@ -12,9 +12,15 @@ function foo(a, b, c) {
12 }
13
14 export const FIXTURE_ENTRYPOINT = {
15 - fn: foo,
16 - params: [1, 2, 3],
17 - isComponent: false,
15 + fn: Component,
16 + params: [{ a: 1, b: 20, c: 300 }],
17 + sequentialRenders: [
18 + { a: 2, b: 20, c: 300 },
19 + { a: 3, b: 20, c: 300 },
20 + { a: 3, b: 21, c: 300 },
21 + { a: 3, b: 22, c: 300 },
22 + { a: 3, b: 22, c: 301 },
23 + ],
24 };
25
26 ```
@@ -23,52 +29,52 @@ export const FIXTURE_ENTRYPOINT = {
29
30 ```javascript
31 import { c as _c } from "react/compiler-runtime";
26 -function foo(a, b, c) {
27 - const $ = _c(10);
28 - let x;
29 - let z;
32 +function Component(t0) {
33 + const $ = _c(6);
34 + const { a, b, c } = t0;
35 + let t1;
36 if ($[0] !== a || $[1] !== b || $[2] !== c) {
31 - x = [a];
32 - let t0;
33 - if ($[5] !== b) {
34 - t0 = [null, b];
35 - $[5] = b;
36 - $[6] = t0;
37 + const x = [a];
38 + let t2;
39 + if ($[4] !== b) {
40 + t2 = [null, b];
41 + $[4] = b;
42 + $[5] = t2;
43 } else {
38 - t0 = $[6];
44 + t2 = $[5];
45 }
40 - const y = t0;
41 - z = [[], [], [c]];
46 + const y = t2;
47 + const z = [[], [], [c]];
48 x[0] = y[1];
49 z[0][0] = x[0];
50 + t1 = [x, z];
51 $[0] = a;
52 $[1] = b;
53 $[2] = c;
47 - $[3] = x;
48 - $[4] = z;
54 + $[3] = t1;
55 } else {
50 - x = $[3];
51 - z = $[4];
56 + t1 = $[3];
57 }
53 - let t0;
54 - if ($[7] !== x || $[8] !== z) {
55 - t0 = [x, z];
56 - $[7] = x;
57 - $[8] = z;
58 - $[9] = t0;
59 - } else {
60 - t0 = $[9];
61 - }
62 - return t0;
58 + return t1;
59 }
60
61 export const FIXTURE_ENTRYPOINT = {
66 - fn: foo,
67 - params: [1, 2, 3],
68 - isComponent: false,
62 + fn: Component,
63 + params: [{ a: 1, b: 20, c: 300 }],
64 + sequentialRenders: [
65 + { a: 2, b: 20, c: 300 },
66 + { a: 3, b: 20, c: 300 },
67 + { a: 3, b: 21, c: 300 },
68 + { a: 3, b: 22, c: 300 },
69 + { a: 3, b: 22, c: 301 },
70 + ],
71 };
72
73 ```
74
75 ### Eval output
74 -(kind: ok) [[2],[[2],[],[3]]]
\ No newline at end of file
76 +(kind: ok) [[20],[[20],[],[300]]]
77 +[[20],[[20],[],[300]]]
78 +[[21],[[21],[],[300]]]
79 +[[22],[[22],[],[300]]]
80 +[[22],[[22],[],[301]]]
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-access-assignment.js
+10 -4
@@ -1,4 +1,4 @@
1 -function foo(a, b, c) {
1 +function Component({ a, b, c }) {
2 const x = [a];
3 const y = [null, b];
4 const z = [[], [], [c]];
@@ -8,7 +8,13 @@ function foo(a, b, c) {
8 }
9
10 export const FIXTURE_ENTRYPOINT = {
11 - fn: foo,
12 - params: [1, 2, 3],
13 - isComponent: false,
11 + fn: Component,
12 + params: [{ a: 1, b: 20, c: 300 }],
13 + sequentialRenders: [
14 + { a: 2, b: 20, c: 300 },
15 + { a: 3, b: 20, c: 300 },
16 + { a: 3, b: 21, c: 300 },
17 + { a: 3, b: 22, c: 300 },
18 + { a: 3, b: 22, c: 301 },
19 + ],
20 };
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-emit-make-read-only.expect.md
+8 -14
@@ -23,26 +23,20 @@ import { makeReadOnly } from "react-compiler-runtime";
23 import { c as _c } from "react/compiler-runtime"; // @enableEmitFreeze true
24
25 function MyComponentName(props) {
26 - const $ = _c(5);
27 - let x;
26 + const $ = _c(3);
27 + let y;
28 if ($[0] !== props.a || $[1] !== props.b) {
29 - x = {};
29 + const x = {};
30 foo(x, props.a);
31 foo(x, props.b);
32 - $[0] = props.a;
33 - $[1] = props.b;
34 - $[2] = __DEV__ ? makeReadOnly(x, "MyComponentName") : x;
35 - } else {
36 - x = $[2];
37 - }
38 - let y;
39 - if ($[3] !== x) {
32 +
33 y = [];
34 y.push(x);
42 - $[3] = x;
43 - $[4] = __DEV__ ? makeReadOnly(y, "MyComponentName") : y;
35 + $[0] = props.a;
36 + $[1] = props.b;
37 + $[2] = __DEV__ ? makeReadOnly(y, "MyComponentName") : y;
38 } else {
45 - y = $[4];
39 + y = $[2];
40 }
41 return y;
42 }
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/dont-merge-if-dep-is-inner-declaration-of-previous-scope.expect.md new
+162
@@ -0,0 +1,162 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import { ValidateMemoization } from "shared-runtime";
6 +
7 +// Achieving Forget's level of memoization precision in this example isn't possible with useMemo
8 +// without significantly altering the code, so disable the non-Forget evaluation of this fixture.
9 +// @disableNonForgetInSprout
10 +function Component({ a, b, c }) {
11 + const x = [];
12 + let y;
13 + if (a) {
14 + y = [b];
15 + }
16 + x.push(c);
17 +
18 + // this scope should not merge with the above scope because y does not invalidate
19 + // on changes to `c`
20 + const z = [y];
21 +
22 + // return [x, z];
23 + return (
24 + <>
25 + <ValidateMemoization inputs={[a, b, c]} output={x} />
26 + <ValidateMemoization inputs={[a, b]} output={z} />
27 + </>
28 + );
29 +}
30 +
31 +export const FIXTURE_ENTRYPOINT = {
32 + fn: Component,
33 + params: [{ a: false, b: null, c: 0 }],
34 + sequentialRenders: [
35 + { a: false, b: null, c: 0 },
36 + { a: false, b: null, c: 1 },
37 + { a: true, b: 0, c: 1 },
38 + { a: true, b: 1, c: 1 },
39 + ],
40 +};
41 +
42 +```
43 +
44 +## Code
45 +
46 +```javascript
47 +import { c as _c } from "react/compiler-runtime";
48 +import { ValidateMemoization } from "shared-runtime";
49 +
50 +// Achieving Forget's level of memoization precision in this example isn't possible with useMemo
51 +// without significantly altering the code, so disable the non-Forget evaluation of this fixture.
52 +// @disableNonForgetInSprout
53 +function Component(t0) {
54 + const $ = _c(25);
55 + const { a, b, c } = t0;
56 + let y;
57 + let x;
58 + if ($[0] !== a || $[1] !== b || $[2] !== c) {
59 + x = [];
60 + if (a) {
61 + let t1;
62 + if ($[5] !== b) {
63 + t1 = [b];
64 + $[5] = b;
65 + $[6] = t1;
66 + } else {
67 + t1 = $[6];
68 + }
69 + y = t1;
70 + }
71 +
72 + x.push(c);
73 + $[0] = a;
74 + $[1] = b;
75 + $[2] = c;
76 + $[3] = y;
77 + $[4] = x;
78 + } else {
79 + y = $[3];
80 + x = $[4];
81 + }
82 + let t1;
83 + if ($[7] !== y) {
84 + t1 = [y];
85 + $[7] = y;
86 + $[8] = t1;
87 + } else {
88 + t1 = $[8];
89 + }
90 + const z = t1;
91 + let t2;
92 + if ($[9] !== a || $[10] !== b || $[11] !== c) {
93 + t2 = [a, b, c];
94 + $[9] = a;
95 + $[10] = b;
96 + $[11] = c;
97 + $[12] = t2;
98 + } else {
99 + t2 = $[12];
100 + }
101 + let t3;
102 + if ($[13] !== t2 || $[14] !== x) {
103 + t3 = <ValidateMemoization inputs={t2} output={x} />;
104 + $[13] = t2;
105 + $[14] = x;
106 + $[15] = t3;
107 + } else {
108 + t3 = $[15];
109 + }
110 + let t4;
111 + if ($[16] !== a || $[17] !== b) {
112 + t4 = [a, b];
113 + $[16] = a;
114 + $[17] = b;
115 + $[18] = t4;
116 + } else {
117 + t4 = $[18];
118 + }
119 + let t5;
120 + if ($[19] !== t4 || $[20] !== z) {
121 + t5 = <ValidateMemoization inputs={t4} output={z} />;
122 + $[19] = t4;
123 + $[20] = z;
124 + $[21] = t5;
125 + } else {
126 + t5 = $[21];
127 + }
128 + let t6;
129 + if ($[22] !== t3 || $[23] !== t5) {
130 + t6 = (
131 + <>
132 + {t3}
133 + {t5}
134 + </>
135 + );
136 + $[22] = t3;
137 + $[23] = t5;
138 + $[24] = t6;
139 + } else {
140 + t6 = $[24];
141 + }
142 + return t6;
143 +}
144 +
145 +export const FIXTURE_ENTRYPOINT = {
146 + fn: Component,
147 + params: [{ a: false, b: null, c: 0 }],
148 + sequentialRenders: [
149 + { a: false, b: null, c: 0 },
150 + { a: false, b: null, c: 1 },
151 + { a: true, b: 0, c: 1 },
152 + { a: true, b: 1, c: 1 },
153 + ],
154 +};
155 +
156 +```
157 +
158 +### Eval output
159 +(kind: ok) <div>{"inputs":[false,null,0],"output":[0]}</div><div>{"inputs":[false,null],"output":[null]}</div>
160 +<div>{"inputs":[false,null,1],"output":[1]}</div><div>{"inputs":[false,null],"output":[null]}</div>
161 +<div>{"inputs":[true,0,1],"output":[1]}</div><div>{"inputs":[true,0],"output":[[0]]}</div>
162 +<div>{"inputs":[true,1,1],"output":[1]}</div><div>{"inputs":[true,1],"output":[[1]]}</div>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/dont-merge-if-dep-is-inner-declaration-of-previous-scope.js new
+36
@@ -0,0 +1,36 @@
1 +import { ValidateMemoization } from "shared-runtime";
2 +
3 +// Achieving Forget's level of memoization precision in this example isn't possible with useMemo
4 +// without significantly altering the code, so disable the non-Forget evaluation of this fixture.
5 +// @disableNonForgetInSprout
6 +function Component({ a, b, c }) {
7 + const x = [];
8 + let y;
9 + if (a) {
10 + y = [b];
11 + }
12 + x.push(c);
13 +
14 + // this scope should not merge with the above scope because y does not invalidate
15 + // on changes to `c`
16 + const z = [y];
17 +
18 + // return [x, z];
19 + return (
20 + <>
21 + <ValidateMemoization inputs={[a, b, c]} output={x} />
22 + <ValidateMemoization inputs={[a, b]} output={z} />
23 + </>
24 + );
25 +}
26 +
27 +export const FIXTURE_ENTRYPOINT = {
28 + fn: Component,
29 + params: [{ a: false, b: null, c: 0 }],
30 + sequentialRenders: [
31 + { a: false, b: null, c: 0 },
32 + { a: false, b: null, c: 1 },
33 + { a: true, b: 0, c: 1 },
34 + { a: true, b: 1, c: 1 },
35 + ],
36 +};
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/for-in-statement.expect.md
+17 -14
@@ -13,6 +13,10 @@ function Component(props) {
13 export const FIXTURE_ENTRYPOINT = {
14 fn: Component,
15 params: [{ hello: null, world: undefined, "!": true }],
16 + sequentialRenders: [
17 + { a: null, b: null, c: null },
18 + { lauren: true, mofei: true, sathya: true, jason: true },
19 + ],
20 };
21
22 ```
@@ -22,25 +26,19 @@ export const FIXTURE_ENTRYPOINT = {
26 ```javascript
27 import { c as _c } from "react/compiler-runtime";
28 function Component(props) {
25 - const $ = _c(4);
26 - let items;
29 + const $ = _c(2);
30 + let t0;
31 if ($[0] !== props) {
28 - items = [];
32 + const items = [];
33 for (const key in props) {
34 items.push(<div key={key}>{key}</div>);
35 }
32 - $[0] = props;
33 - $[1] = items;
34 - } else {
35 - items = $[1];
36 - }
37 - let t0;
38 - if ($[2] !== items) {
36 +
37 t0 = <div>{items}</div>;
40 - $[2] = items;
41 - $[3] = t0;
38 + $[0] = props;
39 + $[1] = t0;
40 } else {
43 - t0 = $[3];
41 + t0 = $[1];
42 }
43 return t0;
44 }
@@ -48,9 +46,14 @@ function Component(props) {
46 export const FIXTURE_ENTRYPOINT = {
47 fn: Component,
48 params: [{ hello: null, world: undefined, "!": true }],
49 + sequentialRenders: [
50 + { a: null, b: null, c: null },
51 + { lauren: true, mofei: true, sathya: true, jason: true },
52 + ],
53 };
54
55 ```
56
57 ### Eval output
56 -(kind: ok) <div><div>hello</div><div>world</div><div>!</div></div>
\ No newline at end of file
58 +(kind: ok) <div><div>a</div><div>b</div><div>c</div></div>
59 +<div><div>lauren</div><div>mofei</div><div>sathya</div><div>jason</div></div>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/for-in-statement.js
+4
@@ -9,4 +9,8 @@ function Component(props) {
9 export const FIXTURE_ENTRYPOINT = {
10 fn: Component,
11 params: [{ hello: null, world: undefined, "!": true }],
12 + sequentialRenders: [
13 + { a: null, b: null, c: null },
14 + { lauren: true, mofei: true, sathya: true, jason: true },
15 + ],
16 };
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/for-loop-with-value-block-initializer.expect.md
+76 -14
@@ -23,6 +23,38 @@ export const FIXTURE_ENTRYPOINT = {
23 ],
24 },
25 ],
26 + sequentialRenders: [
27 + {
28 + start: 1,
29 + items: [
30 + { id: 0, value: "zero" },
31 + { id: 1, value: "one" },
32 + ],
33 + },
34 + {
35 + start: 2,
36 + items: [
37 + { id: 0, value: "zero" },
38 + { id: 1, value: "one" },
39 + ],
40 + },
41 + {
42 + start: 0,
43 + items: [
44 + { id: 0, value: "zero" },
45 + { id: 1, value: "one" },
46 + { id: 2, value: "two" },
47 + ],
48 + },
49 + {
50 + start: 1,
51 + items: [
52 + { id: 0, value: "zero" },
53 + { id: 1, value: "one" },
54 + { id: 2, value: "two" },
55 + ],
56 + },
57 + ],
58 };
59
60 ```
@@ -33,27 +65,21 @@ export const FIXTURE_ENTRYPOINT = {
65 import { c as _c } from "react/compiler-runtime";
66 const TOTAL = 10;
67 function Component(props) {
36 - const $ = _c(5);
37 - let items;
68 + const $ = _c(3);
69 + let t0;
70 if ($[0] !== props.start || $[1] !== props.items) {
39 - items = [];
71 + const items = [];
72 for (let i = props.start ?? 0; i < props.items.length; i++) {
73 const item = props.items[i];
74 items.push(<div key={item.id}>{item.value}</div>);
75 }
76 +
77 + t0 = <div>{items}</div>;
78 $[0] = props.start;
79 $[1] = props.items;
46 - $[2] = items;
47 - } else {
48 - items = $[2];
49 - }
50 - let t0;
51 - if ($[3] !== items) {
52 - t0 = <div>{items}</div>;
53 - $[3] = items;
54 - $[4] = t0;
80 + $[2] = t0;
81 } else {
56 - t0 = $[4];
82 + t0 = $[2];
83 }
84 return t0;
85 }
@@ -69,9 +95,45 @@ export const FIXTURE_ENTRYPOINT = {
95 ],
96 },
97 ],
98 +
99 + sequentialRenders: [
100 + {
101 + start: 1,
102 + items: [
103 + { id: 0, value: "zero" },
104 + { id: 1, value: "one" },
105 + ],
106 + },
107 + {
108 + start: 2,
109 + items: [
110 + { id: 0, value: "zero" },
111 + { id: 1, value: "one" },
112 + ],
113 + },
114 + {
115 + start: 0,
116 + items: [
117 + { id: 0, value: "zero" },
118 + { id: 1, value: "one" },
119 + { id: 2, value: "two" },
120 + ],
121 + },
122 + {
123 + start: 1,
124 + items: [
125 + { id: 0, value: "zero" },
126 + { id: 1, value: "one" },
127 + { id: 2, value: "two" },
128 + ],
129 + },
130 + ],
131 };
132
133 ```
134
135 ### Eval output
77 -(kind: ok) <div><div>zero</div><div>one</div></div>
\ No newline at end of file
136 +(kind: ok) <div><div>one</div></div>
137 +<div></div>
138 +<div><div>zero</div><div>one</div><div>two</div></div>
139 +<div><div>one</div><div>two</div></div>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/for-loop-with-value-block-initializer.js
+32
@@ -19,4 +19,36 @@ export const FIXTURE_ENTRYPOINT = {
19 ],
20 },
21 ],
22 + sequentialRenders: [
23 + {
24 + start: 1,
25 + items: [
26 + { id: 0, value: "zero" },
27 + { id: 1, value: "one" },
28 + ],
29 + },
30 + {
31 + start: 2,
32 + items: [
33 + { id: 0, value: "zero" },
34 + { id: 1, value: "one" },
35 + ],
36 + },
37 + {
38 + start: 0,
39 + items: [
40 + { id: 0, value: "zero" },
41 + { id: 1, value: "one" },
42 + { id: 2, value: "two" },
43 + ],
44 + },
45 + {
46 + start: 1,
47 + items: [
48 + { id: 0, value: "zero" },
49 + { id: 1, value: "one" },
50 + { id: 2, value: "two" },
51 + ],
52 + },
53 + ],
54 };
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-type-inference-array-push.expect.md
+21 -18
@@ -22,6 +22,11 @@ function Component(props) {
22 export const FIXTURE_ENTRYPOINT = {
23 fn: Component,
24 params: [{ cond: true, value: 42 }],
25 + sequentialRenders: [
26 + { cond: true, value: 3.14 },
27 + { cond: false, value: 3.14 },
28 + { cond: true, value: 42 },
29 + ],
30 };
31
32 ```
@@ -31,11 +36,11 @@ export const FIXTURE_ENTRYPOINT = {
36 ```javascript
37 import { c as _c } from "react/compiler-runtime";
38 function Component(props) {
34 - const $ = _c(6);
35 - let x;
36 - let y;
39 + const $ = _c(2);
40 + let t0;
41 if ($[0] !== props) {
38 - x = {};
42 + const x = {};
43 + let y;
44 if (props.cond) {
45 y = [props.value];
46 } else {
@@ -43,21 +48,12 @@ function Component(props) {
48 }
49
50 y.push(x);
46 - $[0] = props;
47 - $[1] = x;
48 - $[2] = y;
49 - } else {
50 - x = $[1];
51 - y = $[2];
52 - }
53 - let t0;
54 - if ($[3] !== x || $[4] !== y) {
51 +
52 t0 = [x, y];
56 - $[3] = x;
57 - $[4] = y;
58 - $[5] = t0;
53 + $[0] = props;
54 + $[1] = t0;
55 } else {
60 - t0 = $[5];
56 + t0 = $[1];
57 }
58 return t0;
59 }
@@ -65,9 +61,16 @@ function Component(props) {
61 export const FIXTURE_ENTRYPOINT = {
62 fn: Component,
63 params: [{ cond: true, value: 42 }],
64 + sequentialRenders: [
65 + { cond: true, value: 3.14 },
66 + { cond: false, value: 3.14 },
67 + { cond: true, value: 42 },
68 + ],
69 };
70
71 ```
72
73 ### Eval output
73 -(kind: ok) [{},[42,"[[ cyclic ref *1 ]]"]]
\ No newline at end of file
74 +(kind: ok) [{},[3.14,"[[ cyclic ref *1 ]]"]]
75 +[{},["[[ cyclic ref *1 ]]"]]
76 +[{},[42,"[[ cyclic ref *1 ]]"]]
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-type-inference-array-push.js
+5
@@ -18,4 +18,9 @@ function Component(props) {
18 export const FIXTURE_ENTRYPOINT = {
19 fn: Component,
20 params: [{ cond: true, value: 42 }],
21 + sequentialRenders: [
22 + { cond: true, value: 3.14 },
23 + { cond: false, value: 3.14 },
24 + { cond: true, value: 42 },
25 + ],
26 };
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useCallback-in-other-reactive-block.expect.md
+14 -25
@@ -37,41 +37,30 @@ import { arrayPush } from "shared-runtime";
37 // useCallback-produced values can exist in nested reactive blocks, as long
38 // as their reactive dependencies are a subset of depslist from source
39 function useFoo(minWidth, otherProp) {
40 - const $ = _c(11);
40 + const $ = _c(7);
41 const [width] = useState(1);
42 - let style;
43 - let x;
42 + let t0;
43 if ($[0] !== width || $[1] !== minWidth || $[2] !== otherProp) {
45 - x = [];
46 - let t0;
47 - if ($[5] !== minWidth || $[6] !== width) {
48 - t0 = () => ({ width: Math.max(minWidth, width) });
49 - $[5] = minWidth;
50 - $[6] = width;
51 - $[7] = t0;
44 + const x = [];
45 + let t1;
46 + if ($[4] !== minWidth || $[5] !== width) {
47 + t1 = () => ({ width: Math.max(minWidth, width) });
48 + $[4] = minWidth;
49 + $[5] = width;
50 + $[6] = t1;
51 } else {
53 - t0 = $[7];
52 + t1 = $[6];
53 }
55 - style = t0;
54 + const style = t1;
55
56 arrayPush(x, otherProp);
57 + t0 = [style, x];
58 $[0] = width;
59 $[1] = minWidth;
60 $[2] = otherProp;
61 - $[3] = style;
62 - $[4] = x;
63 - } else {
64 - style = $[3];
65 - x = $[4];
66 - }
67 - let t0;
68 - if ($[8] !== style || $[9] !== x) {
69 - t0 = [style, x];
70 - $[8] = style;
71 - $[9] = x;
72 - $[10] = t0;
61 + $[3] = t0;
62 } else {
74 - t0 = $[10];
63 + t0 = $[3];
64 }
65 return t0;
66 }
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-in-other-reactive-block.expect.md
+16 -27
@@ -37,44 +37,33 @@ import { arrayPush } from "shared-runtime";
37 // useMemo-produced values can exist in nested reactive blocks, as long
38 // as their reactive dependencies are a subset of depslist from source
39 function useFoo(minWidth, otherProp) {
40 - const $ = _c(10);
40 + const $ = _c(6);
41 const [width] = useState(1);
42 - let style;
43 - let x;
42 + let t0;
43 if ($[0] !== width || $[1] !== minWidth || $[2] !== otherProp) {
45 - x = [];
46 - let t0;
44 + const x = [];
45 + let t1;
46
48 - const t1 = Math.max(minWidth, width);
49 - let t2;
50 - if ($[5] !== t1) {
51 - t2 = { width: t1 };
52 - $[5] = t1;
53 - $[6] = t2;
47 + const t2 = Math.max(minWidth, width);
48 + let t3;
49 + if ($[4] !== t2) {
50 + t3 = { width: t2 };
51 + $[4] = t2;
52 + $[5] = t3;
53 } else {
55 - t2 = $[6];
54 + t3 = $[5];
55 }
57 - t0 = t2;
58 - style = t0;
56 + t1 = t3;
57 + const style = t1;
58
59 arrayPush(x, otherProp);
60 + t0 = [style, x];
61 $[0] = width;
62 $[1] = minWidth;
63 $[2] = otherProp;
64 - $[3] = style;
65 - $[4] = x;
66 - } else {
67 - style = $[3];
68 - x = $[4];
69 - }
70 - let t0;
71 - if ($[7] !== style || $[8] !== x) {
72 - t0 = [style, x];
73 - $[7] = style;
74 - $[8] = x;
75 - $[9] = t0;
64 + $[3] = t0;
65 } else {
77 - t0 = $[9];
66 + t0 = $[3];
67 }
68 return t0;
69 }
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/primitive-as-dep-nested-scope.expect.md
+59 -29
@@ -6,15 +6,33 @@
6 // emit it trivially and repeatedly (e.g. no need to memoize props.b + 1
7 // separately from props.b)
8 // Correctness:
9 +
10 +import { identity, mutate, setProperty } from "shared-runtime";
11 +
12 // y depends on either props.b or props.b + 1
13 function PrimitiveAsDepNested(props) {
14 let x = {};
15 mutate(x);
13 - let y = foo(props.b + 1);
14 - mutate(x, props.a);
16 + let y = identity(props.b + 1);
17 + setProperty(x, props.a);
18 return [x, y];
19 }
20
21 +export const FIXTURE_ENTRYPOINT = {
22 + fn: PrimitiveAsDepNested,
23 + params: [{ a: 1, b: 2 }],
24 + sequentialRenders: [
25 + // change b
26 + { a: 1, b: 3 },
27 + // change b
28 + { a: 1, b: 4 },
29 + // change a
30 + { a: 2, b: 4 },
31 + // change a
32 + { a: 3, b: 4 },
33 + ],
34 +};
35 +
36 ```
37
38 ## Code
@@ -24,44 +42,56 @@ import { c as _c } from "react/compiler-runtime"; // props.b + 1 is an non-alloc
42 // emit it trivially and repeatedly (e.g. no need to memoize props.b + 1
43 // separately from props.b)
44 // Correctness:
45 +
46 +import { identity, mutate, setProperty } from "shared-runtime";
47 +
48 // y depends on either props.b or props.b + 1
49 function PrimitiveAsDepNested(props) {
29 - const $ = _c(9);
30 - let x;
31 - let y;
50 + const $ = _c(5);
51 + let t0;
52 if ($[0] !== props.b || $[1] !== props.a) {
33 - x = {};
53 + const x = {};
54 mutate(x);
35 - const t0 = props.b + 1;
36 - let t1;
37 - if ($[4] !== t0) {
38 - t1 = foo(t0);
39 - $[4] = t0;
40 - $[5] = t1;
55 + const t1 = props.b + 1;
56 + let t2;
57 + if ($[3] !== t1) {
58 + t2 = identity(t1);
59 + $[3] = t1;
60 + $[4] = t2;
61 } else {
42 - t1 = $[5];
62 + t2 = $[4];
63 }
44 - y = t1;
45 - mutate(x, props.a);
64 + const y = t2;
65 + setProperty(x, props.a);
66 + t0 = [x, y];
67 $[0] = props.b;
68 $[1] = props.a;
48 - $[2] = x;
49 - $[3] = y;
50 - } else {
51 - x = $[2];
52 - y = $[3];
53 - }
54 - let t0;
55 - if ($[6] !== x || $[7] !== y) {
56 - t0 = [x, y];
57 - $[6] = x;
58 - $[7] = y;
59 - $[8] = t0;
69 + $[2] = t0;
70 } else {
61 - t0 = $[8];
71 + t0 = $[2];
72 }
73 return t0;
74 }
75
76 +export const FIXTURE_ENTRYPOINT = {
77 + fn: PrimitiveAsDepNested,
78 + params: [{ a: 1, b: 2 }],
79 + sequentialRenders: [
80 + // change b
81 + { a: 1, b: 3 },
82 + // change b
83 + { a: 1, b: 4 },
84 + // change a
85 + { a: 2, b: 4 },
86 + // change a
87 + { a: 3, b: 4 },
88 + ],
89 +};
90 +
91 ```
67 -
\ No newline at end of file
92 +
93 +### Eval output
94 +(kind: ok) [{"wat0":"joe","wat1":1},4]
95 +[{"wat0":"joe","wat1":1},5]
96 +[{"wat0":"joe","wat1":2},5]
97 +[{"wat0":"joe","wat1":3},5]
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/primitive-as-dep-nested-scope.js
+20 -2
@@ -2,11 +2,29 @@
2 // emit it trivially and repeatedly (e.g. no need to memoize props.b + 1
3 // separately from props.b)
4 // Correctness:
5 +
6 +import { identity, mutate, setProperty } from "shared-runtime";
7 +
8 // y depends on either props.b or props.b + 1
9 function PrimitiveAsDepNested(props) {
10 let x = {};
11 mutate(x);
9 - let y = foo(props.b + 1);
10 - mutate(x, props.a);
12 + let y = identity(props.b + 1);
13 + setProperty(x, props.a);
14 return [x, y];
15 }
16 +
17 +export const FIXTURE_ENTRYPOINT = {
18 + fn: PrimitiveAsDepNested,
19 + params: [{ a: 1, b: 2 }],
20 + sequentialRenders: [
21 + // change b
22 + { a: 1, b: 3 },
23 + // change b
24 + { a: 1, b: 4 },
25 + // change a
26 + { a: 2, b: 4 },
27 + // change a
28 + { a: 3, b: 4 },
29 + ],
30 +};
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-dependency-object-captured-with-reactive-mutated.expect.md
+7 -13
@@ -27,25 +27,19 @@ import { c as _c } from "react/compiler-runtime";
27 const { mutate } = require("shared-runtime");
28
29 function Component(props) {
30 - const $ = _c(4);
31 - let x;
30 + const $ = _c(2);
31 + let t0;
32 if ($[0] !== props.y) {
33 - x = {};
33 + const x = {};
34 const y = props.y;
35 const z = [x, y];
36 mutate(z);
37 - $[0] = props.y;
38 - $[1] = x;
39 - } else {
40 - x = $[1];
41 - }
42 - let t0;
43 - if ($[2] !== x) {
37 +
38 t0 = [x];
45 - $[2] = x;
46 - $[3] = t0;
39 + $[0] = props.y;
40 + $[1] = t0;
41 } else {
48 - t0 = $[3];
42 + t0 = $[1];
43 }
44 return t0;
45 }
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-scopes.expect.md
+7 -13
@@ -26,28 +26,22 @@ export const FIXTURE_ENTRYPOINT = {
26 ```javascript
27 import { c as _c } from "react/compiler-runtime";
28 function f(a, b) {
29 - const $ = _c(5);
30 - let x;
29 + const $ = _c(3);
30 + let t0;
31 if ($[0] !== a.length || $[1] !== b) {
32 - x = [];
32 + const x = [];
33 if (a.length === 1) {
34 if (b) {
35 x.push(b);
36 }
37 }
38 +
39 + t0 = <div>{x}</div>;
40 $[0] = a.length;
41 $[1] = b;
40 - $[2] = x;
41 - } else {
42 - x = $[2];
43 - }
44 - let t0;
45 - if ($[3] !== x) {
46 - t0 = <div>{x}</div>;
47 - $[3] = x;
48 - $[4] = t0;
42 + $[2] = t0;
43 } else {
50 - t0 = $[4];
44 + t0 = $[2];
45 }
46 return t0;
47 }
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactivity-analysis-interleaved-reactivity.expect.md
+11 -14
@@ -35,35 +35,32 @@ export const FIXTURE_ENTRYPOINT = {
35 ```javascript
36 import { c as _c } from "react/compiler-runtime";
37 function Component(props) {
38 - const $ = _c(7);
38 + const $ = _c(6);
39 let a;
40 + let t0;
41 if ($[0] !== props.b) {
42 a = {};
43 const b = [];
44 b.push(props.b);
45 a.a = null;
46 +
47 + t0 = [a];
48 $[0] = props.b;
49 $[1] = a;
50 + $[2] = t0;
51 } else {
52 a = $[1];
49 - }
50 - let t0;
51 - if ($[2] !== a) {
52 - t0 = [a];
53 - $[2] = a;
54 - $[3] = t0;
55 - } else {
56 - t0 = $[3];
53 + t0 = $[2];
54 }
55 const c = t0;
56 let t1;
60 - if ($[4] !== c || $[5] !== a) {
57 + if ($[3] !== c || $[4] !== a) {
58 t1 = [c, a];
62 - $[4] = c;
63 - $[5] = a;
64 - $[6] = t1;
59 + $[3] = c;
60 + $[4] = a;
61 + $[5] = t1;
62 } else {
66 - t1 = $[6];
63 + t1 = $[5];
64 }
65 return t1;
66 }
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reassignment-separate-scopes.expect.md
+19 -22
@@ -41,63 +41,60 @@ export const FIXTURE_ENTRYPOINT = {
41 ```javascript
42 import { c as _c } from "react/compiler-runtime";
43 function foo(a, b, c) {
44 - const $ = _c(11);
44 + const $ = _c(10);
45 let x;
46 + let t0;
47 if ($[0] !== a) {
48 x = [];
49 if (a) {
50 x.push(a);
51 }
52 +
53 + t0 = <div>{x}</div>;
54 $[0] = a;
55 $[1] = x;
56 + $[2] = t0;
57 } else {
58 x = $[1];
55 - }
56 - let t0;
57 - if ($[2] !== x) {
58 - t0 = <div>{x}</div>;
59 - $[2] = x;
60 - $[3] = t0;
61 - } else {
62 - t0 = $[3];
59 + t0 = $[2];
60 }
61 const y = t0;
62 bb0: switch (b) {
63 case 0: {
67 - if ($[4] !== b) {
64 + if ($[3] !== b) {
65 x = [];
66 x.push(b);
70 - $[4] = b;
71 - $[5] = x;
67 + $[3] = b;
68 + $[4] = x;
69 } else {
73 - x = $[5];
70 + x = $[4];
71 }
72 break bb0;
73 }
74 default: {
78 - if ($[6] !== c) {
75 + if ($[5] !== c) {
76 x = [];
77 x.push(c);
81 - $[6] = c;
82 - $[7] = x;
78 + $[5] = c;
79 + $[6] = x;
80 } else {
84 - x = $[7];
81 + x = $[6];
82 }
83 }
84 }
85 let t1;
89 - if ($[8] !== y || $[9] !== x) {
86 + if ($[7] !== y || $[8] !== x) {
87 t1 = (
88 <div>
89 {y}
90 {x}
91 </div>
92 );
96 - $[8] = y;
97 - $[9] = x;
98 - $[10] = t1;
93 + $[7] = y;
94 + $[8] = x;
95 + $[9] = t1;
96 } else {
100 - t1 = $[10];
97 + t1 = $[9];
98 }
99 return t1;
100 }
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/join-uncond-scopes-cond-deps.expect.md
+11 -21
@@ -61,38 +61,28 @@ import { c as _c } from "react/compiler-runtime"; // This tests an optimization,
61 import { CONST_TRUE, setProperty } from "shared-runtime";
62
63 function useJoinCondDepsInUncondScopes(props) {
64 - const $ = _c(8);
65 - let x;
66 - let y;
64 + const $ = _c(4);
65 + let t0;
66 if ($[0] !== props.a.b) {
68 - y = {};
69 - if ($[3] !== props) {
67 + const y = {};
68 + let x;
69 + if ($[2] !== props) {
70 x = {};
71 if (CONST_TRUE) {
72 setProperty(x, props.a.b);
73 }
74 - $[3] = props;
75 - $[4] = x;
74 + $[2] = props;
75 + $[3] = x;
76 } else {
77 - x = $[4];
77 + x = $[3];
78 }
79
80 setProperty(y, props.a.b);
81 - $[0] = props.a.b;
82 - $[1] = x;
83 - $[2] = y;
84 - } else {
85 - x = $[1];
86 - y = $[2];
87 - }
88 - let t0;
89 - if ($[5] !== x || $[6] !== y) {
81 t0 = [x, y];
91 - $[5] = x;
92 - $[6] = y;
93 - $[7] = t0;
82 + $[0] = props.a.b;
83 + $[1] = t0;
84 } else {
95 - t0 = $[7];
85 + t0 = $[1];
86 }
87 return t0;
88 }
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-missing-dependency-if-within-while.expect.md
+7 -13
@@ -39,11 +39,11 @@ export const FIXTURE_ENTRYPOINT = {
39 import { c as _c } from "react/compiler-runtime";
40 const someGlobal = true;
41 export default function Component(props) {
42 - const $ = _c(4);
42 + const $ = _c(2);
43 const { b } = props;
44 - let items;
44 + let t0;
45 if ($[0] !== b) {
46 - items = [];
46 + const items = [];
47 let i = 0;
48 while (i < 10) {
49 if (someGlobal) {
@@ -51,18 +51,12 @@ export default function Component(props) {
51 i++;
52 }
53 }
54 - $[0] = b;
55 - $[1] = items;
56 - } else {
57 - items = $[1];
58 - }
59 - let t0;
60 - if ($[2] !== items) {
54 +
55 t0 = <>{items}</>;
62 - $[2] = items;
63 - $[3] = t0;
56 + $[0] = b;
57 + $[1] = t0;
58 } else {
65 - t0 = $[3];
59 + t0 = $[1];
60 }
61 return t0;
62 }
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-renaming-conflicting-decls.expect.md
+32 -69
@@ -46,7 +46,7 @@ import { Stringify, identity, makeArray, toJSON } from "shared-runtime";
46 import { useMemo } from "react";
47
48 function Component(props) {
49 - const $ = _c(29);
49 + const $ = _c(13);
50 let t0;
51 let t1;
52 let t2;
@@ -83,87 +83,50 @@ function Component(props) {
83 t3 = $[5];
84 }
85 const linkProps = t3;
86 - let T0;
86 let t4;
88 - let t5;
89 - let t6;
90 - let t7;
91 - let t8;
92 - let t9;
93 - let t10;
87 if ($[6] !== linkProps) {
88 const x = {};
96 -
97 - T0 = Stringify;
98 - t4 = linkProps;
99 - if ($[15] === Symbol.for("react.memo_cache_sentinel")) {
89 + let t5;
90 + let t6;
91 + let t7;
92 + let t8;
93 + let t9;
94 + if ($[8] === Symbol.for("react.memo_cache_sentinel")) {
95 t5 = [1];
96 t6 = [2];
97 t7 = [3];
98 t8 = [4];
99 t9 = [5];
105 - $[15] = t5;
106 - $[16] = t6;
107 - $[17] = t7;
108 - $[18] = t8;
109 - $[19] = t9;
100 + $[8] = t5;
101 + $[9] = t6;
102 + $[10] = t7;
103 + $[11] = t8;
104 + $[12] = t9;
105 } else {
111 - t5 = $[15];
112 - t6 = $[16];
113 - t7 = $[17];
114 - t8 = $[18];
115 - t9 = $[19];
106 + t5 = $[8];
107 + t6 = $[9];
108 + t7 = $[10];
109 + t8 = $[11];
110 + t9 = $[12];
111 }
117 -
118 - t10 = makeArray(x, 2);
119 - $[6] = linkProps;
120 - $[7] = T0;
121 - $[8] = t4;
122 - $[9] = t5;
123 - $[10] = t6;
124 - $[11] = t7;
125 - $[12] = t8;
126 - $[13] = t9;
127 - $[14] = t10;
128 - } else {
129 - T0 = $[7];
130 - t4 = $[8];
131 - t5 = $[9];
132 - t6 = $[10];
133 - t7 = $[11];
134 - t8 = $[12];
135 - t9 = $[13];
136 - t10 = $[14];
137 - }
138 - let t11;
139 - if (
140 - $[20] !== T0 ||
141 - $[21] !== t4 ||
142 - $[22] !== t5 ||
143 - $[23] !== t6 ||
144 - $[24] !== t7 ||
145 - $[25] !== t8 ||
146 - $[26] !== t9 ||
147 - $[27] !== t10
148 - ) {
149 - t11 = (
150 - <T0 link={t4} val1={t5} val2={t6} val3={t7} val4={t8} val5={t9}>
151 - {t10}
152 - </T0>
112 + t4 = (
113 + <Stringify
114 + link={linkProps}
115 + val1={t5}
116 + val2={t6}
117 + val3={t7}
118 + val4={t8}
119 + val5={t9}
120 + >
121 + {makeArray(x, 2)}
122 + </Stringify>
123 );
154 - $[20] = T0;
155 - $[21] = t4;
156 - $[22] = t5;
157 - $[23] = t6;
158 - $[24] = t7;
159 - $[25] = t8;
160 - $[26] = t9;
161 - $[27] = t10;
162 - $[28] = t11;
124 + $[6] = linkProps;
125 + $[7] = t4;
126 } else {
164 - t11 = $[28];
127 + t4 = $[7];
128 }
166 - return t11;
129 + return t4;
130 }
131
132 export const FIXTURE_ENTRYPOINT = {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/same-variable-as-dep-and-redeclare.expect.md
+24 -27
@@ -52,68 +52,65 @@ export const FIXTURE_ENTRYPOINT = {
52 import { c as _c } from "react/compiler-runtime"; // note: comments are for the ideal scopes, not what is currently
53 // emitted
54 function foo(props) {
55 - const $ = _c(15);
55 + const $ = _c(14);
56 let x;
57 + let t0;
58 if ($[0] !== props.a) {
59 x = [];
60 x.push(props.a);
61 +
62 + t0 = <div>{x}</div>;
63 $[0] = props.a;
64 $[1] = x;
65 + $[2] = t0;
66 } else {
67 x = $[1];
64 - }
65 - let t0;
66 - if ($[2] !== x) {
67 - t0 = <div>{x}</div>;
68 - $[2] = x;
69 - $[3] = t0;
70 - } else {
71 - t0 = $[3];
68 + t0 = $[2];
69 }
70 const header = t0;
71 let y;
75 - if ($[4] !== x || $[5] !== props.b || $[6] !== props.c) {
72 + if ($[3] !== x || $[4] !== props.b || $[5] !== props.c) {
73 y = [x];
74 x = [];
75 y.push(props.b);
76 x.push(props.c);
80 - $[4] = x;
81 - $[5] = props.b;
82 - $[6] = props.c;
83 - $[7] = y;
84 - $[8] = x;
77 + $[3] = x;
78 + $[4] = props.b;
79 + $[5] = props.c;
80 + $[6] = y;
81 + $[7] = x;
82 } else {
86 - y = $[7];
87 - x = $[8];
83 + y = $[6];
84 + x = $[7];
85 }
86 let t1;
90 - if ($[9] !== x || $[10] !== y) {
87 + if ($[8] !== x || $[9] !== y) {
88 t1 = (
89 <div>
90 {x}
91 {y}
92 </div>
93 );
97 - $[9] = x;
98 - $[10] = y;
99 - $[11] = t1;
94 + $[8] = x;
95 + $[9] = y;
96 + $[10] = t1;
97 } else {
101 - t1 = $[11];
98 + t1 = $[10];
99 }
100 const content = t1;
101 let t2;
105 - if ($[12] !== header || $[13] !== content) {
102 + if ($[11] !== header || $[12] !== content) {
103 t2 = (
104 <>
105 {header}
106 {content}
107 </>
108 );
112 - $[12] = header;
113 - $[13] = content;
114 - $[14] = t2;
109 + $[11] = header;
110 + $[12] = content;
111 + $[13] = t2;
112 } else {
116 - t2 = $[14];
113 + t2 = $[13];
114 }
115 return t2;
116 }
compiler/packages/snap/src/SproutTodoFilter.ts
-2
@@ -220,7 +220,6 @@ const skipFilter = new Set([
220 * Tests with one or more params, with external references.
221 */
222 "alias-computed-load",
223 - "allocating-primitive-as-dep-nested-scope",
223 "allocating-primitive-as-dep",
224 "allow-passing-refs-as-props",
225 "array-at-closure",
@@ -309,7 +308,6 @@ const skipFilter = new Set([
308 "optional-receiver-method-call",
309 "optional-receiver-optional-method",
310 "primitive-alias-mutate",
312 - "primitive-as-dep-nested-scope",
311 "primitive-as-dep",
312 "property-assignment",
313 "property-call-spread",
compiler/packages/snap/src/sprout/index.ts
+23 -21
@@ -32,33 +32,35 @@ export function runSprout(
32 originalCode: string,
33 forgetCode: string
34 ): SproutResult {
35 - const nonForgetResult = doEval(originalCode);
35 const forgetResult = doEval(forgetCode);
37 -
36 if (forgetResult.kind === "UnexpectedError") {
37 return makeError("Unexpected error in Forget runner", forgetResult.value);
40 - } else if (nonForgetResult.kind === "UnexpectedError") {
41 - return makeError(
42 - "Unexpected error in non-forget runner",
43 - nonForgetResult.value
44 - );
45 - } else if (
46 - forgetResult.kind !== nonForgetResult.kind ||
47 - forgetResult.value !== nonForgetResult.value ||
48 - !logsEqual(forgetResult.logs, nonForgetResult.logs)
49 - ) {
50 - return makeError(
51 - "Found differences in evaluator results",
52 - `Non-forget (expected):
38 + }
39 + if (originalCode.indexOf("@disableNonForgetInSprout") === -1) {
40 + const nonForgetResult = doEval(originalCode);
41 +
42 + if (nonForgetResult.kind === "UnexpectedError") {
43 + return makeError(
44 + "Unexpected error in non-forget runner",
45 + nonForgetResult.value
46 + );
47 + } else if (
48 + forgetResult.kind !== nonForgetResult.kind ||
49 + forgetResult.value !== nonForgetResult.value ||
50 + !logsEqual(forgetResult.logs, nonForgetResult.logs)
51 + ) {
52 + return makeError(
53 + "Found differences in evaluator results",
54 + `Non-forget (expected):
55 ${stringify(nonForgetResult)}
56 Forget:
57 ${stringify(forgetResult)}
58 `
57 - );
58 - } else {
59 - return {
60 - kind: "success",
61 - value: stringify(forgetResult),
62 - };
59 + );
60 + }
61 }
62 + return {
63 + kind: "success",
64 + value: stringify(forgetResult),
65 + };
66 }