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 f9ef2f644db11..149ba34bdf651 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 7327fe7231e0f..e15820b053343 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; - } else { - x = $[2]; - y = $[3]; - } - let t0; - if ($[6] !== x || $[7] !== y) { - t0 = [x, y]; - $[6] = x; - $[7] = y; - $[8] = t0; + $[2] = 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 765c7ef79f0ee..896680bb25682 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 011436bd4288f..92da9b6684475 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 7efd9ca69da3e..3904dcf228553 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 61d87440bde14..ba9e39e691b0e 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/for-in-statement.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/for-in-statement.expect.md index de6b7d89714c1..77a34e409a3b9 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 cb2c83bf16411..520d900cb4b0d 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 4586306128eb8..d9a1fd799e08b 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; - } else { - items = $[2]; - } - let t0; - if ($[3] !== items) { - t0 =
{items}
; - $[3] = items; - $[4] = t0; + $[2] = 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 b966dc60d13d5..7ccad19179475 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 7beca60780799..ee7aa62495664 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 052e1a274ad36..1cbf1e0ab28c1 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 f5aa0b4dd73ba..b6ccab92d58b4 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; - } else { - style = $[3]; - x = $[4]; - } - let t0; - if ($[8] !== style || $[9] !== x) { - t0 = [style, x]; - $[8] = style; - $[9] = x; - $[10] = t0; + $[3] = 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 c83f9d4a5041a..6458e9812ac8a 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; - } else { - style = $[3]; - x = $[4]; - } - let t0; - if ($[7] !== style || $[8] !== x) { - t0 = [style, x]; - $[7] = style; - $[8] = x; - $[9] = t0; + $[3] = 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 324cc66af6206..9ac2be35d9312 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; - } else { - x = $[2]; - y = $[3]; - } - let t0; - if ($[6] !== x || $[7] !== y) { - t0 = [x, y]; - $[6] = x; - $[7] = y; - $[8] = t0; + $[2] = 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 19299e9d6e4ce..5eda8d42cd8bc 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 de22d5f653c84..140ca28a72aa2 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 03cd78c2b0f96..f38f4c8334251 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; - } else { - x = $[2]; - } - let t0; - if ($[3] !== x) { - t0 =
{x}
; - $[3] = x; - $[4] = t0; + $[2] = 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 72544728966bb..d11f054a34195 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 d8093ccab414f..f42b92b596a8a 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 3b371cabec135..9657c47fc98b4 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 00b4165007d33..6438a28fdb4d9 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 6f90f0d14143b..686752f9fddd6 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 9674bd80e0089..e53b5ce842d9b 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 d026cc294318f..25ac01cdd5376 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",