From 42e10399eb80937e4fcd596b71ca90879f3940b8 Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Fri, 6 Jun 2025 17:51:24 -0700 Subject: [PATCH] [compiler][newinference] Fixes, update remaining snapshots A bunch of small fixes to make the remaining fixtures work. This is really really close now. ghstack-source-id: a3d6803eb4ea9cf6512c8d716ded88da9d0b90c3 Pull Request resolved: https://github.com/facebook/react/pull/33477 --- .../src/Entrypoint/Pipeline.ts | 2 +- .../Inference/InferMutationAliasingEffects.ts | 63 +++++++-- .../Inference/InferMutationAliasingRanges.ts | 18 ++- .../src/Inference/InferReferenceEffects.ts | 2 +- ...ValidateNoFreezingKnownMutableFunctions.ts | 5 +- ...iased-nested-scope-truncated-dep.expect.md | 13 +- ...ay-map-captures-receiver-noAlias.expect.md | 26 +++- ...-func-maybealias-captured-mutate.expect.md | 22 +-- .../bug-invalid-phi-as-dependency.expect.md | 22 +-- ...nstruction-hoisted-sequence-expr.expect.md | 46 ++----- ...zation-due-to-callback-capturing.expect.md | 104 +++++--------- ...ction-alias-computed-load-2-iife.expect.md | 20 ++- ...ction-alias-computed-load-3-iife.expect.md | 23 +++- ...ction-alias-computed-load-4-iife.expect.md | 20 ++- ...unction-alias-computed-load-iife.expect.md | 20 ++- ...d-reanimated-shared-value-writes.expect.md | 2 +- ...ssign-local-in-hook-return-value.expect.md | 11 +- ...eassign-local-variable-in-effect.expect.md | 20 +-- ...-local-variable-in-hook-argument.expect.md | 20 +-- ...n-local-variable-in-jsx-callback.expect.md | 15 +- ...ange-shared-inner-outer-function.expect.md | 16 +-- ...ences-later-variable-declaration.expect.md | 11 +- .../compiler/hoisting-setstate.expect.md | 77 ----------- .../reactive-setState.expect.md | 22 +-- ...map-named-callback-cross-context.expect.md | 81 +++++------ .../jsx-captures-context-variable.expect.md | 129 ------------------ ...n-local-variable-in-jsx-callback.expect.md | 15 +- ...k-reordering-deplist-controlflow.expect.md | 53 +++---- ...k-reordering-depslist-assignment.expect.md | 41 +++--- ...o-reordering-depslist-assignment.expect.md | 47 ++++--- ...es-memoizes-with-captures-values.expect.md | 77 ----------- .../shared-hook-calls.expect.md | 81 ++++++----- 32 files changed, 433 insertions(+), 691 deletions(-) delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-setstate.expect.md delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-captures-context-variable.expect.md delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-uncalled-function-capturing-mutable-values-memoizes-with-captures-values.expect.md diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts index b73988c604..f3e21e0def 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts @@ -247,7 +247,6 @@ function runWithEnvironment( } if (!env.config.enableNewMutationAliasingModel) { - // NOTE: in the new model this is part of validateNoFreezingKnownMutableFunctions validateLocalsNotReassignedAfterRender(hir); } @@ -275,6 +274,7 @@ function runWithEnvironment( if (mutabilityAliasingErrors.isErr()) { throw mutabilityAliasingErrors.unwrapErr(); } + validateLocalsNotReassignedAfterRender(hir); } } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingEffects.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingEffects.ts index 2ed4498193..f7bc2c33f1 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingEffects.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingEffects.ts @@ -43,6 +43,7 @@ import { } from '../HIR/visitors'; import {Ok, Result} from '../Utils/Result'; import { + getArgumentEffect, getFunctionCallSignature, isKnownMutableEffect, mergeValueKinds, @@ -547,7 +548,11 @@ function applyEffect( effect => effect.kind === 'MutateFrozen' || effect.kind === 'MutateGlobal', ); - const isMutable = hasCaptures || hasTrackedSideEffects; + // For legacy compatibility + const capturesRef = effect.function.loweredFunc.func.context.some( + operand => isRefOrRefValue(operand.identifier), + ); + const isMutable = hasCaptures || hasTrackedSideEffects || capturesRef; for (const operand of effect.function.loweredFunc.func.context) { if (operand.effect !== Effect.Capture) { continue; @@ -805,6 +810,11 @@ function applyEffect( effects, ); } + const mutateIterator = + arg.kind === 'Spread' ? conditionallyMutateIterator(operand) : null; + if (mutateIterator) { + applyEffect(context, state, mutateIterator, aliased, effects); + } applyEffect( context, state, @@ -1299,6 +1309,22 @@ type InstructionSignature = { effects: ReadonlyArray; }; +function conditionallyMutateIterator(place: Place): AliasingEffect | null { + if ( + !( + isArrayType(place.identifier) || + isSetType(place.identifier) || + isMapType(place.identifier) + ) + ) { + return { + kind: 'MutateTransitiveConditionally', + value: place, + }; + } + return null; +} + /** * Computes an effect signature for the instruction _without_ looking at the inference state, * and only using the semantics of the instructions and the inferred types. The idea is to make @@ -1334,6 +1360,10 @@ function computeSignatureForInstruction( into: lvalue, }); } else if (element.kind === 'Spread') { + const mutateIterator = conditionallyMutateIterator(element.place); + if (mutateIterator != null) { + effects.push(mutateIterator); + } effects.push({ kind: 'Capture', from: element.place, @@ -1567,7 +1597,7 @@ function computeSignatureForInstruction( // Extracts part of the original collection into the result effects.push({ kind: 'CreateFrom', - from: value.iterator, + from: value.collection, into: lvalue, }); break; @@ -1623,11 +1653,20 @@ function computeSignatureForInstruction( } case 'Destructure': { for (const patternLValue of eachInstructionValueLValue(value)) { - effects.push({ - kind: 'CreateFrom', - from: value.value, - into: patternLValue, - }); + if (isPrimitiveType(patternLValue.identifier)) { + effects.push({ + kind: 'Create', + into: patternLValue, + value: ValueKind.Primitive, + reason: ValueReason.Other, + }); + } else { + effects.push({ + kind: 'CreateFrom', + from: value.value, + into: patternLValue, + }); + } } effects.push({kind: 'Assign', from: value.value, into: lvalue}); break; @@ -1947,17 +1986,11 @@ function computeEffectsForLegacySignature( continue; } const place = arg.kind === 'Identifier' ? arg : arg.place; - const effect = + const signatureEffect = arg.kind === 'Identifier' && i < signature.positionalParams.length ? signature.positionalParams[i]! : (signature.restParam ?? Effect.ConditionallyMutate); - - if (arg.kind === 'Spread' && effect === Effect.Freeze) { - CompilerError.throwTodo({ - reason: 'Support spread syntax for hook arguments', - loc: arg.place.loc, - }); - } + const effect = getArgumentEffect(signatureEffect, arg); visit(place, effect); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingRanges.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingRanges.ts index da6f7036e6..3fd29297e5 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingRanges.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingRanges.ts @@ -121,7 +121,12 @@ export function inferMutationAliasingRanges( }); } else if (effect.kind === 'CreateFrom') { state.createFrom(index++, effect.from, effect.into); - } else if (effect.kind === 'Assign' || effect.kind === 'Alias') { + } else if (effect.kind === 'Assign') { + if (!state.nodes.has(effect.into.identifier)) { + state.create(effect.into, {kind: 'Object'}); + } + state.assign(index++, effect.from, effect.into); + } else if (effect.kind === 'Alias') { state.assign(index++, effect.from, effect.into); } else if (effect.kind === 'Capture') { state.capture(index++, effect.from, effect.into); @@ -204,8 +209,8 @@ export function inferMutationAliasingRanges( errors, ); } - if (VERBOSE) { - console.log(state.debug()); + if (DEBUG) { + console.log(pretty([...state.nodes.keys()])); } fn.aliasingEffects ??= []; for (const param of [...fn.context, ...fn.params]) { @@ -534,6 +539,11 @@ class AliasingState { loc: SourceLocation, errors: CompilerError, ): void { + if (DEBUG) { + console.log( + `mutate ix=${index} start=$${start.id} end=[${end}]${transitive ? ' transitive' : ''} kind=${kind}`, + ); + } const seen = new Set(); const queue: Array<{ place: Identifier; @@ -557,7 +567,7 @@ class AliasingState { } if (DEBUG) { console.log( - `[${end}] mutate index=${index} ${printIdentifier(start)}: ${printIdentifier(node.id)}`, + ` mutate $${node.id.id} transitive=${transitive} direction=${direction}`, ); } node.id.mutableRange.end = makeInstructionId( diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts index 3cb9999300..a202789583 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts @@ -1984,7 +1984,7 @@ function areArgumentsImmutableAndNonMutating( return true; } -function getArgumentEffect( +export function getArgumentEffect( signatureEffect: Effect | null, arg: Place | SpreadPattern, ): Effect { diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoFreezingKnownMutableFunctions.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoFreezingKnownMutableFunctions.ts index eb61d81a8b..573db2f6b7 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoFreezingKnownMutableFunctions.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoFreezingKnownMutableFunctions.ts @@ -131,7 +131,10 @@ export function validateNoFreezingKnownMutableFunctions( lvalue.identifier.id, knownMutation, ); - } else if (context.has(effect.value.identifier.id)) { + } else if ( + context.has(effect.value.identifier.id) && + !isRefOrRefLikeMutableType(effect.value.identifier.type) + ) { contextMutationEffects.set(lvalue.identifier.id, { kind: 'ContextMutation', effect: Effect.Mutate, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/aliased-nested-scope-truncated-dep.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/aliased-nested-scope-truncated-dep.expect.md index 933fafff5f..12c7b4d5ea 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/aliased-nested-scope-truncated-dep.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/aliased-nested-scope-truncated-dep.expect.md @@ -175,21 +175,14 @@ import { * and mutability. */ function Component(t0) { - const $ = _c(4); + const $ = _c(2); const { prop } = t0; let t1; if ($[0] !== prop) { const obj = shallowCopy(prop); const aliasedObj = identity(obj); - let t2; - if ($[2] !== obj) { - t2 = [obj.id]; - $[2] = obj; - $[3] = t2; - } else { - t2 = $[3]; - } - const id = t2; + + const id = [obj.id]; mutate(aliasedObj); setPropertyByKey(aliasedObj, "id", prop.id + 1); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-captures-receiver-noAlias.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-captures-receiver-noAlias.expect.md index efd094c1a5..1680386c74 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-captures-receiver-noAlias.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-captures-receiver-noAlias.expect.md @@ -23,18 +23,34 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function Component(props) { - const $ = _c(2); + const $ = _c(6); let t0; if ($[0] !== props.a) { - const item = { a: props.a }; - const items = [item]; - t0 = items.map(_temp); + t0 = { a: props.a }; $[0] = props.a; $[1] = t0; } else { t0 = $[1]; } - const mapped = t0; + const item = t0; + let t1; + if ($[2] !== item) { + t1 = [item]; + $[2] = item; + $[3] = t1; + } else { + t1 = $[3]; + } + const items = t1; + let t2; + if ($[4] !== items) { + t2 = items.map(_temp); + $[4] = items; + $[5] = t2; + } else { + t2 = $[5]; + } + const mapped = t2; return mapped; } function _temp(item_0) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-capturing-func-maybealias-captured-mutate.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-capturing-func-maybealias-captured-mutate.expect.md index b8c7f8d422..5c769295a7 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-capturing-func-maybealias-captured-mutate.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-capturing-func-maybealias-captured-mutate.expect.md @@ -84,19 +84,11 @@ import { makeArray, mutate } from "shared-runtime"; * used when we analyze CallExpressions. */ function Component(t0) { - const $ = _c(5); + const $ = _c(3); const { foo, bar } = t0; - let t1; - if ($[0] !== foo) { - t1 = { foo }; - $[0] = foo; - $[1] = t1; - } else { - t1 = $[1]; - } - const x = t1; let y; - if ($[2] !== bar || $[3] !== x) { + if ($[0] !== bar || $[1] !== foo) { + const x = { foo }; y = { bar }; const f0 = function () { const a = makeArray(y); @@ -107,11 +99,11 @@ function Component(t0) { f0(); mutate(y.x); - $[2] = bar; - $[3] = x; - $[4] = y; + $[0] = bar; + $[1] = foo; + $[2] = y; } else { - y = $[4]; + y = $[2]; } return y; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-invalid-phi-as-dependency.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-invalid-phi-as-dependency.expect.md index 09d2d8800b..c004430bdd 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-invalid-phi-as-dependency.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-invalid-phi-as-dependency.expect.md @@ -38,7 +38,6 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; import { CONST_TRUE, Stringify, mutate, useIdentity } from "shared-runtime"; /** @@ -54,31 +53,14 @@ import { CONST_TRUE, Stringify, mutate, useIdentity } from "shared-runtime"; * */ function Component() { - const $ = _c(4); const obj = CONST_TRUE ? { inner: { value: "hello" } } : null; - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = [obj?.inner]; - $[0] = t0; - } else { - t0 = $[0]; - } - const boxedInner = t0; + const boxedInner = [obj?.inner]; useIdentity(null); mutate(obj); if (boxedInner[0] !== obj?.inner) { throw new Error("invariant broken"); } - let t1; - if ($[1] !== boxedInner || $[2] !== obj) { - t1 = ; - $[1] = boxedInner; - $[2] = obj; - $[3] = t1; - } else { - t1 = $[3]; - } - return t1; + return ; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-object-expression-computed-key-modified-during-after-construction-hoisted-sequence-expr.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-object-expression-computed-key-modified-during-after-construction-hoisted-sequence-expr.expect.md index 4ffe0fcb6a..134c5918af 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-object-expression-computed-key-modified-during-after-construction-hoisted-sequence-expr.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-object-expression-computed-key-modified-during-after-construction-hoisted-sequence-expr.expect.md @@ -56,47 +56,21 @@ import { identity, mutate } from "shared-runtime"; * [{"[object Object]":[42]},{"wat0":"joe","wat1":"joe","wat2":"joe"}] */ function Component(props) { - const $ = _c(8); - let key; + const $ = _c(2); let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - key = {}; - t0 = (mutate(key), key); - $[0] = key; + if ($[0] !== props.value) { + const key = {}; + const tmp = (mutate(key), key); + const context = { [tmp]: identity([props.value]) }; + + mutate(key); + t0 = [context, key]; + $[0] = props.value; $[1] = t0; } else { - key = $[0]; t0 = $[1]; } - const tmp = t0; - let t1; - if ($[2] !== props.value) { - t1 = identity([props.value]); - $[2] = props.value; - $[3] = t1; - } else { - t1 = $[3]; - } - let t2; - if ($[4] !== t1) { - t2 = { [tmp]: t1 }; - $[4] = t1; - $[5] = t2; - } else { - t2 = $[5]; - } - const context = t2; - - mutate(key); - let t3; - if ($[6] !== context) { - t3 = [context, key]; - $[6] = context; - $[7] = t3; - } else { - t3 = $[7]; - } - return t3; + return t0; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-separate-memoization-due-to-callback-capturing.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-separate-memoization-due-to-callback-capturing.expect.md index 20c128b3ec..f57dbe4971 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-separate-memoization-due-to-callback-capturing.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-separate-memoization-due-to-callback-capturing.expect.md @@ -66,88 +66,54 @@ const Codes = { }; function Component(a) { - const $ = _c(13); + const $ = _c(4); let keys; - let t0; - let t1; - if ($[0] !== a) { - t1 = Symbol.for("react.early_return_sentinel"); - bb0: { - if (a) { - keys = Object.keys(Codes); - } else { - t1 = null; - break bb0; - } - - t0 = keys.map(_temp); + if (a) { + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = Object.keys(Codes); + $[0] = t0; + } else { + t0 = $[0]; } - $[0] = a; + keys = t0; + } else { + return null; + } + let t0; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t0 = keys.map(_temp); $[1] = t0; - $[2] = t1; - $[3] = keys; } else { t0 = $[1]; - t1 = $[2]; - keys = $[3]; - } - if (t1 !== Symbol.for("react.early_return_sentinel")) { - return t1; } const options = t0; + let t1; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t1 = ( + + ); + $[2] = t1; + } else { + t1 = $[2]; + } let t2; - if ($[4] === Symbol.for("react.memo_cache_sentinel")) { - t2 = []; - $[4] = t2; - } else { - t2 = $[4]; - } - let t3; - if ($[5] !== keys) { - t3 = ( - - ); - $[5] = keys; - $[6] = t3; - } else { - t3 = $[6]; - } - let t4; - if ($[7] === Symbol.for("react.memo_cache_sentinel")) { - t4 = []; - $[7] = t4; - } else { - t4 = $[7]; - } - let t5; - if ($[8] !== options) { - t5 = ( - - ); - $[8] = options; - $[9] = t5; - } else { - t5 = $[9]; - } - let t6; - if ($[10] !== t3 || $[11] !== t5) { - t6 = ( + if ($[3] === Symbol.for("react.memo_cache_sentinel")) { + t2 = ( <> - {t3} - {t5} + {t1} + ); - $[10] = t3; - $[11] = t5; - $[12] = t6; + $[3] = t2; } else { - t6 = $[12]; + t2 = $[3]; } - return t6; + return t2; } function _temp(code) { const country = Codes[code]; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-2-iife.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-2-iife.expect.md index 2afc5fd25d..50480f1b25 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-2-iife.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-2-iife.expect.md @@ -25,17 +25,25 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function bar(a) { - const $ = _c(2); - let y; + const $ = _c(4); + let t0; if ($[0] !== a) { - const x = [a]; + t0 = [a]; + $[0] = a; + $[1] = t0; + } else { + t0 = $[1]; + } + const x = t0; + let y; + if ($[2] !== x[0][1]) { y = {}; y = x[0][1]; - $[0] = a; - $[1] = y; + $[2] = x[0][1]; + $[3] = y; } else { - y = $[1]; + y = $[3]; } return y; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-3-iife.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-3-iife.expect.md index f0267c3309..9678918b3d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-3-iife.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-3-iife.expect.md @@ -29,20 +29,29 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function bar(a, b) { - const $ = _c(3); - let y; + const $ = _c(6); + let t0; if ($[0] !== a || $[1] !== b) { - const x = [a, b]; + t0 = [a, b]; + $[0] = a; + $[1] = b; + $[2] = t0; + } else { + t0 = $[2]; + } + const x = t0; + let y; + if ($[3] !== x[0][1] || $[4] !== x[1][0]) { y = {}; let t = {}; y = x[0][1]; t = x[1][0]; - $[0] = a; - $[1] = b; - $[2] = y; + $[3] = x[0][1]; + $[4] = x[1][0]; + $[5] = y; } else { - y = $[2]; + y = $[5]; } return y; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-4-iife.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-4-iife.expect.md index 22728aaf43..edddf3715a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-4-iife.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-4-iife.expect.md @@ -25,17 +25,25 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function bar(a) { - const $ = _c(2); - let y; + const $ = _c(4); + let t0; if ($[0] !== a) { - const x = [a]; + t0 = [a]; + $[0] = a; + $[1] = t0; + } else { + t0 = $[1]; + } + const x = t0; + let y; + if ($[2] !== x[0].a[1]) { y = {}; y = x[0].a[1]; - $[0] = a; - $[1] = y; + $[2] = x[0].a[1]; + $[3] = y; } else { - y = $[1]; + y = $[3]; } return y; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-iife.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-iife.expect.md index 60f829cdc4..c9ce6dda9f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-iife.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-iife.expect.md @@ -24,17 +24,25 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function bar(a) { - const $ = _c(2); - let y; + const $ = _c(4); + let t0; if ($[0] !== a) { - const x = [a]; + t0 = [a]; + $[0] = a; + $[1] = t0; + } else { + t0 = $[1]; + } + const x = t0; + let y; + if ($[2] !== x[0]) { y = {}; y = x[0]; - $[0] = a; - $[1] = y; + $[2] = x[0]; + $[3] = y; } else { - y = $[1]; + y = $[3]; } return y; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-non-imported-reanimated-shared-value-writes.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-non-imported-reanimated-shared-value-writes.expect.md index f1399a41b6..d3bb7f4136 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-non-imported-reanimated-shared-value-writes.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-non-imported-reanimated-shared-value-writes.expect.md @@ -27,7 +27,7 @@ function SomeComponent() { 9 | return ( 10 | ; - | ^^^^^^^ InvalidReact: This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead (31:31) - -InvalidReact: The function modifies a local variable here (5:5) - 32 | } - 33 | + 3 | + 4 | const reassignLocal = newValue => { +> 5 | local = newValue; + | ^^^^^ InvalidReact: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead. Variable `local` cannot be reassigned after render (5:5) + 6 | }; + 7 | + 8 | const onClick = newValue => { ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.mutable-range-shared-inner-outer-function.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.mutable-range-shared-inner-outer-function.expect.md index 5bb4bf1ae8..a6f2a2719f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.mutable-range-shared-inner-outer-function.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.mutable-range-shared-inner-outer-function.expect.md @@ -32,15 +32,13 @@ export const FIXTURE_ENTRYPOINT = { ## Error ``` - 15 | b.push(false); - 16 | }; -> 17 | return
; - | ^ InvalidReact: This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead (17:17) - -InvalidReact: The function modifies a local variable here (8:8) - 18 | } - 19 | - 20 | export const FIXTURE_ENTRYPOINT = { + 6 | const f = () => { + 7 | if (cond) { +> 8 | a = {}; + | ^ InvalidReact: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead. Variable `a` cannot be reassigned after render (8:8) + 9 | b = []; + 10 | } else { + 11 | a = {}; ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-function-expression-references-later-variable-declaration.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-function-expression-references-later-variable-declaration.expect.md index c096e1243a..d53d6b9ea4 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-function-expression-references-later-variable-declaration.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-function-expression-references-later-variable-declaration.expect.md @@ -17,14 +17,13 @@ function Component() { ## Error ``` + 1 | function Component() { + 2 | let callback = () => { +> 3 | onClick = () => {}; + | ^^^^^^^ InvalidReact: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead. Variable `onClick` cannot be reassigned after render (3:3) + 4 | }; 5 | let onClick; 6 | -> 7 | return
; - | ^^^^^^^^ InvalidReact: This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead (7:7) - -InvalidReact: The function modifies a local variable here (3:3) - 8 | } - 9 | ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-setstate.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-setstate.expect.md deleted file mode 100644 index 483d9b1a8e..0000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-setstate.expect.md +++ /dev/null @@ -1,77 +0,0 @@ - -## Input - -```javascript -import {useEffect, useState} from 'react'; -import {Stringify} from 'shared-runtime'; - -function Foo() { - /** - * Previously, this lowered to - * $1 = LoadContext capture setState - * $2 = FunctionExpression deps=$1 context=setState - * [[ at this point, we freeze the `LoadContext setState` instruction, but it will never be referenced again ]] - * - * Now, this function expression directly references `setState`, which freezes - * the source `DeclareContext HoistedConst setState`. Freezing source identifiers - * (instead of the one level removed `LoadContext`) is more semantically correct - * for everything *other* than hoisted context declarations. - * - * $2 = Function context=setState - */ - useEffect(() => setState(2), []); - - const [state, setState] = useState(0); - return ; -} - -export const FIXTURE_ENTRYPOINT = { - fn: Foo, - params: [{}], - sequentialRenders: [{}, {}], -}; - -``` - -## Code - -```javascript -import { c as _c } from "react/compiler-runtime"; -import { useEffect, useState } from "react"; -import { Stringify } from "shared-runtime"; - -function Foo() { - const $ = _c(3); - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = []; - $[0] = t0; - } else { - t0 = $[0]; - } - useEffect(() => setState(2), t0); - - const [state, t1] = useState(0); - const setState = t1; - let t2; - if ($[1] !== state) { - t2 = ; - $[1] = state; - $[2] = t2; - } else { - t2 = $[2]; - } - return t2; -} - -export const FIXTURE_ENTRYPOINT = { - fn: Foo, - params: [{}], - sequentialRenders: [{}, {}], -}; - -``` - -### Eval output -(kind: ok)
{"state":2}
-
{"state":2}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/reactive-setState.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/reactive-setState.expect.md index 3af2b9b8b1..2329aaed21 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/reactive-setState.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/reactive-setState.expect.md @@ -34,22 +34,28 @@ import { print } from "shared-runtime"; * setState types are not enough to determine to omit from deps. Must also take reactivity into account. */ function ReactiveRefInEffect(props) { - const $ = _c(2); + const $ = _c(4); const [, setState1] = useRef("initial value"); const [, setState2] = useRef("initial value"); let setState; - if (props.foo) { - setState = setState1; + if ($[0] !== props.foo) { + if (props.foo) { + setState = setState1; + } else { + setState = setState2; + } + $[0] = props.foo; + $[1] = setState; } else { - setState = setState2; + setState = $[1]; } let t0; - if ($[0] !== setState) { + if ($[2] !== setState) { t0 = () => print(setState); - $[0] = setState; - $[1] = t0; + $[2] = setState; + $[3] = t0; } else { - t0 = $[1]; + t0 = $[3]; } useEffect(t0, [setState]); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-callback-cross-context.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-callback-cross-context.expect.md index c1a6dfb3ea..d24536d616 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-callback-cross-context.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-callback-cross-context.expect.md @@ -57,62 +57,67 @@ import { Stringify } from "shared-runtime"; * - cb1 is not assumed to be called since it's only used as a call operand */ function useFoo(t0) { - const $ = _c(13); - const { arr1, arr2 } = t0; + const $ = _c(14); + let arr1; + let arr2; let t1; - if ($[0] !== arr1[0]) { - t1 = (e) => arr1[0].value + e.value; - $[0] = arr1[0]; - $[1] = t1; + if ($[0] !== t0) { + ({ arr1, arr2 } = t0); + let t2; + if ($[4] !== arr1[0]) { + t2 = (e) => arr1[0].value + e.value; + $[4] = arr1[0]; + $[5] = t2; + } else { + t2 = $[5]; + } + const cb1 = t2; + t1 = () => arr1.map(cb1); + $[0] = t0; + $[1] = arr1; + $[2] = arr2; + $[3] = t1; } else { - t1 = $[1]; + arr1 = $[1]; + arr2 = $[2]; + t1 = $[3]; } - const cb1 = t1; + const getArrMap1 = t1; let t2; - if ($[2] !== arr1 || $[3] !== cb1) { - t2 = () => arr1.map(cb1); - $[2] = arr1; - $[3] = cb1; - $[4] = t2; + if ($[6] !== arr2) { + t2 = (e_0) => arr2[0].value + e_0.value; + $[6] = arr2; + $[7] = t2; } else { - t2 = $[4]; + t2 = $[7]; } - const getArrMap1 = t2; + const cb2 = t2; let t3; - if ($[5] !== arr2) { - t3 = (e_0) => arr2[0].value + e_0.value; - $[5] = arr2; - $[6] = t3; + if ($[8] !== arr1 || $[9] !== cb2) { + t3 = () => arr1.map(cb2); + $[8] = arr1; + $[9] = cb2; + $[10] = t3; } else { - t3 = $[6]; + t3 = $[10]; } - const cb2 = t3; + const getArrMap2 = t3; let t4; - if ($[7] !== arr1 || $[8] !== cb2) { - t4 = () => arr1.map(cb2); - $[7] = arr1; - $[8] = cb2; - $[9] = t4; - } else { - t4 = $[9]; - } - const getArrMap2 = t4; - let t5; - if ($[10] !== getArrMap1 || $[11] !== getArrMap2) { - t5 = ( + if ($[11] !== getArrMap1 || $[12] !== getArrMap2) { + t4 = ( ); - $[10] = getArrMap1; - $[11] = getArrMap2; - $[12] = t5; + $[11] = getArrMap1; + $[12] = getArrMap2; + $[13] = t4; } else { - t5 = $[12]; + t4 = $[13]; } - return t5; + return t4; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-captures-context-variable.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-captures-context-variable.expect.md deleted file mode 100644 index c1a9ad205c..0000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-captures-context-variable.expect.md +++ /dev/null @@ -1,129 +0,0 @@ - -## Input - -```javascript -import {Stringify, useIdentity} from 'shared-runtime'; - -function Component({prop1, prop2}) { - 'use memo'; - - const data = useIdentity( - new Map([ - [0, 'value0'], - [1, 'value1'], - ]) - ); - let i = 0; - const items = []; - items.push( - data.get(i) + prop1} - shouldInvokeFns={true} - /> - ); - i = i + 1; - items.push( - data.get(i) + prop2} - shouldInvokeFns={true} - /> - ); - return <>{items}; -} - -export const FIXTURE_ENTRYPOINT = { - fn: Component, - params: [{prop1: 'prop1', prop2: 'prop2'}], - sequentialRenders: [ - {prop1: 'prop1', prop2: 'prop2'}, - {prop1: 'prop1', prop2: 'prop2'}, - {prop1: 'changed', prop2: 'prop2'}, - ], -}; - -``` - -## Code - -```javascript -import { c as _c } from "react/compiler-runtime"; -import { Stringify, useIdentity } from "shared-runtime"; - -function Component(t0) { - "use memo"; - const $ = _c(12); - const { prop1, prop2 } = t0; - let t1; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t1 = new Map([ - [0, "value0"], - [1, "value1"], - ]); - $[0] = t1; - } else { - t1 = $[0]; - } - const data = useIdentity(t1); - let t2; - if ($[1] !== data || $[2] !== prop1 || $[3] !== prop2) { - let i = 0; - const items = []; - items.push( - data.get(i) + prop1} - shouldInvokeFns={true} - />, - ); - i = i + 1; - - const t3 = i; - let t4; - if ($[5] !== data || $[6] !== i || $[7] !== prop2) { - t4 = () => data.get(i) + prop2; - $[5] = data; - $[6] = i; - $[7] = prop2; - $[8] = t4; - } else { - t4 = $[8]; - } - let t5; - if ($[9] !== t3 || $[10] !== t4) { - t5 = ; - $[9] = t3; - $[10] = t4; - $[11] = t5; - } else { - t5 = $[11]; - } - items.push(t5); - t2 = <>{items}; - $[1] = data; - $[2] = prop1; - $[3] = prop2; - $[4] = t2; - } else { - t2 = $[4]; - } - return t2; -} - -export const FIXTURE_ENTRYPOINT = { - fn: Component, - params: [{ prop1: "prop1", prop2: "prop2" }], - sequentialRenders: [ - { prop1: "prop1", prop2: "prop2" }, - { prop1: "prop1", prop2: "prop2" }, - { prop1: "changed", prop2: "prop2" }, - ], -}; - -``` - -### Eval output -(kind: ok)
{"onClick":{"kind":"Function","result":"value1prop1"},"shouldInvokeFns":true}
{"onClick":{"kind":"Function","result":"value1prop2"},"shouldInvokeFns":true}
-
{"onClick":{"kind":"Function","result":"value1prop1"},"shouldInvokeFns":true}
{"onClick":{"kind":"Function","result":"value1prop2"},"shouldInvokeFns":true}
-
{"onClick":{"kind":"Function","result":"value1changed"},"shouldInvokeFns":true}
{"onClick":{"kind":"Function","result":"value1prop2"},"shouldInvokeFns":true}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/error.invalid-reassign-local-variable-in-jsx-callback.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/error.invalid-reassign-local-variable-in-jsx-callback.expect.md index 3cab6fd671..fe684586cb 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/error.invalid-reassign-local-variable-in-jsx-callback.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/error.invalid-reassign-local-variable-in-jsx-callback.expect.md @@ -41,14 +41,13 @@ function Component() { ## Error ``` - 29 | }; - 30 | -> 31 | return ; - | ^^^^^^^ InvalidReact: This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead (31:31) - -InvalidReact: The function modifies a local variable here (5:5) - 32 | } - 33 | + 3 | + 4 | const reassignLocal = newValue => { +> 5 | local = newValue; + | ^^^^^ InvalidReact: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead. Variable `local` cannot be reassigned after render (5:5) + 6 | }; + 7 | + 8 | const onClick = newValue => { ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useCallback-reordering-deplist-controlflow.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useCallback-reordering-deplist-controlflow.expect.md index 080cc0a74a..18e0621d62 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useCallback-reordering-deplist-controlflow.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useCallback-reordering-deplist-controlflow.expect.md @@ -40,39 +40,46 @@ import { useCallback } from "react"; import { Stringify } from "shared-runtime"; function Foo(t0) { - const $ = _c(8); + const $ = _c(10); const { arr1, arr2, foo } = t0; - let getVal1; let t1; - if ($[0] !== arr1 || $[1] !== arr2 || $[2] !== foo) { - const x = [arr1]; - + if ($[0] !== arr1) { + t1 = [arr1]; + $[0] = arr1; + $[1] = t1; + } else { + t1 = $[1]; + } + const x = t1; + let getVal1; + let t2; + if ($[2] !== arr2 || $[3] !== foo || $[4] !== x) { let y = []; getVal1 = _temp; - t1 = () => [y]; + t2 = () => [y]; foo ? (y = x.concat(arr2)) : y; - $[0] = arr1; - $[1] = arr2; - $[2] = foo; - $[3] = getVal1; - $[4] = t1; - } else { - getVal1 = $[3]; - t1 = $[4]; - } - const getVal2 = t1; - let t2; - if ($[5] !== getVal1 || $[6] !== getVal2) { - t2 = ; + $[2] = arr2; + $[3] = foo; + $[4] = x; $[5] = getVal1; - $[6] = getVal2; - $[7] = t2; + $[6] = t2; } else { - t2 = $[7]; + getVal1 = $[5]; + t2 = $[6]; } - return t2; + const getVal2 = t2; + let t3; + if ($[7] !== getVal1 || $[8] !== getVal2) { + t3 = ; + $[7] = getVal1; + $[8] = getVal2; + $[9] = t3; + } else { + t3 = $[9]; + } + return t3; } function _temp() { return { x: 2 }; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useCallback-reordering-depslist-assignment.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useCallback-reordering-depslist-assignment.expect.md index 89a6ad80c3..f7ebdf0ca5 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useCallback-reordering-depslist-assignment.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useCallback-reordering-depslist-assignment.expect.md @@ -36,31 +36,38 @@ import { Stringify } from "shared-runtime"; // We currently produce invalid output (incorrect scoping for `y` declaration) function useFoo(arr1, arr2) { - const $ = _c(5); + const $ = _c(7); let t0; - if ($[0] !== arr1 || $[1] !== arr2) { - const x = [arr1]; - + if ($[0] !== arr1) { + t0 = [arr1]; + $[0] = arr1; + $[1] = t0; + } else { + t0 = $[1]; + } + const x = t0; + let t1; + if ($[2] !== arr2 || $[3] !== x) { let y; - t0 = () => ({ y }); + t1 = () => ({ y }); (y = x.concat(arr2)), y; - $[0] = arr1; - $[1] = arr2; - $[2] = t0; - } else { - t0 = $[2]; - } - const getVal = t0; - let t1; - if ($[3] !== getVal) { - t1 = ; - $[3] = getVal; + $[2] = arr2; + $[3] = x; $[4] = t1; } else { t1 = $[4]; } - return t1; + const getVal = t1; + let t2; + if ($[5] !== getVal) { + t2 = ; + $[5] = getVal; + $[6] = t2; + } else { + t2 = $[6]; + } + return t2; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-reordering-depslist-assignment.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-reordering-depslist-assignment.expect.md index 3fffec6a7d..6a3197de54 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-reordering-depslist-assignment.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-reordering-depslist-assignment.expect.md @@ -30,29 +30,36 @@ import { c as _c } from "react/compiler-runtime"; import { useMemo } from "react"; function useFoo(arr1, arr2) { - const $ = _c(5); - let y; - if ($[0] !== arr1 || $[1] !== arr2) { - const x = [arr1]; - - (y = x.concat(arr2)), y; - $[0] = arr1; - $[1] = arr2; - $[2] = y; - } else { - y = $[2]; - } + const $ = _c(7); let t0; - let t1; - if ($[3] !== y) { - t1 = { y }; - $[3] = y; - $[4] = t1; + if ($[0] !== arr1) { + t0 = [arr1]; + $[0] = arr1; + $[1] = t0; } else { - t1 = $[4]; + t0 = $[1]; } - t0 = t1; - return t0; + const x = t0; + let y; + if ($[2] !== arr2 || $[3] !== x) { + (y = x.concat(arr2)), y; + $[2] = arr2; + $[3] = x; + $[4] = y; + } else { + y = $[4]; + } + let t1; + let t2; + if ($[5] !== y) { + t2 = { y }; + $[5] = y; + $[6] = t2; + } else { + t2 = $[6]; + } + t1 = t2; + return t1; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-uncalled-function-capturing-mutable-values-memoizes-with-captures-values.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-uncalled-function-capturing-mutable-values-memoizes-with-captures-values.expect.md deleted file mode 100644 index e771bf12bd..0000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-uncalled-function-capturing-mutable-values-memoizes-with-captures-values.expect.md +++ /dev/null @@ -1,77 +0,0 @@ - -## Input - -```javascript -// @flow -/** - * This hook returns a function that when called with an input object, - * will return the result of mapping that input with the supplied map - * function. Results are cached, so if the same input is passed again, - * the same output object will be returned. - * - * Note that this technically violates the rules of React and is unsafe: - * hooks must return immutable objects and be pure, and a function which - * captures and mutates a value when called is inherently not pure. - * - * However, in this case it is technically safe _if_ the mapping function - * is pure *and* the resulting objects are never modified. This is because - * the function only caches: the result of `returnedFunction(someInput)` - * strictly depends on `returnedFunction` and `someInput`, and cannot - * otherwise change over time. - */ -hook useMemoMap( - map: TInput => TOutput -): TInput => TOutput { - return useMemo(() => { - // The original issue is that `cache` was not memoized together with the returned - // function. This was because neither appears to ever be mutated — the function - // is known to mutate `cache` but the function isn't called. - // - // The fix is to detect cases like this — functions that are mutable but not called - - // and ensure that their mutable captures are aliased together into the same scope. - const cache = new WeakMap(); - return input => { - let output = cache.get(input); - if (output == null) { - output = map(input); - cache.set(input, output); - } - return output; - }; - }, [map]); -} - -``` - -## Code - -```javascript -import { c as _c } from "react/compiler-runtime"; - -function useMemoMap(map) { - const $ = _c(2); - let t0; - let t1; - if ($[0] !== map) { - const cache = new WeakMap(); - t1 = (input) => { - let output = cache.get(input); - if (output == null) { - output = map(input); - cache.set(input, output); - } - return output; - }; - $[0] = map; - $[1] = t1; - } else { - t1 = $[1]; - } - t0 = t1; - return t0; -} - -``` - -### Eval output -(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/shared-hook-calls.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/shared-hook-calls.expect.md index 92dbf9843a..d98b3e03ab 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/shared-hook-calls.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/shared-hook-calls.expect.md @@ -30,47 +30,60 @@ import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire import { fire } from "react"; function Component(t0) { - const $ = _c(9); - const { bar, baz } = t0; - let t1; - if ($[0] !== bar) { - t1 = () => { - console.log(bar); - }; - $[0] = bar; - $[1] = t1; + const $ = _c(13); + let bar; + let baz; + let foo; + if ($[0] !== t0) { + ({ bar, baz } = t0); + let t1; + if ($[4] !== bar) { + t1 = () => { + console.log(bar); + }; + $[4] = bar; + $[5] = t1; + } else { + t1 = $[5]; + } + foo = t1; + $[0] = t0; + $[1] = bar; + $[2] = baz; + $[3] = foo; } else { - t1 = $[1]; + bar = $[1]; + baz = $[2]; + foo = $[3]; } - const foo = t1; - const t2 = useFire(foo); - const t3 = useFire(baz); - let t4; - if ($[2] !== bar || $[3] !== t2 || $[4] !== t3) { - t4 = () => { - t2(bar); - t3(bar); - }; - $[2] = bar; - $[3] = t2; - $[4] = t3; - $[5] = t4; - } else { - t4 = $[5]; - } - useEffect(t4); - let t5; - if ($[6] !== bar || $[7] !== t2) { - t5 = () => { + const t1 = useFire(foo); + const t2 = useFire(baz); + let t3; + if ($[6] !== bar || $[7] !== t1 || $[8] !== t2) { + t3 = () => { + t1(bar); t2(bar); }; $[6] = bar; - $[7] = t2; - $[8] = t5; + $[7] = t1; + $[8] = t2; + $[9] = t3; } else { - t5 = $[8]; + t3 = $[9]; } - useEffect(t5); + useEffect(t3); + let t4; + if ($[10] !== bar || $[11] !== t1) { + t4 = () => { + t1(bar); + }; + $[10] = bar; + $[11] = t1; + $[12] = t4; + } else { + t4 = $[12]; + } + useEffect(t4); return null; }