From b687fd27b524f7922b67b8a6b01dc80fc8d8a233 Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Thu, 23 May 2024 01:09:49 +0100 Subject: [PATCH] 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 --- ...rgeReactiveScopesThatInvalidateTogether.ts | 64 +------ ...ng-primitive-as-dep-nested-scope.expect.md | 88 ++++++---- ...llocating-primitive-as-dep-nested-scope.js | 22 ++- .../array-access-assignment.expect.md | 76 ++++---- .../compiler/array-access-assignment.js | 14 +- .../codegen-emit-make-read-only.expect.md | 22 +-- ...er-declaration-of-previous-scope.expect.md | 162 ++++++++++++++++++ ...-is-inner-declaration-of-previous-scope.js | 36 ++++ .../compiler/for-in-statement.expect.md | 31 ++-- .../fixtures/compiler/for-in-statement.js | 4 + ...oop-with-value-block-initializer.expect.md | 90 ++++++++-- .../for-loop-with-value-block-initializer.js | 32 ++++ .../phi-type-inference-array-push.expect.md | 39 +++-- .../compiler/phi-type-inference-array-push.js | 5 + ...Callback-in-other-reactive-block.expect.md | 39 ++--- .../useMemo-in-other-reactive-block.expect.md | 43 ++--- .../primitive-as-dep-nested-scope.expect.md | 88 ++++++---- .../compiler/primitive-as-dep-nested-scope.js | 22 ++- ...t-captured-with-reactive-mutated.expect.md | 20 +-- .../compiler/reactive-scopes.expect.md | 20 +-- ...-analysis-interleaved-reactivity.expect.md | 25 ++- .../reassignment-separate-scopes.expect.md | 41 ++--- .../join-uncond-scopes-cond-deps.expect.md | 32 ++-- ...ssing-dependency-if-within-while.expect.md | 20 +-- ...repro-renaming-conflicting-decls.expect.md | 101 ++++------- ...me-variable-as-dep-and-redeclare.expect.md | 51 +++--- .../packages/snap/src/SproutTodoFilter.ts | 2 - compiler/packages/snap/src/sprout/index.ts | 44 ++--- 28 files changed, 744 insertions(+), 489 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/dont-merge-if-dep-is-inner-declaration-of-previous-scope.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/dont-merge-if-dep-is-inner-declaration-of-previous-scope.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/MergeReactiveScopesThatInvalidateTogether.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/MergeReactiveScopesThatInvalidateTogether.ts index f9ef2f644d..149ba34bdf 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/MergeReactiveScopesThatInvalidateTogether.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/MergeReactiveScopesThatInvalidateTogether.ts @@ -13,7 +13,6 @@ import { Place, ReactiveBlock, ReactiveFunction, - ReactiveInstruction, ReactiveScope, ReactiveScopeBlock, ReactiveScopeDependencies, @@ -515,64 +514,7 @@ function scopeIsEligibleForMerging(scopeBlock: ReactiveScopeBlock): boolean { */ return true; } - const visitor = new DeclarationTypeVisitor(scopeBlock.scope); - visitor.visitScope(scopeBlock, undefined); - return visitor.alwaysInvalidatesOnInputChange; -} - -class DeclarationTypeVisitor extends ReactiveFunctionVisitor { - scope: ReactiveScope; - alwaysInvalidatesOnInputChange: boolean = false; - - constructor(scope: ReactiveScope) { - super(); - this.scope = scope; - } - - override visitScope(scopeBlock: ReactiveScopeBlock, state: void): void { - if (scopeBlock.scope.id !== this.scope.id) { - return; - } - this.traverseScope(scopeBlock, state); - } - - override visitInstruction( - instruction: ReactiveInstruction, - state: void - ): void { - this.traverseInstruction(instruction, state); - if ( - instruction.lvalue === null || - !this.scope.declarations.has(instruction.lvalue.identifier.id) - ) { - /* - * no lvalue or this instruction isn't directly constructing a - * scope output value, skip - */ - log( - ` skip instruction lvalue=${ - instruction.lvalue?.identifier.id - } declaration?=${ - instruction.lvalue != null && - this.scope.declarations.has(instruction.lvalue.identifier.id) - } scope=${printReactiveScopeSummary(this.scope)}` - ); - return; - } - switch (instruction.value.kind) { - case "FunctionExpression": - case "ArrayExpression": - case "JsxExpression": - case "JsxFragment": - case "ObjectExpression": { - /* - * These instruction types *always* allocate. If they execute - * they will produce a new value, triggering downstream reactive - * updates - */ - this.alwaysInvalidatesOnInputChange = true; - break; - } - } - } + return [...scopeBlock.scope.declarations].some(([, decl]) => + isAlwaysInvalidatingType(decl.identifier.type) + ); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allocating-primitive-as-dep-nested-scope.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allocating-primitive-as-dep-nested-scope.expect.md index 7327fe7231..e15820b053 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allocating-primitive-as-dep-nested-scope.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allocating-primitive-as-dep-nested-scope.expect.md @@ -5,15 +5,33 @@ // bar(props.b) is an allocating expression that produces a primitive, which means // that Forget should memoize it. // Correctness: + +import { identity, mutate, setProperty } from "shared-runtime"; + // - y depends on either bar(props.b) or bar(props.b) + 1 function AllocatingPrimitiveAsDepNested(props) { let x = {}; mutate(x); - let y = foo(bar(props.b) + 1); - mutate(x, props.a); + let y = identity(identity(props.b) + 1); + setProperty(x, props.a); return [x, y]; } +export const FIXTURE_ENTRYPOINT = { + fn: AllocatingPrimitiveAsDepNested, + params: [{ a: 1, b: 2 }], + sequentialRenders: [ + // change b + { a: 1, b: 3 }, + // change b + { a: 1, b: 4 }, + // change a + { a: 2, b: 4 }, + // change a + { a: 3, b: 4 }, + ], +}; + ``` ## Code @@ -22,44 +40,56 @@ function AllocatingPrimitiveAsDepNested(props) { import { c as _c } from "react/compiler-runtime"; // bar(props.b) is an allocating expression that produces a primitive, which means // that Forget should memoize it. // Correctness: + +import { identity, mutate, setProperty } from "shared-runtime"; + // - y depends on either bar(props.b) or bar(props.b) + 1 function AllocatingPrimitiveAsDepNested(props) { - const $ = _c(9); - let x; - let y; + const $ = _c(5); + let t0; if ($[0] !== props.b || $[1] !== props.a) { - x = {}; + const x = {}; mutate(x); - const t0 = bar(props.b) + 1; - let t1; - if ($[4] !== t0) { - t1 = foo(t0); - $[4] = t0; - $[5] = t1; + const t1 = identity(props.b) + 1; + let t2; + if ($[3] !== t1) { + t2 = identity(t1); + $[3] = t1; + $[4] = t2; } else { - t1 = $[5]; + t2 = $[4]; } - y = t1; - mutate(x, props.a); + const y = t2; + setProperty(x, props.a); + t0 = [x, y]; $[0] = props.b; $[1] = props.a; - $[2] = x; - $[3] = y; + $[2] = t0; } else { - x = $[2]; - y = $[3]; - } - let t0; - if ($[6] !== x || $[7] !== y) { - t0 = [x, y]; - $[6] = x; - $[7] = y; - $[8] = t0; - } else { - t0 = $[8]; + t0 = $[2]; } return t0; } +export const FIXTURE_ENTRYPOINT = { + fn: AllocatingPrimitiveAsDepNested, + params: [{ a: 1, b: 2 }], + sequentialRenders: [ + // change b + { a: 1, b: 3 }, + // change b + { a: 1, b: 4 }, + // change a + { a: 2, b: 4 }, + // change a + { a: 3, b: 4 }, + ], +}; + ``` - \ No newline at end of file + +### Eval output +(kind: ok) [{"wat0":"joe","wat1":1},4] +[{"wat0":"joe","wat1":1},5] +[{"wat0":"joe","wat1":2},5] +[{"wat0":"joe","wat1":3},5] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allocating-primitive-as-dep-nested-scope.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allocating-primitive-as-dep-nested-scope.js index 765c7ef79f..896680bb25 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allocating-primitive-as-dep-nested-scope.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allocating-primitive-as-dep-nested-scope.js @@ -1,11 +1,29 @@ // bar(props.b) is an allocating expression that produces a primitive, which means // that Forget should memoize it. // Correctness: + +import { identity, mutate, setProperty } from "shared-runtime"; + // - y depends on either bar(props.b) or bar(props.b) + 1 function AllocatingPrimitiveAsDepNested(props) { let x = {}; mutate(x); - let y = foo(bar(props.b) + 1); - mutate(x, props.a); + let y = identity(identity(props.b) + 1); + setProperty(x, props.a); return [x, y]; } + +export const FIXTURE_ENTRYPOINT = { + fn: AllocatingPrimitiveAsDepNested, + params: [{ a: 1, b: 2 }], + sequentialRenders: [ + // change b + { a: 1, b: 3 }, + // change b + { a: 1, b: 4 }, + // change a + { a: 2, b: 4 }, + // change a + { a: 3, b: 4 }, + ], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-access-assignment.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-access-assignment.expect.md index 011436bd42..92da9b6684 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-access-assignment.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-access-assignment.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -function foo(a, b, c) { +function Component({ a, b, c }) { const x = [a]; const y = [null, b]; const z = [[], [], [c]]; @@ -12,9 +12,15 @@ function foo(a, b, c) { } export const FIXTURE_ENTRYPOINT = { - fn: foo, - params: [1, 2, 3], - isComponent: false, + fn: Component, + params: [{ a: 1, b: 20, c: 300 }], + sequentialRenders: [ + { a: 2, b: 20, c: 300 }, + { a: 3, b: 20, c: 300 }, + { a: 3, b: 21, c: 300 }, + { a: 3, b: 22, c: 300 }, + { a: 3, b: 22, c: 301 }, + ], }; ``` @@ -23,52 +29,52 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; -function foo(a, b, c) { - const $ = _c(10); - let x; - let z; +function Component(t0) { + const $ = _c(6); + const { a, b, c } = t0; + let t1; if ($[0] !== a || $[1] !== b || $[2] !== c) { - x = [a]; - let t0; - if ($[5] !== b) { - t0 = [null, b]; - $[5] = b; - $[6] = t0; + const x = [a]; + let t2; + if ($[4] !== b) { + t2 = [null, b]; + $[4] = b; + $[5] = t2; } else { - t0 = $[6]; + t2 = $[5]; } - const y = t0; - z = [[], [], [c]]; + const y = t2; + const z = [[], [], [c]]; x[0] = y[1]; z[0][0] = x[0]; + t1 = [x, z]; $[0] = a; $[1] = b; $[2] = c; - $[3] = x; - $[4] = z; + $[3] = t1; } else { - x = $[3]; - z = $[4]; + t1 = $[3]; } - let t0; - if ($[7] !== x || $[8] !== z) { - t0 = [x, z]; - $[7] = x; - $[8] = z; - $[9] = t0; - } else { - t0 = $[9]; - } - return t0; + return t1; } export const FIXTURE_ENTRYPOINT = { - fn: foo, - params: [1, 2, 3], - isComponent: false, + fn: Component, + params: [{ a: 1, b: 20, c: 300 }], + sequentialRenders: [ + { a: 2, b: 20, c: 300 }, + { a: 3, b: 20, c: 300 }, + { a: 3, b: 21, c: 300 }, + { a: 3, b: 22, c: 300 }, + { a: 3, b: 22, c: 301 }, + ], }; ``` ### Eval output -(kind: ok) [[2],[[2],[],[3]]] \ No newline at end of file +(kind: ok) [[20],[[20],[],[300]]] +[[20],[[20],[],[300]]] +[[21],[[21],[],[300]]] +[[22],[[22],[],[300]]] +[[22],[[22],[],[301]]] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-access-assignment.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-access-assignment.js index 7efd9ca69d..3904dcf228 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-access-assignment.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-access-assignment.js @@ -1,4 +1,4 @@ -function foo(a, b, c) { +function Component({ a, b, c }) { const x = [a]; const y = [null, b]; const z = [[], [], [c]]; @@ -8,7 +8,13 @@ function foo(a, b, c) { } export const FIXTURE_ENTRYPOINT = { - fn: foo, - params: [1, 2, 3], - isComponent: false, + fn: Component, + params: [{ a: 1, b: 20, c: 300 }], + sequentialRenders: [ + { a: 2, b: 20, c: 300 }, + { a: 3, b: 20, c: 300 }, + { a: 3, b: 21, c: 300 }, + { a: 3, b: 22, c: 300 }, + { a: 3, b: 22, c: 301 }, + ], }; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-emit-make-read-only.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-emit-make-read-only.expect.md index 61d87440bd..ba9e39e691 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-emit-make-read-only.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-emit-make-read-only.expect.md @@ -23,26 +23,20 @@ import { makeReadOnly } from "react-compiler-runtime"; import { c as _c } from "react/compiler-runtime"; // @enableEmitFreeze true function MyComponentName(props) { - const $ = _c(5); - let x; + const $ = _c(3); + let y; if ($[0] !== props.a || $[1] !== props.b) { - x = {}; + const x = {}; foo(x, props.a); foo(x, props.b); - $[0] = props.a; - $[1] = props.b; - $[2] = __DEV__ ? makeReadOnly(x, "MyComponentName") : x; - } else { - x = $[2]; - } - let y; - if ($[3] !== x) { + y = []; y.push(x); - $[3] = x; - $[4] = __DEV__ ? makeReadOnly(y, "MyComponentName") : y; + $[0] = props.a; + $[1] = props.b; + $[2] = __DEV__ ? makeReadOnly(y, "MyComponentName") : y; } else { - y = $[4]; + y = $[2]; } return y; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/dont-merge-if-dep-is-inner-declaration-of-previous-scope.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/dont-merge-if-dep-is-inner-declaration-of-previous-scope.expect.md new file mode 100644 index 0000000000..ad843e230f --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/dont-merge-if-dep-is-inner-declaration-of-previous-scope.expect.md @@ -0,0 +1,162 @@ + +## Input + +```javascript +import { ValidateMemoization } from "shared-runtime"; + +// Achieving Forget's level of memoization precision in this example isn't possible with useMemo +// without significantly altering the code, so disable the non-Forget evaluation of this fixture. +// @disableNonForgetInSprout +function Component({ a, b, c }) { + const x = []; + let y; + if (a) { + y = [b]; + } + x.push(c); + + // this scope should not merge with the above scope because y does not invalidate + // on changes to `c` + const z = [y]; + + // return [x, z]; + return ( + <> + + + + ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ a: false, b: null, c: 0 }], + sequentialRenders: [ + { a: false, b: null, c: 0 }, + { a: false, b: null, c: 1 }, + { a: true, b: 0, c: 1 }, + { a: true, b: 1, c: 1 }, + ], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { ValidateMemoization } from "shared-runtime"; + +// Achieving Forget's level of memoization precision in this example isn't possible with useMemo +// without significantly altering the code, so disable the non-Forget evaluation of this fixture. +// @disableNonForgetInSprout +function Component(t0) { + const $ = _c(25); + const { a, b, c } = t0; + let y; + let x; + if ($[0] !== a || $[1] !== b || $[2] !== c) { + x = []; + if (a) { + let t1; + if ($[5] !== b) { + t1 = [b]; + $[5] = b; + $[6] = t1; + } else { + t1 = $[6]; + } + y = t1; + } + + x.push(c); + $[0] = a; + $[1] = b; + $[2] = c; + $[3] = y; + $[4] = x; + } else { + y = $[3]; + x = $[4]; + } + let t1; + if ($[7] !== y) { + t1 = [y]; + $[7] = y; + $[8] = t1; + } else { + t1 = $[8]; + } + const z = t1; + let t2; + if ($[9] !== a || $[10] !== b || $[11] !== c) { + t2 = [a, b, c]; + $[9] = a; + $[10] = b; + $[11] = c; + $[12] = t2; + } else { + t2 = $[12]; + } + let t3; + if ($[13] !== t2 || $[14] !== x) { + t3 = ; + $[13] = t2; + $[14] = x; + $[15] = t3; + } else { + t3 = $[15]; + } + let t4; + if ($[16] !== a || $[17] !== b) { + t4 = [a, b]; + $[16] = a; + $[17] = b; + $[18] = t4; + } else { + t4 = $[18]; + } + let t5; + if ($[19] !== t4 || $[20] !== z) { + t5 = ; + $[19] = t4; + $[20] = z; + $[21] = t5; + } else { + t5 = $[21]; + } + let t6; + if ($[22] !== t3 || $[23] !== t5) { + t6 = ( + <> + {t3} + {t5} + + ); + $[22] = t3; + $[23] = t5; + $[24] = t6; + } else { + t6 = $[24]; + } + return t6; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ a: false, b: null, c: 0 }], + sequentialRenders: [ + { a: false, b: null, c: 0 }, + { a: false, b: null, c: 1 }, + { a: true, b: 0, c: 1 }, + { a: true, b: 1, c: 1 }, + ], +}; + +``` + +### Eval output +(kind: ok)
{"inputs":[false,null,0],"output":[0]}
{"inputs":[false,null],"output":[null]}
+
{"inputs":[false,null,1],"output":[1]}
{"inputs":[false,null],"output":[null]}
+
{"inputs":[true,0,1],"output":[1]}
{"inputs":[true,0],"output":[[0]]}
+
{"inputs":[true,1,1],"output":[1]}
{"inputs":[true,1],"output":[[1]]}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/dont-merge-if-dep-is-inner-declaration-of-previous-scope.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/dont-merge-if-dep-is-inner-declaration-of-previous-scope.js new file mode 100644 index 0000000000..706f6363b8 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/dont-merge-if-dep-is-inner-declaration-of-previous-scope.js @@ -0,0 +1,36 @@ +import { ValidateMemoization } from "shared-runtime"; + +// Achieving Forget's level of memoization precision in this example isn't possible with useMemo +// without significantly altering the code, so disable the non-Forget evaluation of this fixture. +// @disableNonForgetInSprout +function Component({ a, b, c }) { + const x = []; + let y; + if (a) { + y = [b]; + } + x.push(c); + + // this scope should not merge with the above scope because y does not invalidate + // on changes to `c` + const z = [y]; + + // return [x, z]; + return ( + <> + + + + ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ a: false, b: null, c: 0 }], + sequentialRenders: [ + { a: false, b: null, c: 0 }, + { a: false, b: null, c: 1 }, + { a: true, b: 0, c: 1 }, + { a: true, b: 1, c: 1 }, + ], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/for-in-statement.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/for-in-statement.expect.md index de6b7d8971..77a34e409a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/for-in-statement.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/for-in-statement.expect.md @@ -13,6 +13,10 @@ function Component(props) { export const FIXTURE_ENTRYPOINT = { fn: Component, params: [{ hello: null, world: undefined, "!": true }], + sequentialRenders: [ + { a: null, b: null, c: null }, + { lauren: true, mofei: true, sathya: true, jason: true }, + ], }; ``` @@ -22,25 +26,19 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function Component(props) { - const $ = _c(4); - let items; + const $ = _c(2); + let t0; if ($[0] !== props) { - items = []; + const items = []; for (const key in props) { items.push(
{key}
); } - $[0] = props; - $[1] = items; - } else { - items = $[1]; - } - let t0; - if ($[2] !== items) { + t0 =
{items}
; - $[2] = items; - $[3] = t0; + $[0] = props; + $[1] = t0; } else { - t0 = $[3]; + t0 = $[1]; } return t0; } @@ -48,9 +46,14 @@ function Component(props) { export const FIXTURE_ENTRYPOINT = { fn: Component, params: [{ hello: null, world: undefined, "!": true }], + sequentialRenders: [ + { a: null, b: null, c: null }, + { lauren: true, mofei: true, sathya: true, jason: true }, + ], }; ``` ### Eval output -(kind: ok)
hello
world
!
\ No newline at end of file +(kind: ok)
a
b
c
+
lauren
mofei
sathya
jason
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/for-in-statement.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/for-in-statement.js index cb2c83bf16..520d900cb4 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/for-in-statement.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/for-in-statement.js @@ -9,4 +9,8 @@ function Component(props) { export const FIXTURE_ENTRYPOINT = { fn: Component, params: [{ hello: null, world: undefined, "!": true }], + sequentialRenders: [ + { a: null, b: null, c: null }, + { lauren: true, mofei: true, sathya: true, jason: true }, + ], }; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/for-loop-with-value-block-initializer.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/for-loop-with-value-block-initializer.expect.md index 4586306128..d9a1fd799e 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/for-loop-with-value-block-initializer.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/for-loop-with-value-block-initializer.expect.md @@ -23,6 +23,38 @@ export const FIXTURE_ENTRYPOINT = { ], }, ], + sequentialRenders: [ + { + start: 1, + items: [ + { id: 0, value: "zero" }, + { id: 1, value: "one" }, + ], + }, + { + start: 2, + items: [ + { id: 0, value: "zero" }, + { id: 1, value: "one" }, + ], + }, + { + start: 0, + items: [ + { id: 0, value: "zero" }, + { id: 1, value: "one" }, + { id: 2, value: "two" }, + ], + }, + { + start: 1, + items: [ + { id: 0, value: "zero" }, + { id: 1, value: "one" }, + { id: 2, value: "two" }, + ], + }, + ], }; ``` @@ -33,27 +65,21 @@ export const FIXTURE_ENTRYPOINT = { import { c as _c } from "react/compiler-runtime"; const TOTAL = 10; function Component(props) { - const $ = _c(5); - let items; + const $ = _c(3); + let t0; if ($[0] !== props.start || $[1] !== props.items) { - items = []; + const items = []; for (let i = props.start ?? 0; i < props.items.length; i++) { const item = props.items[i]; items.push(
{item.value}
); } + + t0 =
{items}
; $[0] = props.start; $[1] = props.items; - $[2] = items; + $[2] = t0; } else { - items = $[2]; - } - let t0; - if ($[3] !== items) { - t0 =
{items}
; - $[3] = items; - $[4] = t0; - } else { - t0 = $[4]; + t0 = $[2]; } return t0; } @@ -69,9 +95,45 @@ export const FIXTURE_ENTRYPOINT = { ], }, ], + + sequentialRenders: [ + { + start: 1, + items: [ + { id: 0, value: "zero" }, + { id: 1, value: "one" }, + ], + }, + { + start: 2, + items: [ + { id: 0, value: "zero" }, + { id: 1, value: "one" }, + ], + }, + { + start: 0, + items: [ + { id: 0, value: "zero" }, + { id: 1, value: "one" }, + { id: 2, value: "two" }, + ], + }, + { + start: 1, + items: [ + { id: 0, value: "zero" }, + { id: 1, value: "one" }, + { id: 2, value: "two" }, + ], + }, + ], }; ``` ### Eval output -(kind: ok)
zero
one
\ No newline at end of file +(kind: ok)
one
+
+
zero
one
two
+
one
two
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/for-loop-with-value-block-initializer.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/for-loop-with-value-block-initializer.js index b966dc60d1..7ccad19179 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/for-loop-with-value-block-initializer.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/for-loop-with-value-block-initializer.js @@ -19,4 +19,36 @@ export const FIXTURE_ENTRYPOINT = { ], }, ], + sequentialRenders: [ + { + start: 1, + items: [ + { id: 0, value: "zero" }, + { id: 1, value: "one" }, + ], + }, + { + start: 2, + items: [ + { id: 0, value: "zero" }, + { id: 1, value: "one" }, + ], + }, + { + start: 0, + items: [ + { id: 0, value: "zero" }, + { id: 1, value: "one" }, + { id: 2, value: "two" }, + ], + }, + { + start: 1, + items: [ + { id: 0, value: "zero" }, + { id: 1, value: "one" }, + { id: 2, value: "two" }, + ], + }, + ], }; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-type-inference-array-push.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-type-inference-array-push.expect.md index 7beca60780..ee7aa62495 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-type-inference-array-push.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-type-inference-array-push.expect.md @@ -22,6 +22,11 @@ function Component(props) { export const FIXTURE_ENTRYPOINT = { fn: Component, params: [{ cond: true, value: 42 }], + sequentialRenders: [ + { cond: true, value: 3.14 }, + { cond: false, value: 3.14 }, + { cond: true, value: 42 }, + ], }; ``` @@ -31,11 +36,11 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function Component(props) { - const $ = _c(6); - let x; - let y; + const $ = _c(2); + let t0; if ($[0] !== props) { - x = {}; + const x = {}; + let y; if (props.cond) { y = [props.value]; } else { @@ -43,21 +48,12 @@ function Component(props) { } y.push(x); - $[0] = props; - $[1] = x; - $[2] = y; - } else { - x = $[1]; - y = $[2]; - } - let t0; - if ($[3] !== x || $[4] !== y) { + t0 = [x, y]; - $[3] = x; - $[4] = y; - $[5] = t0; + $[0] = props; + $[1] = t0; } else { - t0 = $[5]; + t0 = $[1]; } return t0; } @@ -65,9 +61,16 @@ function Component(props) { export const FIXTURE_ENTRYPOINT = { fn: Component, params: [{ cond: true, value: 42 }], + sequentialRenders: [ + { cond: true, value: 3.14 }, + { cond: false, value: 3.14 }, + { cond: true, value: 42 }, + ], }; ``` ### Eval output -(kind: ok) [{},[42,"[[ cyclic ref *1 ]]"]] \ No newline at end of file +(kind: ok) [{},[3.14,"[[ cyclic ref *1 ]]"]] +[{},["[[ cyclic ref *1 ]]"]] +[{},[42,"[[ cyclic ref *1 ]]"]] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-type-inference-array-push.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-type-inference-array-push.js index 052e1a274a..1cbf1e0ab2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-type-inference-array-push.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-type-inference-array-push.js @@ -18,4 +18,9 @@ function Component(props) { export const FIXTURE_ENTRYPOINT = { fn: Component, params: [{ cond: true, value: 42 }], + sequentialRenders: [ + { cond: true, value: 3.14 }, + { cond: false, value: 3.14 }, + { cond: true, value: 42 }, + ], }; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useCallback-in-other-reactive-block.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useCallback-in-other-reactive-block.expect.md index f5aa0b4dd7..b6ccab92d5 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useCallback-in-other-reactive-block.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useCallback-in-other-reactive-block.expect.md @@ -37,41 +37,30 @@ import { arrayPush } from "shared-runtime"; // useCallback-produced values can exist in nested reactive blocks, as long // as their reactive dependencies are a subset of depslist from source function useFoo(minWidth, otherProp) { - const $ = _c(11); + const $ = _c(7); const [width] = useState(1); - let style; - let x; + let t0; if ($[0] !== width || $[1] !== minWidth || $[2] !== otherProp) { - x = []; - let t0; - if ($[5] !== minWidth || $[6] !== width) { - t0 = () => ({ width: Math.max(minWidth, width) }); - $[5] = minWidth; - $[6] = width; - $[7] = t0; + const x = []; + let t1; + if ($[4] !== minWidth || $[5] !== width) { + t1 = () => ({ width: Math.max(minWidth, width) }); + $[4] = minWidth; + $[5] = width; + $[6] = t1; } else { - t0 = $[7]; + t1 = $[6]; } - style = t0; + const style = t1; arrayPush(x, otherProp); + t0 = [style, x]; $[0] = width; $[1] = minWidth; $[2] = otherProp; - $[3] = style; - $[4] = x; + $[3] = t0; } else { - style = $[3]; - x = $[4]; - } - let t0; - if ($[8] !== style || $[9] !== x) { - t0 = [style, x]; - $[8] = style; - $[9] = x; - $[10] = t0; - } else { - t0 = $[10]; + t0 = $[3]; } return t0; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-in-other-reactive-block.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-in-other-reactive-block.expect.md index c83f9d4a50..6458e9812a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-in-other-reactive-block.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-in-other-reactive-block.expect.md @@ -37,44 +37,33 @@ import { arrayPush } from "shared-runtime"; // useMemo-produced values can exist in nested reactive blocks, as long // as their reactive dependencies are a subset of depslist from source function useFoo(minWidth, otherProp) { - const $ = _c(10); + const $ = _c(6); const [width] = useState(1); - let style; - let x; + let t0; if ($[0] !== width || $[1] !== minWidth || $[2] !== otherProp) { - x = []; - let t0; + const x = []; + let t1; - const t1 = Math.max(minWidth, width); - let t2; - if ($[5] !== t1) { - t2 = { width: t1 }; - $[5] = t1; - $[6] = t2; + const t2 = Math.max(minWidth, width); + let t3; + if ($[4] !== t2) { + t3 = { width: t2 }; + $[4] = t2; + $[5] = t3; } else { - t2 = $[6]; + t3 = $[5]; } - t0 = t2; - style = t0; + t1 = t3; + const style = t1; arrayPush(x, otherProp); + t0 = [style, x]; $[0] = width; $[1] = minWidth; $[2] = otherProp; - $[3] = style; - $[4] = x; + $[3] = t0; } else { - style = $[3]; - x = $[4]; - } - let t0; - if ($[7] !== style || $[8] !== x) { - t0 = [style, x]; - $[7] = style; - $[8] = x; - $[9] = t0; - } else { - t0 = $[9]; + t0 = $[3]; } return t0; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/primitive-as-dep-nested-scope.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/primitive-as-dep-nested-scope.expect.md index 324cc66af6..9ac2be35d9 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/primitive-as-dep-nested-scope.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/primitive-as-dep-nested-scope.expect.md @@ -6,15 +6,33 @@ // emit it trivially and repeatedly (e.g. no need to memoize props.b + 1 // separately from props.b) // Correctness: + +import { identity, mutate, setProperty } from "shared-runtime"; + // y depends on either props.b or props.b + 1 function PrimitiveAsDepNested(props) { let x = {}; mutate(x); - let y = foo(props.b + 1); - mutate(x, props.a); + let y = identity(props.b + 1); + setProperty(x, props.a); return [x, y]; } +export const FIXTURE_ENTRYPOINT = { + fn: PrimitiveAsDepNested, + params: [{ a: 1, b: 2 }], + sequentialRenders: [ + // change b + { a: 1, b: 3 }, + // change b + { a: 1, b: 4 }, + // change a + { a: 2, b: 4 }, + // change a + { a: 3, b: 4 }, + ], +}; + ``` ## Code @@ -24,44 +42,56 @@ import { c as _c } from "react/compiler-runtime"; // props.b + 1 is an non-alloc // emit it trivially and repeatedly (e.g. no need to memoize props.b + 1 // separately from props.b) // Correctness: + +import { identity, mutate, setProperty } from "shared-runtime"; + // y depends on either props.b or props.b + 1 function PrimitiveAsDepNested(props) { - const $ = _c(9); - let x; - let y; + const $ = _c(5); + let t0; if ($[0] !== props.b || $[1] !== props.a) { - x = {}; + const x = {}; mutate(x); - const t0 = props.b + 1; - let t1; - if ($[4] !== t0) { - t1 = foo(t0); - $[4] = t0; - $[5] = t1; + const t1 = props.b + 1; + let t2; + if ($[3] !== t1) { + t2 = identity(t1); + $[3] = t1; + $[4] = t2; } else { - t1 = $[5]; + t2 = $[4]; } - y = t1; - mutate(x, props.a); + const y = t2; + setProperty(x, props.a); + t0 = [x, y]; $[0] = props.b; $[1] = props.a; - $[2] = x; - $[3] = y; + $[2] = t0; } else { - x = $[2]; - y = $[3]; - } - let t0; - if ($[6] !== x || $[7] !== y) { - t0 = [x, y]; - $[6] = x; - $[7] = y; - $[8] = t0; - } else { - t0 = $[8]; + t0 = $[2]; } return t0; } +export const FIXTURE_ENTRYPOINT = { + fn: PrimitiveAsDepNested, + params: [{ a: 1, b: 2 }], + sequentialRenders: [ + // change b + { a: 1, b: 3 }, + // change b + { a: 1, b: 4 }, + // change a + { a: 2, b: 4 }, + // change a + { a: 3, b: 4 }, + ], +}; + ``` - \ No newline at end of file + +### Eval output +(kind: ok) [{"wat0":"joe","wat1":1},4] +[{"wat0":"joe","wat1":1},5] +[{"wat0":"joe","wat1":2},5] +[{"wat0":"joe","wat1":3},5] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/primitive-as-dep-nested-scope.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/primitive-as-dep-nested-scope.js index 19299e9d6e..5eda8d42cd 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/primitive-as-dep-nested-scope.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/primitive-as-dep-nested-scope.js @@ -2,11 +2,29 @@ // emit it trivially and repeatedly (e.g. no need to memoize props.b + 1 // separately from props.b) // Correctness: + +import { identity, mutate, setProperty } from "shared-runtime"; + // y depends on either props.b or props.b + 1 function PrimitiveAsDepNested(props) { let x = {}; mutate(x); - let y = foo(props.b + 1); - mutate(x, props.a); + let y = identity(props.b + 1); + setProperty(x, props.a); return [x, y]; } + +export const FIXTURE_ENTRYPOINT = { + fn: PrimitiveAsDepNested, + params: [{ a: 1, b: 2 }], + sequentialRenders: [ + // change b + { a: 1, b: 3 }, + // change b + { a: 1, b: 4 }, + // change a + { a: 2, b: 4 }, + // change a + { a: 3, b: 4 }, + ], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-dependency-object-captured-with-reactive-mutated.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-dependency-object-captured-with-reactive-mutated.expect.md index de22d5f653..140ca28a72 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-dependency-object-captured-with-reactive-mutated.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-dependency-object-captured-with-reactive-mutated.expect.md @@ -27,25 +27,19 @@ import { c as _c } from "react/compiler-runtime"; const { mutate } = require("shared-runtime"); function Component(props) { - const $ = _c(4); - let x; + const $ = _c(2); + let t0; if ($[0] !== props.y) { - x = {}; + const x = {}; const y = props.y; const z = [x, y]; mutate(z); - $[0] = props.y; - $[1] = x; - } else { - x = $[1]; - } - let t0; - if ($[2] !== x) { + t0 = [x]; - $[2] = x; - $[3] = t0; + $[0] = props.y; + $[1] = t0; } else { - t0 = $[3]; + t0 = $[1]; } return t0; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-scopes.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-scopes.expect.md index 03cd78c2b0..f38f4c8334 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-scopes.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-scopes.expect.md @@ -26,28 +26,22 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function f(a, b) { - const $ = _c(5); - let x; + const $ = _c(3); + let t0; if ($[0] !== a.length || $[1] !== b) { - x = []; + const x = []; if (a.length === 1) { if (b) { x.push(b); } } + + t0 =
{x}
; $[0] = a.length; $[1] = b; - $[2] = x; + $[2] = t0; } else { - x = $[2]; - } - let t0; - if ($[3] !== x) { - t0 =
{x}
; - $[3] = x; - $[4] = t0; - } else { - t0 = $[4]; + t0 = $[2]; } return t0; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactivity-analysis-interleaved-reactivity.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactivity-analysis-interleaved-reactivity.expect.md index 7254472896..d11f054a34 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactivity-analysis-interleaved-reactivity.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactivity-analysis-interleaved-reactivity.expect.md @@ -35,35 +35,32 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function Component(props) { - const $ = _c(7); + const $ = _c(6); let a; + let t0; if ($[0] !== props.b) { a = {}; const b = []; b.push(props.b); a.a = null; + + t0 = [a]; $[0] = props.b; $[1] = a; + $[2] = t0; } else { a = $[1]; - } - let t0; - if ($[2] !== a) { - t0 = [a]; - $[2] = a; - $[3] = t0; - } else { - t0 = $[3]; + t0 = $[2]; } const c = t0; let t1; - if ($[4] !== c || $[5] !== a) { + if ($[3] !== c || $[4] !== a) { t1 = [c, a]; - $[4] = c; - $[5] = a; - $[6] = t1; + $[3] = c; + $[4] = a; + $[5] = t1; } else { - t1 = $[6]; + t1 = $[5]; } return t1; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reassignment-separate-scopes.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reassignment-separate-scopes.expect.md index d8093ccab4..f42b92b596 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reassignment-separate-scopes.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reassignment-separate-scopes.expect.md @@ -41,63 +41,60 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function foo(a, b, c) { - const $ = _c(11); + const $ = _c(10); let x; + let t0; if ($[0] !== a) { x = []; if (a) { x.push(a); } + + t0 =
{x}
; $[0] = a; $[1] = x; + $[2] = t0; } else { x = $[1]; - } - let t0; - if ($[2] !== x) { - t0 =
{x}
; - $[2] = x; - $[3] = t0; - } else { - t0 = $[3]; + t0 = $[2]; } const y = t0; bb0: switch (b) { case 0: { - if ($[4] !== b) { + if ($[3] !== b) { x = []; x.push(b); - $[4] = b; - $[5] = x; + $[3] = b; + $[4] = x; } else { - x = $[5]; + x = $[4]; } break bb0; } default: { - if ($[6] !== c) { + if ($[5] !== c) { x = []; x.push(c); - $[6] = c; - $[7] = x; + $[5] = c; + $[6] = x; } else { - x = $[7]; + x = $[6]; } } } let t1; - if ($[8] !== y || $[9] !== x) { + if ($[7] !== y || $[8] !== x) { t1 = (
{y} {x}
); - $[8] = y; - $[9] = x; - $[10] = t1; + $[7] = y; + $[8] = x; + $[9] = t1; } else { - t1 = $[10]; + t1 = $[9]; } return t1; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/join-uncond-scopes-cond-deps.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/join-uncond-scopes-cond-deps.expect.md index 3b371cabec..9657c47fc9 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/join-uncond-scopes-cond-deps.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/join-uncond-scopes-cond-deps.expect.md @@ -61,38 +61,28 @@ import { c as _c } from "react/compiler-runtime"; // This tests an optimization, import { CONST_TRUE, setProperty } from "shared-runtime"; function useJoinCondDepsInUncondScopes(props) { - const $ = _c(8); - let x; - let y; + const $ = _c(4); + let t0; if ($[0] !== props.a.b) { - y = {}; - if ($[3] !== props) { + const y = {}; + let x; + if ($[2] !== props) { x = {}; if (CONST_TRUE) { setProperty(x, props.a.b); } - $[3] = props; - $[4] = x; + $[2] = props; + $[3] = x; } else { - x = $[4]; + x = $[3]; } setProperty(y, props.a.b); - $[0] = props.a.b; - $[1] = x; - $[2] = y; - } else { - x = $[1]; - y = $[2]; - } - let t0; - if ($[5] !== x || $[6] !== y) { t0 = [x, y]; - $[5] = x; - $[6] = y; - $[7] = t0; + $[0] = props.a.b; + $[1] = t0; } else { - t0 = $[7]; + t0 = $[1]; } return t0; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-missing-dependency-if-within-while.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-missing-dependency-if-within-while.expect.md index 00b4165007..6438a28fdb 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-missing-dependency-if-within-while.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-missing-dependency-if-within-while.expect.md @@ -39,11 +39,11 @@ export const FIXTURE_ENTRYPOINT = { import { c as _c } from "react/compiler-runtime"; const someGlobal = true; export default function Component(props) { - const $ = _c(4); + const $ = _c(2); const { b } = props; - let items; + let t0; if ($[0] !== b) { - items = []; + const items = []; let i = 0; while (i < 10) { if (someGlobal) { @@ -51,18 +51,12 @@ export default function Component(props) { i++; } } - $[0] = b; - $[1] = items; - } else { - items = $[1]; - } - let t0; - if ($[2] !== items) { + t0 = <>{items}; - $[2] = items; - $[3] = t0; + $[0] = b; + $[1] = t0; } else { - t0 = $[3]; + t0 = $[1]; } return t0; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-renaming-conflicting-decls.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-renaming-conflicting-decls.expect.md index 6f90f0d141..686752f9fd 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-renaming-conflicting-decls.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-renaming-conflicting-decls.expect.md @@ -46,7 +46,7 @@ import { Stringify, identity, makeArray, toJSON } from "shared-runtime"; import { useMemo } from "react"; function Component(props) { - const $ = _c(29); + const $ = _c(13); let t0; let t1; let t2; @@ -83,87 +83,50 @@ function Component(props) { t3 = $[5]; } const linkProps = t3; - let T0; let t4; - let t5; - let t6; - let t7; - let t8; - let t9; - let t10; if ($[6] !== linkProps) { const x = {}; - - T0 = Stringify; - t4 = linkProps; - if ($[15] === Symbol.for("react.memo_cache_sentinel")) { + let t5; + let t6; + let t7; + let t8; + let t9; + if ($[8] === Symbol.for("react.memo_cache_sentinel")) { t5 = [1]; t6 = [2]; t7 = [3]; t8 = [4]; t9 = [5]; - $[15] = t5; - $[16] = t6; - $[17] = t7; - $[18] = t8; - $[19] = t9; + $[8] = t5; + $[9] = t6; + $[10] = t7; + $[11] = t8; + $[12] = t9; } else { - t5 = $[15]; - t6 = $[16]; - t7 = $[17]; - t8 = $[18]; - t9 = $[19]; + t5 = $[8]; + t6 = $[9]; + t7 = $[10]; + t8 = $[11]; + t9 = $[12]; } - - t10 = makeArray(x, 2); - $[6] = linkProps; - $[7] = T0; - $[8] = t4; - $[9] = t5; - $[10] = t6; - $[11] = t7; - $[12] = t8; - $[13] = t9; - $[14] = t10; - } else { - T0 = $[7]; - t4 = $[8]; - t5 = $[9]; - t6 = $[10]; - t7 = $[11]; - t8 = $[12]; - t9 = $[13]; - t10 = $[14]; - } - let t11; - if ( - $[20] !== T0 || - $[21] !== t4 || - $[22] !== t5 || - $[23] !== t6 || - $[24] !== t7 || - $[25] !== t8 || - $[26] !== t9 || - $[27] !== t10 - ) { - t11 = ( - - {t10} - + t4 = ( + + {makeArray(x, 2)} + ); - $[20] = T0; - $[21] = t4; - $[22] = t5; - $[23] = t6; - $[24] = t7; - $[25] = t8; - $[26] = t9; - $[27] = t10; - $[28] = t11; + $[6] = linkProps; + $[7] = t4; } else { - t11 = $[28]; + t4 = $[7]; } - return t11; + return t4; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/same-variable-as-dep-and-redeclare.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/same-variable-as-dep-and-redeclare.expect.md index 9674bd80e0..e53b5ce842 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/same-variable-as-dep-and-redeclare.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/same-variable-as-dep-and-redeclare.expect.md @@ -52,68 +52,65 @@ export const FIXTURE_ENTRYPOINT = { import { c as _c } from "react/compiler-runtime"; // note: comments are for the ideal scopes, not what is currently // emitted function foo(props) { - const $ = _c(15); + const $ = _c(14); let x; + let t0; if ($[0] !== props.a) { x = []; x.push(props.a); + + t0 =
{x}
; $[0] = props.a; $[1] = x; + $[2] = t0; } else { x = $[1]; - } - let t0; - if ($[2] !== x) { - t0 =
{x}
; - $[2] = x; - $[3] = t0; - } else { - t0 = $[3]; + t0 = $[2]; } const header = t0; let y; - if ($[4] !== x || $[5] !== props.b || $[6] !== props.c) { + if ($[3] !== x || $[4] !== props.b || $[5] !== props.c) { y = [x]; x = []; y.push(props.b); x.push(props.c); - $[4] = x; - $[5] = props.b; - $[6] = props.c; - $[7] = y; - $[8] = x; + $[3] = x; + $[4] = props.b; + $[5] = props.c; + $[6] = y; + $[7] = x; } else { - y = $[7]; - x = $[8]; + y = $[6]; + x = $[7]; } let t1; - if ($[9] !== x || $[10] !== y) { + if ($[8] !== x || $[9] !== y) { t1 = (
{x} {y}
); - $[9] = x; - $[10] = y; - $[11] = t1; + $[8] = x; + $[9] = y; + $[10] = t1; } else { - t1 = $[11]; + t1 = $[10]; } const content = t1; let t2; - if ($[12] !== header || $[13] !== content) { + if ($[11] !== header || $[12] !== content) { t2 = ( <> {header} {content} ); - $[12] = header; - $[13] = content; - $[14] = t2; + $[11] = header; + $[12] = content; + $[13] = t2; } else { - t2 = $[14]; + t2 = $[13]; } return t2; } diff --git a/compiler/packages/snap/src/SproutTodoFilter.ts b/compiler/packages/snap/src/SproutTodoFilter.ts index d026cc2943..25ac01cdd5 100644 --- a/compiler/packages/snap/src/SproutTodoFilter.ts +++ b/compiler/packages/snap/src/SproutTodoFilter.ts @@ -220,7 +220,6 @@ const skipFilter = new Set([ * Tests with one or more params, with external references. */ "alias-computed-load", - "allocating-primitive-as-dep-nested-scope", "allocating-primitive-as-dep", "allow-passing-refs-as-props", "array-at-closure", @@ -309,7 +308,6 @@ const skipFilter = new Set([ "optional-receiver-method-call", "optional-receiver-optional-method", "primitive-alias-mutate", - "primitive-as-dep-nested-scope", "primitive-as-dep", "property-assignment", "property-call-spread", diff --git a/compiler/packages/snap/src/sprout/index.ts b/compiler/packages/snap/src/sprout/index.ts index 2e275e45d9..b3adad346a 100644 --- a/compiler/packages/snap/src/sprout/index.ts +++ b/compiler/packages/snap/src/sprout/index.ts @@ -32,33 +32,35 @@ export function runSprout( originalCode: string, forgetCode: string ): SproutResult { - const nonForgetResult = doEval(originalCode); const forgetResult = doEval(forgetCode); - if (forgetResult.kind === "UnexpectedError") { return makeError("Unexpected error in Forget runner", forgetResult.value); - } else if (nonForgetResult.kind === "UnexpectedError") { - return makeError( - "Unexpected error in non-forget runner", - nonForgetResult.value - ); - } else if ( - forgetResult.kind !== nonForgetResult.kind || - forgetResult.value !== nonForgetResult.value || - !logsEqual(forgetResult.logs, nonForgetResult.logs) - ) { - return makeError( - "Found differences in evaluator results", - `Non-forget (expected): + } + if (originalCode.indexOf("@disableNonForgetInSprout") === -1) { + const nonForgetResult = doEval(originalCode); + + if (nonForgetResult.kind === "UnexpectedError") { + return makeError( + "Unexpected error in non-forget runner", + nonForgetResult.value + ); + } else if ( + forgetResult.kind !== nonForgetResult.kind || + forgetResult.value !== nonForgetResult.value || + !logsEqual(forgetResult.logs, nonForgetResult.logs) + ) { + return makeError( + "Found differences in evaluator results", + `Non-forget (expected): ${stringify(nonForgetResult)} Forget: ${stringify(forgetResult)} ` - ); - } else { - return { - kind: "success", - value: stringify(forgetResult), - }; + ); + } } + return { + kind: "success", + value: stringify(forgetResult), + }; }