From 52612a7cbdd8e1fee9599478247f78725869ebad Mon Sep 17 00:00:00 2001 From: lauren Date: Fri, 1 Aug 2025 15:10:34 -0400 Subject: [PATCH 1/4] [compiler] Emit more specific error when making identifiers with reserved words (#34080) This currently throws an invariant which may be misleading. I checked the ecma262 spec and used the same list of reserved words in our check. To err on the side of being conservative, we also error when strict mode reserved words are used. --- .../src/HIR/HIR.ts | 22 +++-- .../src/Utils/Keyword.ts | 87 +++++++++++++++++++ .../ecma/error.reserved-words.expect.md | 32 +++++++ .../compiler/ecma/error.reserved-words.ts | 13 +++ 4 files changed, 148 insertions(+), 6 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/Utils/Keyword.ts create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ecma/error.reserved-words.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ecma/error.reserved-words.ts diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts index f9caa844f3c52..51715b3c1e1df 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts @@ -14,6 +14,7 @@ import type {HookKind} from './ObjectShape'; import {Type, makeType} from './Types'; import {z} from 'zod'; import type {AliasingEffect} from '../Inference/AliasingEffects'; +import {isReservedWord} from '../Utils/Keyword'; /* * ******************************************************************************************* @@ -1320,12 +1321,21 @@ export function forkTemporaryIdentifier( * original source code. */ export function makeIdentifierName(name: string): ValidatedIdentifier { - CompilerError.invariant(t.isValidIdentifier(name), { - reason: `Expected a valid identifier name`, - loc: GeneratedSource, - description: `\`${name}\` is not a valid JavaScript identifier`, - suggestions: null, - }); + if (isReservedWord(name)) { + CompilerError.throwInvalidJS({ + reason: 'Expected a non-reserved identifier name', + loc: GeneratedSource, + description: `\`${name}\` is a reserved word in JavaScript and cannot be used as an identifier name`, + suggestions: null, + }); + } else { + CompilerError.invariant(t.isValidIdentifier(name), { + reason: `Expected a valid identifier name`, + loc: GeneratedSource, + description: `\`${name}\` is not a valid JavaScript identifier`, + suggestions: null, + }); + } return { kind: 'named', value: name as ValidIdentifierName, diff --git a/compiler/packages/babel-plugin-react-compiler/src/Utils/Keyword.ts b/compiler/packages/babel-plugin-react-compiler/src/Utils/Keyword.ts new file mode 100644 index 0000000000000..3964f4accc85f --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/Utils/Keyword.ts @@ -0,0 +1,87 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/** + * https://tc39.es/ecma262/multipage/ecmascript-language-lexical-grammar.html#sec-keywords-and-reserved-words + */ + +/** + * Note: `await` and `yield` are contextually allowed as identifiers. + * await: reserved inside async functions and modules + * yield: reserved inside generator functions + * + * Note: `async` is not reserved. + */ +const RESERVED_WORDS = new Set([ + 'break', + 'case', + 'catch', + 'class', + 'const', + 'continue', + 'debugger', + 'default', + 'delete', + 'do', + 'else', + 'enum', + 'export', + 'extends', + 'false', + 'finally', + 'for', + 'function', + 'if', + 'import', + 'in', + 'instanceof', + 'new', + 'null', + 'return', + 'super', + 'switch', + 'this', + 'throw', + 'true', + 'try', + 'typeof', + 'var', + 'void', + 'while', + 'with', +]); + +/** + * Reserved when a module has a 'use strict' directive. + */ +const STRICT_MODE_RESERVED_WORDS = new Set([ + 'let', + 'static', + 'implements', + 'interface', + 'package', + 'private', + 'protected', + 'public', +]); +/** + * The names arguments and eval are not keywords, but they are subject to some restrictions in + * strict mode code. + */ +const STRICT_MODE_RESTRICTED_WORDS = new Set(['eval', 'arguments']); + +/** + * Conservative check for whether an identifer name is reserved or not. We assume that code is + * written with strict mode. + */ +export function isReservedWord(identifierName: string): boolean { + return ( + RESERVED_WORDS.has(identifierName) || + STRICT_MODE_RESERVED_WORDS.has(identifierName) || + STRICT_MODE_RESTRICTED_WORDS.has(identifierName) + ); +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ecma/error.reserved-words.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ecma/error.reserved-words.expect.md new file mode 100644 index 0000000000000..a6ee8a798b583 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ecma/error.reserved-words.expect.md @@ -0,0 +1,32 @@ + +## Input + +```javascript +import {useRef} from 'react'; + +function useThing(fn) { + const fnRef = useRef(fn); + const ref = useRef(null); + + if (ref.current === null) { + ref.current = function (this: unknown, ...args) { + return fnRef.current.call(this, ...args); + }; + } + return ref.current; +} + +``` + + +## Error + +``` +Found 1 error: + +Error: Expected a non-reserved identifier name + +`this` is a reserved word in JavaScript and cannot be used as an identifier name. +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ecma/error.reserved-words.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ecma/error.reserved-words.ts new file mode 100644 index 0000000000000..2937ba8df5e28 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ecma/error.reserved-words.ts @@ -0,0 +1,13 @@ +import {useRef} from 'react'; + +function useThing(fn) { + const fnRef = useRef(fn); + const ref = useRef(null); + + if (ref.current === null) { + ref.current = function (this: unknown, ...args) { + return fnRef.current.call(this, ...args); + }; + } + return ref.current; +} From 538ac7ae4b918136c5bd7d15ada19439d15f8080 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Fri, 1 Aug 2025 15:44:48 -0400 Subject: [PATCH 2/4] [Flight] Fix debug info leaking to outer handler (#34081) The `waitForReference` call for debug info can trigger inside a different object's initializingHandler. In that case, we can get confused by which one is the root object. We have this special case to detect if the initializing handler's object is `null` and we have an empty string key, then we should replace the root object's value with the resolved value. https://github.com/facebook/react/blob/52612a7cbdd8e1fee9599478247f78725869ebad/packages/react-client/src/ReactFlightClient.js#L1374 However, if the initializing handler actually should have the value `null` then we might get confused by this and replace it with the resolved value from a debug object. This fixes it by just using a non-empty string as the key for the waitForReference on debug value since we're not going to use it anyway. It used to be impossible to get into this state since a `null` value at the root couldn't have any reference inside itself but now the debug info for a `null` value can have outstanding references. However, a better fix might be using a placeholder marker object instead of null or better yet ensuring that we know which root we're initializing in the debug model. --- .../react-client/src/ReactFlightClient.js | 2 +- .../src/__tests__/ReactFlightDOMEdge-test.js | 53 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index 4a13d094a826b..fe88fe0357be7 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -879,7 +879,7 @@ function initializeDebugChunk( waitForReference( debugChunk, {}, // noop, since we'll have already added an entry to debug info - '', // noop + 'debug', // noop, but we need it to not be empty string since that indicates the root object response, initializeDebugInfo, [''], // path diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js index 54177d8f8cb4e..42cff2ad51d0c 100644 --- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js +++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js @@ -1915,4 +1915,57 @@ describe('ReactFlightDOMEdge', () => { expect(ownerStack).toBeNull(); } }); + + it('can pass an async import that resolves later as a prop to a null component', async () => { + let resolveClientComponentChunk; + const client = clientExports( + { + foo: 'bar', + }, + '42', + '/test.js', + new Promise(resolve => (resolveClientComponentChunk = resolve)), + ); + + function ServerComponent(props) { + return null; + } + + function App() { + return ( +
+ +
+ ); + } + + const stream = await serverAct(() => + passThrough( + ReactServerDOMServer.renderToReadableStream(, webpackMap), + ), + ); + + // Parsing the root blocks because the module hasn't loaded yet + const response = ReactServerDOMClient.createFromReadableStream(stream, { + serverConsumerManifest: { + moduleMap: null, + moduleLoading: null, + }, + }); + + function ClientRoot() { + return use(response); + } + + // Initialize to be blocked. + response.then(() => {}); + // Unblock. + resolveClientComponentChunk(); + + const ssrStream = await serverAct(() => + ReactDOMServer.renderToReadableStream(), + ); + const result = await readResult(ssrStream); + expect(result).toEqual('
'); + }); }); From 0860b9cc1f4a7188b41204bddc57a127a8bbf6e9 Mon Sep 17 00:00:00 2001 From: Joseph Savona <6425824+josephsavona@users.noreply.github.com> Date: Fri, 1 Aug 2025 12:59:49 -0700 Subject: [PATCH 3/4] [compiler] Add definitions for Object entries/keys/values (#34047) Fixes remaining issue in #32261, where passing a previously useMemo()-d value to `Object.entries()` makes the compiler think the value is mutated and fail validatePreserveExistingMemo. While I was there I added Object.keys() and Object.values() too. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34047). * #34049 * __->__ #34047 * #34044 --- .../src/HIR/Globals.ts | 93 +++++++++++++++ .../src/HIR/ObjectShape.ts | 1 + .../src/HIR/TypeSchema.ts | 15 +++ ...validate-object-entries-mutation.expect.md | 57 +++++++++ .../error.validate-object-entries-mutation.js | 16 +++ ....validate-object-values-mutation.expect.md | 57 +++++++++ .../error.validate-object-values-mutation.js | 16 +++ .../object-entries-mutation.expect.md | 57 +++++++++ .../compiler/object-entries-mutation.js | 15 +++ .../fixtures/compiler/object-keys.expect.md | 108 ++++++++++++++++++ .../fixtures/compiler/object-keys.js | 36 ++++++ .../compiler/object-values-mutation.expect.md | 57 +++++++++ .../compiler/object-values-mutation.js | 15 +++ .../fixtures/compiler/object-values.expect.md | 103 +++++++++++++++++ .../fixtures/compiler/object-values.js | 39 +++++++ ...repro-object-fromEntries-entries.expect.md | 97 ++++++++++++++++ .../repro-object-fromEntries-entries.js | 36 ++++++ 17 files changed, 818 insertions(+) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.validate-object-entries-mutation.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.validate-object-entries-mutation.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.validate-object-values-mutation.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.validate-object-values-mutation.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-entries-mutation.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-entries-mutation.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-keys.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-keys.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-values-mutation.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-values-mutation.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-values.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-values.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-object-fromEntries-entries.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-object-fromEntries-entries.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts index c3eadb89f50c3..264174debafc0 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts @@ -114,6 +114,99 @@ const TYPED_GLOBALS: Array<[string, BuiltInType]> = [ returnValueKind: ValueKind.Mutable, }), ], + [ + 'entries', + addFunction(DEFAULT_SHAPES, [], { + positionalParams: [Effect.Capture], + restParam: null, + returnType: {kind: 'Object', shapeId: BuiltInArrayId}, + calleeEffect: Effect.Read, + returnValueKind: ValueKind.Mutable, + aliasing: { + receiver: '@receiver', + params: ['@object'], + rest: null, + returns: '@returns', + temporaries: [], + effects: [ + { + kind: 'Create', + into: '@returns', + reason: ValueReason.KnownReturnSignature, + value: ValueKind.Mutable, + }, + // Object values are captured into the return + { + kind: 'Capture', + from: '@object', + into: '@returns', + }, + ], + }, + }), + ], + [ + 'keys', + addFunction(DEFAULT_SHAPES, [], { + positionalParams: [Effect.Read], + restParam: null, + returnType: {kind: 'Object', shapeId: BuiltInArrayId}, + calleeEffect: Effect.Read, + returnValueKind: ValueKind.Mutable, + aliasing: { + receiver: '@receiver', + params: ['@object'], + rest: null, + returns: '@returns', + temporaries: [], + effects: [ + { + kind: 'Create', + into: '@returns', + reason: ValueReason.KnownReturnSignature, + value: ValueKind.Mutable, + }, + // Only keys are captured, and keys are immutable + { + kind: 'ImmutableCapture', + from: '@object', + into: '@returns', + }, + ], + }, + }), + ], + [ + 'values', + addFunction(DEFAULT_SHAPES, [], { + positionalParams: [Effect.Capture], + restParam: null, + returnType: {kind: 'Object', shapeId: BuiltInArrayId}, + calleeEffect: Effect.Read, + returnValueKind: ValueKind.Mutable, + aliasing: { + receiver: '@receiver', + params: ['@object'], + rest: null, + returns: '@returns', + temporaries: [], + effects: [ + { + kind: 'Create', + into: '@returns', + reason: ValueReason.KnownReturnSignature, + value: ValueKind.Mutable, + }, + // Object values are captured into the return + { + kind: 'Capture', + from: '@object', + into: '@returns', + }, + ], + }, + }), + ], ]), ], [ diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/ObjectShape.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/ObjectShape.ts index eaf728db95119..ced080adcc0c3 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/ObjectShape.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/ObjectShape.ts @@ -142,6 +142,7 @@ function parseAliasingSignatureConfig( const effects = typeConfig.effects.map( (effect: AliasingEffectConfig): AliasingEffect => { switch (effect.kind) { + case 'ImmutableCapture': case 'CreateFrom': case 'Capture': case 'Alias': diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/TypeSchema.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/TypeSchema.ts index 5945e3a07822f..e63ef067b20c7 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/TypeSchema.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/TypeSchema.ts @@ -111,6 +111,19 @@ export const AliasEffectSchema: z.ZodType = z.object({ into: LifetimeIdSchema, }); +export type ImmutableCaptureEffectConfig = { + kind: 'ImmutableCapture'; + from: string; + into: string; +}; + +export const ImmutableCaptureEffectSchema: z.ZodType = + z.object({ + kind: z.literal('ImmutableCapture'), + from: LifetimeIdSchema, + into: LifetimeIdSchema, + }); + export type CaptureEffectConfig = { kind: 'Capture'; from: string; @@ -187,6 +200,7 @@ export type AliasingEffectConfig = | AssignEffectConfig | AliasEffectConfig | CaptureEffectConfig + | ImmutableCaptureEffectConfig | ImpureEffectConfig | MutateEffectConfig | MutateTransitiveConditionallyConfig @@ -199,6 +213,7 @@ export const AliasingEffectSchema: z.ZodType = z.union([ AssignEffectSchema, AliasEffectSchema, CaptureEffectSchema, + ImmutableCaptureEffectSchema, ImpureEffectSchema, MutateEffectSchema, MutateTransitiveConditionallySchema, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.validate-object-entries-mutation.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.validate-object-entries-mutation.expect.md new file mode 100644 index 0000000000000..09ff6e7214ba3 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.validate-object-entries-mutation.expect.md @@ -0,0 +1,57 @@ + +## Input + +```javascript +// @validatePreserveExistingMemoizationGuarantees +import {makeObject_Primitives, Stringify} from 'shared-runtime'; + +function Component(props) { + const object = {object: props.object}; + const entries = useMemo(() => Object.entries(object), [object]); + entries.map(([, value]) => { + value.updated = true; + }); + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{object: {key: makeObject_Primitives()}}], +}; + +``` + + +## Error + +``` +Found 2 errors: + +Memoization: Compilation skipped because existing memoization could not be preserved + +React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This dependency may be mutated later, which could cause the value to change unexpectedly. + +error.validate-object-entries-mutation.ts:6:57 + 4 | function Component(props) { + 5 | const object = {object: props.object}; +> 6 | const entries = useMemo(() => Object.entries(object), [object]); + | ^^^^^^ This dependency may be modified later + 7 | entries.map(([, value]) => { + 8 | value.updated = true; + 9 | }); + +Memoization: Compilation skipped because existing memoization could not be preserved + +React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value was memoized in source but not in compilation output. + +error.validate-object-entries-mutation.ts:6:18 + 4 | function Component(props) { + 5 | const object = {object: props.object}; +> 6 | const entries = useMemo(() => Object.entries(object), [object]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Could not preserve existing memoization + 7 | entries.map(([, value]) => { + 8 | value.updated = true; + 9 | }); +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.validate-object-entries-mutation.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.validate-object-entries-mutation.js new file mode 100644 index 0000000000000..b4145b1617f81 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.validate-object-entries-mutation.js @@ -0,0 +1,16 @@ +// @validatePreserveExistingMemoizationGuarantees +import {makeObject_Primitives, Stringify} from 'shared-runtime'; + +function Component(props) { + const object = {object: props.object}; + const entries = useMemo(() => Object.entries(object), [object]); + entries.map(([, value]) => { + value.updated = true; + }); + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{object: {key: makeObject_Primitives()}}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.validate-object-values-mutation.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.validate-object-values-mutation.expect.md new file mode 100644 index 0000000000000..b791b629278af --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.validate-object-values-mutation.expect.md @@ -0,0 +1,57 @@ + +## Input + +```javascript +// @validatePreserveExistingMemoizationGuarantees +import {makeObject_Primitives, Stringify} from 'shared-runtime'; + +function Component(props) { + const object = {object: props.object}; + const values = useMemo(() => Object.values(object), [object]); + values.map(value => { + value.updated = true; + }); + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{object: {key: makeObject_Primitives()}}], +}; + +``` + + +## Error + +``` +Found 2 errors: + +Memoization: Compilation skipped because existing memoization could not be preserved + +React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This dependency may be mutated later, which could cause the value to change unexpectedly. + +error.validate-object-values-mutation.ts:6:55 + 4 | function Component(props) { + 5 | const object = {object: props.object}; +> 6 | const values = useMemo(() => Object.values(object), [object]); + | ^^^^^^ This dependency may be modified later + 7 | values.map(value => { + 8 | value.updated = true; + 9 | }); + +Memoization: Compilation skipped because existing memoization could not be preserved + +React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value was memoized in source but not in compilation output. + +error.validate-object-values-mutation.ts:6:17 + 4 | function Component(props) { + 5 | const object = {object: props.object}; +> 6 | const values = useMemo(() => Object.values(object), [object]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Could not preserve existing memoization + 7 | values.map(value => { + 8 | value.updated = true; + 9 | }); +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.validate-object-values-mutation.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.validate-object-values-mutation.js new file mode 100644 index 0000000000000..3482887d92077 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.validate-object-values-mutation.js @@ -0,0 +1,16 @@ +// @validatePreserveExistingMemoizationGuarantees +import {makeObject_Primitives, Stringify} from 'shared-runtime'; + +function Component(props) { + const object = {object: props.object}; + const values = useMemo(() => Object.values(object), [object]); + values.map(value => { + value.updated = true; + }); + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{object: {key: makeObject_Primitives()}}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-entries-mutation.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-entries-mutation.expect.md new file mode 100644 index 0000000000000..bc541b47f1f5d --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-entries-mutation.expect.md @@ -0,0 +1,57 @@ + +## Input + +```javascript +import {makeObject_Primitives, Stringify} from 'shared-runtime'; + +function Component(props) { + const object = {object: props.object}; + const entries = Object.entries(object); + entries.map(([, value]) => { + value.updated = true; + }); + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{object: {key: makeObject_Primitives()}}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { makeObject_Primitives, Stringify } from "shared-runtime"; + +function Component(props) { + const $ = _c(2); + let t0; + if ($[0] !== props.object) { + const object = { object: props.object }; + const entries = Object.entries(object); + entries.map(_temp); + t0 = ; + $[0] = props.object; + $[1] = t0; + } else { + t0 = $[1]; + } + return t0; +} +function _temp(t0) { + const [, value] = t0; + value.updated = true; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ object: { key: makeObject_Primitives() } }], +}; + +``` + +### Eval output +(kind: ok)
{"entries":[["object",{"key":{"a":0,"b":"value1","c":true},"updated":true}]]}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-entries-mutation.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-entries-mutation.js new file mode 100644 index 0000000000000..2902cffd014fe --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-entries-mutation.js @@ -0,0 +1,15 @@ +import {makeObject_Primitives, Stringify} from 'shared-runtime'; + +function Component(props) { + const object = {object: props.object}; + const entries = Object.entries(object); + entries.map(([, value]) => { + value.updated = true; + }); + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{object: {key: makeObject_Primitives()}}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-keys.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-keys.expect.md new file mode 100644 index 0000000000000..f076d9032db0e --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-keys.expect.md @@ -0,0 +1,108 @@ + +## Input + +```javascript +// @validatePreserveExistingMemoizationGuarantees +import {useMemo} from 'react'; +import {Stringify} from 'shared-runtime'; + +// derived from https://github.com/facebook/react/issues/32261 +function Component({items}) { + const record = useMemo( + () => + Object.fromEntries( + items.map(item => [item.id, ref => ]) + ), + [items] + ); + + // Without a declaration for Object.entries(), this would be assumed to mutate + // `record`, meaning existing memoization couldn't be preserved + return ( +
+ {Object.keys(record).map(id => ( + + ))} +
+ ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [ + { + items: [ + {id: '0', name: 'Hello'}, + {id: '1', name: 'World!'}, + ], + }, + ], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees +import { useMemo } from "react"; +import { Stringify } from "shared-runtime"; + +// derived from https://github.com/facebook/react/issues/32261 +function Component(t0) { + const $ = _c(7); + const { items } = t0; + let t1; + if ($[0] !== items) { + t1 = Object.fromEntries(items.map(_temp)); + $[0] = items; + $[1] = t1; + } else { + t1 = $[1]; + } + const record = t1; + let t2; + if ($[2] !== record) { + t2 = Object.keys(record); + $[2] = record; + $[3] = t2; + } else { + t2 = $[3]; + } + let t3; + if ($[4] !== record || $[5] !== t2) { + t3 = ( +
+ {t2.map((id) => ( + + ))} +
+ ); + $[4] = record; + $[5] = t2; + $[6] = t3; + } else { + t3 = $[6]; + } + return t3; +} +function _temp(item) { + return [item.id, (ref) => ]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [ + { + items: [ + { id: "0", name: "Hello" }, + { id: "1", name: "World!" }, + ], + }, + ], +}; + +``` + +### Eval output +(kind: ok)
{"render":"[[ function params=1 ]]"}
{"render":"[[ function params=1 ]]"}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-keys.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-keys.js new file mode 100644 index 0000000000000..38ae97ab95643 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-keys.js @@ -0,0 +1,36 @@ +// @validatePreserveExistingMemoizationGuarantees +import {useMemo} from 'react'; +import {Stringify} from 'shared-runtime'; + +// derived from https://github.com/facebook/react/issues/32261 +function Component({items}) { + const record = useMemo( + () => + Object.fromEntries( + items.map(item => [item.id, ref => ]) + ), + [items] + ); + + // Without a declaration for Object.entries(), this would be assumed to mutate + // `record`, meaning existing memoization couldn't be preserved + return ( +
+ {Object.keys(record).map(id => ( + + ))} +
+ ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [ + { + items: [ + {id: '0', name: 'Hello'}, + {id: '1', name: 'World!'}, + ], + }, + ], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-values-mutation.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-values-mutation.expect.md new file mode 100644 index 0000000000000..bc541b47f1f5d --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-values-mutation.expect.md @@ -0,0 +1,57 @@ + +## Input + +```javascript +import {makeObject_Primitives, Stringify} from 'shared-runtime'; + +function Component(props) { + const object = {object: props.object}; + const entries = Object.entries(object); + entries.map(([, value]) => { + value.updated = true; + }); + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{object: {key: makeObject_Primitives()}}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { makeObject_Primitives, Stringify } from "shared-runtime"; + +function Component(props) { + const $ = _c(2); + let t0; + if ($[0] !== props.object) { + const object = { object: props.object }; + const entries = Object.entries(object); + entries.map(_temp); + t0 = ; + $[0] = props.object; + $[1] = t0; + } else { + t0 = $[1]; + } + return t0; +} +function _temp(t0) { + const [, value] = t0; + value.updated = true; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ object: { key: makeObject_Primitives() } }], +}; + +``` + +### Eval output +(kind: ok)
{"entries":[["object",{"key":{"a":0,"b":"value1","c":true},"updated":true}]]}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-values-mutation.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-values-mutation.js new file mode 100644 index 0000000000000..2902cffd014fe --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-values-mutation.js @@ -0,0 +1,15 @@ +import {makeObject_Primitives, Stringify} from 'shared-runtime'; + +function Component(props) { + const object = {object: props.object}; + const entries = Object.entries(object); + entries.map(([, value]) => { + value.updated = true; + }); + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{object: {key: makeObject_Primitives()}}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-values.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-values.expect.md new file mode 100644 index 0000000000000..900399066413c --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-values.expect.md @@ -0,0 +1,103 @@ + +## Input + +```javascript +// @validatePreserveExistingMemoizationGuarantees +import {useMemo} from 'react'; +import {Stringify} from 'shared-runtime'; + +// derived from https://github.com/facebook/react/issues/32261 +function Component({items}) { + const record = useMemo( + () => + Object.fromEntries( + items.map(item => [ + item.id, + {id: item.id, render: ref => }, + ]) + ), + [items] + ); + + // Without a declaration for Object.entries(), this would be assumed to mutate + // `record`, meaning existing memoization couldn't be preserved + return ( +
+ {Object.values(record).map(({id, render}) => ( + + ))} +
+ ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [ + { + items: [ + {id: '0', name: 'Hello'}, + {id: '1', name: 'World!'}, + ], + }, + ], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees +import { useMemo } from "react"; +import { Stringify } from "shared-runtime"; + +// derived from https://github.com/facebook/react/issues/32261 +function Component(t0) { + const $ = _c(4); + const { items } = t0; + let t1; + if ($[0] !== items) { + t1 = Object.fromEntries(items.map(_temp)); + $[0] = items; + $[1] = t1; + } else { + t1 = $[1]; + } + const record = t1; + let t2; + if ($[2] !== record) { + t2 =
{Object.values(record).map(_temp2)}
; + $[2] = record; + $[3] = t2; + } else { + t2 = $[3]; + } + return t2; +} +function _temp2(t0) { + const { id, render } = t0; + return ; +} +function _temp(item) { + return [ + item.id, + { id: item.id, render: (ref) => }, + ]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [ + { + items: [ + { id: "0", name: "Hello" }, + { id: "1", name: "World!" }, + ], + }, + ], +}; + +``` + +### Eval output +(kind: ok)
{"render":"[[ function params=1 ]]"}
{"render":"[[ function params=1 ]]"}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-values.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-values.js new file mode 100644 index 0000000000000..4cf229c379a56 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-values.js @@ -0,0 +1,39 @@ +// @validatePreserveExistingMemoizationGuarantees +import {useMemo} from 'react'; +import {Stringify} from 'shared-runtime'; + +// derived from https://github.com/facebook/react/issues/32261 +function Component({items}) { + const record = useMemo( + () => + Object.fromEntries( + items.map(item => [ + item.id, + {id: item.id, render: ref => }, + ]) + ), + [items] + ); + + // Without a declaration for Object.entries(), this would be assumed to mutate + // `record`, meaning existing memoization couldn't be preserved + return ( +
+ {Object.values(record).map(({id, render}) => ( + + ))} +
+ ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [ + { + items: [ + {id: '0', name: 'Hello'}, + {id: '1', name: 'World!'}, + ], + }, + ], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-object-fromEntries-entries.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-object-fromEntries-entries.expect.md new file mode 100644 index 0000000000000..b7ebcbc1cf6ed --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-object-fromEntries-entries.expect.md @@ -0,0 +1,97 @@ + +## Input + +```javascript +// @validatePreserveExistingMemoizationGuarantees +import {useMemo} from 'react'; +import {Stringify} from 'shared-runtime'; + +// derived from https://github.com/facebook/react/issues/32261 +function Component({items}) { + const record = useMemo( + () => + Object.fromEntries( + items.map(item => [item.id, ref => ]) + ), + [items] + ); + + // Without a declaration for Object.entries(), this would be assumed to mutate + // `record`, meaning existing memoization couldn't be preserved + return ( +
+ {Object.entries(record).map(([id, render]) => ( + + ))} +
+ ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [ + { + items: [ + {id: '0', name: 'Hello'}, + {id: '1', name: 'World!'}, + ], + }, + ], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees +import { useMemo } from "react"; +import { Stringify } from "shared-runtime"; + +// derived from https://github.com/facebook/react/issues/32261 +function Component(t0) { + const $ = _c(4); + const { items } = t0; + let t1; + if ($[0] !== items) { + t1 = Object.fromEntries(items.map(_temp)); + $[0] = items; + $[1] = t1; + } else { + t1 = $[1]; + } + const record = t1; + let t2; + if ($[2] !== record) { + t2 =
{Object.entries(record).map(_temp2)}
; + $[2] = record; + $[3] = t2; + } else { + t2 = $[3]; + } + return t2; +} +function _temp2(t0) { + const [id, render] = t0; + return ; +} +function _temp(item) { + return [item.id, (ref) => ]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [ + { + items: [ + { id: "0", name: "Hello" }, + { id: "1", name: "World!" }, + ], + }, + ], +}; + +``` + +### Eval output +(kind: ok)
{"render":"[[ function params=1 ]]"}
{"render":"[[ function params=1 ]]"}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-object-fromEntries-entries.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-object-fromEntries-entries.js new file mode 100644 index 0000000000000..7dd8f34826a05 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-object-fromEntries-entries.js @@ -0,0 +1,36 @@ +// @validatePreserveExistingMemoizationGuarantees +import {useMemo} from 'react'; +import {Stringify} from 'shared-runtime'; + +// derived from https://github.com/facebook/react/issues/32261 +function Component({items}) { + const record = useMemo( + () => + Object.fromEntries( + items.map(item => [item.id, ref => ]) + ), + [items] + ); + + // Without a declaration for Object.entries(), this would be assumed to mutate + // `record`, meaning existing memoization couldn't be preserved + return ( +
+ {Object.entries(record).map(([id, render]) => ( + + ))} +
+ ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [ + { + items: [ + {id: '0', name: 'Hello'}, + {id: '1', name: 'World!'}, + ], + }, + ], +}; From ddf8bc3fbac7aefbf557e2e4a3e14d8de1b80872 Mon Sep 17 00:00:00 2001 From: Joseph Savona <6425824+josephsavona@users.noreply.github.com> Date: Fri, 1 Aug 2025 13:00:01 -0700 Subject: [PATCH 4/4] [compiler] Improve merging of scopes that invalidate together (#34049) We try to merge consecutive reactive scopes that will always invalidate together, but there's one common case that isn't handled. ```js const y = [[x]]; ``` Here we'll create two consecutive scopes for the inner and outer array expressions. Because the input to the second scope is a temporary, they'll merge into one scope. But if we name the inner array, the merging stops: ```js const array = [x]; const y = [array]; ``` This is because the merging logic checks if all the dependencies of the second scope are outputs of the first scope, but doesn't account for renaming due to LoadLocal/StoreLocal. The fix is to track these temporaries. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34049). * __->__ #34049 * #34047 * #34044 --- ...rgeReactiveScopesThatInvalidateTogether.ts | 30 +++++++- .../compiler/array-at-closure.expect.md | 17 ++--- ...ay-map-captures-receiver-noAlias.expect.md | 26 ++----- ...ay-map-noAlias-escaping-function.expect.md | 16 +--- .../capturing-arrow-function-1.expect.md | 20 ++--- .../compiler/capturing-function-1.expect.md | 20 ++--- ...apturing-function-runs-inference.expect.md | 26 ++----- ...pturing-variable-in-nested-block.expect.md | 20 ++--- ...ring-variable-in-nested-function.expect.md | 22 ++---- ...ved-allocating-nested-dependency.expect.md | 27 +++---- .../fbt/fbt-param-with-newline.expect.md | 17 ++--- .../fbt/fbt-param-with-quotes.expect.md | 29 +++---- .../fbt/fbt-param-with-unicode.expect.md | 25 +++--- .../compiler/fbt/fbt-to-string.expect.md | 29 +++---- ...-maybe-mutates-hook-return-value.expect.md | 17 ++--- ...nction-expression-prototype-call.expect.md | 17 ++--- ...ing-functionexpr-conditional-dep.expect.md | 17 ++--- ...setstate-captured-indirectly-jsx.expect.md | 29 +++---- ...rray-map-named-chained-callbacks.expect.md | 76 ++++++++----------- .../conditional-call-chain.expect.md | 17 ++--- ...se-localvar-memberexpr-in-lambda.expect.md | 16 +--- ...ay-map-captures-receiver-noAlias.expect.md | 26 ++----- .../prop-capturing-function-1.expect.md | 20 ++--- ...function-uncond-access-local-var.expect.md | 16 +--- ...function-uncond-access-local-var.expect.md | 16 +--- ...er-nested-function-uncond-access.expect.md | 17 ++--- ...nfer-object-method-uncond-access.expect.md | 17 ++--- ...scopes-whose-deps-invalidate-jsx.expect.md | 17 ++--- ...-whose-deps-may-invalidate-array.expect.md | 17 ++--- .../compiler/reactive-ref-param.expect.md | 16 +--- .../fixtures/compiler/reactive-ref.expect.md | 16 +--- ...-analysis-interleaved-reactivity.expect.md | 27 ++----- ...ivity-via-aliased-mutation-array.expect.md | 20 +++-- ...vity-via-aliased-mutation-lambda.expect.md | 20 +++-- ...rrent-aliased-not-added-to-dep-2.expect.md | 17 ++--- .../ref-current-not-added-to-dep-2.expect.md | 17 ++--- ...source-variables-nested-function.expect.md | 18 ++--- ...lack-of-phi-types-explicit-types.expect.md | 50 +++++------- ...ng-memoization-lack-of-phi-types.expect.md | 50 +++++------- ...-argument-in-function-expression.expect.md | 25 +++--- ...zen-value-in-function-expression.expect.md | 25 +++--- ...repro-renaming-conflicting-decls.expect.md | 67 ++++++++-------- ...ession-of-jsxexpressioncontainer.expect.md | 17 ++--- ...-expression-returns-caught-value.expect.md | 17 ++--- .../type-annotation-var-array.expect.md | 16 +--- .../type-annotation-var-array_.flow.expect.md | 16 +--- .../compiler/use-memo-simple.expect.md | 16 +--- ...context-in-callback-if-condition.expect.md | 25 +++--- 48 files changed, 401 insertions(+), 733 deletions(-) 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 6f2d97ff8e528..ea2f798770525 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/MergeReactiveScopesThatInvalidateTogether.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/MergeReactiveScopesThatInvalidateTogether.ts @@ -119,6 +119,7 @@ class FindLastUsageVisitor extends ReactiveFunctionVisitor { class Transform extends ReactiveFunctionTransform { lastUsage: Map; + temporaries: Map = new Map(); constructor(lastUsage: Map) { super(); @@ -215,6 +216,12 @@ class Transform extends ReactiveFunctionTransform, ): boolean { // Don't merge scopes with reassignments if ( @@ -465,11 +480,14 @@ function canMergeScopes( (next.scope.dependencies.size !== 0 && [...next.scope.dependencies].every( dep => + dep.path.length === 0 && isAlwaysInvalidatingType(dep.identifier.type) && Iterable_some( current.scope.declarations.values(), decl => - decl.identifier.declarationId === dep.identifier.declarationId, + decl.identifier.declarationId === dep.identifier.declarationId || + decl.identifier.declarationId === + temporaries.get(dep.identifier.declarationId), ), )) ) { @@ -477,8 +495,12 @@ function canMergeScopes( return true; } log(` cannot merge scopes:`); - log(` ${printReactiveScopeSummary(current.scope)}`); - log(` ${printReactiveScopeSummary(next.scope)}`); + log( + ` ${printReactiveScopeSummary(current.scope)} ${[...current.scope.declarations.values()].map(decl => decl.identifier.declarationId)}`, + ); + log( + ` ${printReactiveScopeSummary(next.scope)} ${[...next.scope.dependencies].map(dep => `${dep.identifier.declarationId} ${temporaries.get(dep.identifier.declarationId) ?? dep.identifier.declarationId}`)}`, + ); return false; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-at-closure.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-at-closure.expect.md index 9f26c192f5f87..810f1a3f5e4ee 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-at-closure.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-at-closure.expect.md @@ -19,7 +19,7 @@ function Component(props) { ```javascript import { c as _c } from "react/compiler-runtime"; function Component(props) { - const $ = _c(7); + const $ = _c(5); let t0; if ($[0] !== props.x) { t0 = foo(props.x); @@ -31,26 +31,19 @@ function Component(props) { const x = t0; let t1; if ($[2] !== props || $[3] !== x) { - t1 = function () { + const fn = function () { const arr = [...bar(props)]; return arr.at(x); }; + + t1 = fn(); $[2] = props; $[3] = x; $[4] = t1; } else { t1 = $[4]; } - const fn = t1; - let t2; - if ($[5] !== fn) { - t2 = fn(); - $[5] = fn; - $[6] = t2; - } else { - t2 = $[6]; - } - const fnResult = t2; + const fnResult = t1; return fnResult; } 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 1680386c741a3..efd094c1a5360 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,34 +23,18 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function Component(props) { - const $ = _c(6); + const $ = _c(2); let t0; if ($[0] !== props.a) { - t0 = { a: props.a }; + const item = { a: props.a }; + const items = [item]; + t0 = items.map(_temp); $[0] = props.a; $[1] = t0; } else { t0 = $[1]; } - 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; + const mapped = t0; return mapped; } function _temp(item_0) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-noAlias-escaping-function.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-noAlias-escaping-function.expect.md index 867d51cb232bc..f165502a29b60 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-noAlias-escaping-function.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-noAlias-escaping-function.expect.md @@ -21,26 +21,18 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function Component(props) { - const $ = _c(4); + const $ = _c(2); const f = _temp; let t0; if ($[0] !== props.items) { - t0 = [...props.items].map(f); + const x = [...props.items].map(f); + t0 = [x, f]; $[0] = props.items; $[1] = t0; } else { t0 = $[1]; } - const x = t0; - let t1; - if ($[2] !== x) { - t1 = [x, f]; - $[2] = x; - $[3] = t1; - } else { - t1 = $[3]; - } - return t1; + return t0; } function _temp(item) { return item; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-arrow-function-1.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-arrow-function-1.expect.md index a11b0d8ada4da..85950edabac7a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-arrow-function-1.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-arrow-function-1.expect.md @@ -23,27 +23,19 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function component(a) { - const $ = _c(4); + const $ = _c(2); let t0; if ($[0] !== a) { - t0 = { a }; + const z = { a }; + t0 = () => { + console.log(z); + }; $[0] = a; $[1] = t0; } else { t0 = $[1]; } - const z = t0; - let t1; - if ($[2] !== z) { - t1 = () => { - console.log(z); - }; - $[2] = z; - $[3] = t1; - } else { - t1 = $[3]; - } - const x = t1; + const x = t0; return x; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-1.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-1.expect.md index 206cf4b6b73b0..41732aed0d6e8 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-1.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-1.expect.md @@ -23,27 +23,19 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function component(a) { - const $ = _c(4); + const $ = _c(2); let t0; if ($[0] !== a) { - t0 = { a }; + const z = { a }; + t0 = function () { + console.log(z); + }; $[0] = a; $[1] = t0; } else { t0 = $[1]; } - const z = t0; - let t1; - if ($[2] !== z) { - t1 = function () { - console.log(z); - }; - $[2] = z; - $[3] = t1; - } else { - t1 = $[3]; - } - const x = t1; + const x = t0; return x; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-runs-inference.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-runs-inference.expect.md index dc0961c61264c..31b80bcda3b29 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-runs-inference.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-runs-inference.expect.md @@ -22,35 +22,19 @@ export const FIXTURE_ENTRYPOINT = { import { c as _c } from "react/compiler-runtime"; import { Stringify } from "shared-runtime"; function Component(t0) { - const $ = _c(6); + const $ = _c(2); const { a } = t0; let t1; if ($[0] !== a) { - t1 = { a }; + const z = { a }; + const p = () => {z}; + t1 = p(); $[0] = a; $[1] = t1; } else { t1 = $[1]; } - const z = t1; - let t2; - if ($[2] !== z) { - t2 = () => {z}; - $[2] = z; - $[3] = t2; - } else { - t2 = $[3]; - } - const p = t2; - let t3; - if ($[4] !== p) { - t3 = p(); - $[4] = p; - $[5] = t3; - } else { - t3 = $[5]; - } - return t3; + return t1; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-variable-in-nested-block.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-variable-in-nested-block.expect.md index 0dc10c4851094..ec8a96a392678 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-variable-in-nested-block.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-variable-in-nested-block.expect.md @@ -25,27 +25,19 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function component(a) { - const $ = _c(4); + const $ = _c(2); let t0; if ($[0] !== a) { - t0 = { a }; + const z = { a }; + t0 = function () { + console.log(z); + }; $[0] = a; $[1] = t0; } else { t0 = $[1]; } - const z = t0; - let t1; - if ($[2] !== z) { - t1 = function () { - console.log(z); - }; - $[2] = z; - $[3] = t1; - } else { - t1 = $[3]; - } - const x = t1; + const x = t0; return x; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-variable-in-nested-function.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-variable-in-nested-function.expect.md index b66953a43d353..ee41bc88f5eef 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-variable-in-nested-function.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-variable-in-nested-function.expect.md @@ -25,29 +25,21 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function component(a) { - const $ = _c(4); + const $ = _c(2); let t0; if ($[0] !== a) { - t0 = { a }; - $[0] = a; - $[1] = t0; - } else { - t0 = $[1]; - } - const z = t0; - let t1; - if ($[2] !== z) { - t1 = function () { + const z = { a }; + t0 = function () { (function () { console.log(z); })(); }; - $[2] = z; - $[3] = t1; + $[0] = a; + $[1] = t0; } else { - t1 = $[3]; + t0 = $[1]; } - const x = t1; + const x = t0; return x; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/escape-analysis-non-escaping-interleaved-allocating-nested-dependency.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/escape-analysis-non-escaping-interleaved-allocating-nested-dependency.expect.md index 64355ca6262f1..cee338b14e579 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/escape-analysis-non-escaping-interleaved-allocating-nested-dependency.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/escape-analysis-non-escaping-interleaved-allocating-nested-dependency.expect.md @@ -40,36 +40,29 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function Component(props) { - const $ = _c(7); + const $ = _c(5); let t0; if ($[0] !== props.a) { - t0 = [props.a]; + const a = [props.a]; + + t0 = [a]; $[0] = props.a; $[1] = t0; } else { t0 = $[1]; } - const a = t0; - let t1; - if ($[2] !== a) { - t1 = [a]; - $[2] = a; - $[3] = t1; - } else { - t1 = $[3]; - } - const b = t1; + const b = t0; let c; - if ($[4] !== b || $[5] !== props.b) { + if ($[2] !== b || $[3] !== props.b) { c = []; const d = {}; d.b = b; c.push(props.b); - $[4] = b; - $[5] = props.b; - $[6] = c; + $[2] = b; + $[3] = props.b; + $[4] = c; } else { - c = $[6]; + c = $[4]; } return c; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/fbt-param-with-newline.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/fbt-param-with-newline.expect.md index bb1dcba183070..28c2a8e03b539 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/fbt-param-with-newline.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/fbt-param-with-newline.expect.md @@ -32,10 +32,10 @@ import { c as _c } from "react/compiler-runtime"; import fbt from "fbt"; function Component(props) { - const $ = _c(4); + const $ = _c(2); let t0; if ($[0] !== props.name) { - t0 = fbt._( + const element = fbt._( "Hello {a really long description that got split into multiple lines}", [ fbt._param( @@ -46,21 +46,14 @@ function Component(props) { ], { hk: "1euPUp" }, ); + + t0 = element.toString(); $[0] = props.name; $[1] = t0; } else { t0 = $[1]; } - const element = t0; - let t1; - if ($[2] !== element) { - t1 = element.toString(); - $[2] = element; - $[3] = t1; - } else { - t1 = $[3]; - } - return t1; + return t0; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/fbt-param-with-quotes.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/fbt-param-with-quotes.expect.md index b1543be773eda..eb41b86fdfdb9 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/fbt-param-with-quotes.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/fbt-param-with-quotes.expect.md @@ -27,27 +27,28 @@ import { c as _c } from "react/compiler-runtime"; import fbt from "fbt"; function Component(props) { - const $ = _c(4); + const $ = _c(2); let t0; if ($[0] !== props.name) { - t0 = fbt._('Hello {"user" name}', [fbt._param('"user" name', props.name)], { - hk: "S0vMe", - }); + const element = fbt._( + 'Hello {"user" name}', + [ + fbt._param( + '"user" name', + + props.name, + ), + ], + { hk: "S0vMe" }, + ); + + t0 = element.toString(); $[0] = props.name; $[1] = t0; } else { t0 = $[1]; } - const element = t0; - let t1; - if ($[2] !== element) { - t1 = element.toString(); - $[2] = element; - $[3] = t1; - } else { - t1 = $[3]; - } - return t1; + return t0; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/fbt-param-with-unicode.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/fbt-param-with-unicode.expect.md index c13f618330d18..60d234974665a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/fbt-param-with-unicode.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/fbt-param-with-unicode.expect.md @@ -27,29 +27,28 @@ import { c as _c } from "react/compiler-runtime"; import fbt from "fbt"; function Component(props) { - const $ = _c(4); + const $ = _c(2); let t0; if ($[0] !== props.name) { - t0 = fbt._( + const element = fbt._( "Hello {user name ☺}", - [fbt._param("user name \u263A", props.name)], + [ + fbt._param( + "user name \u263A", + + props.name, + ), + ], { hk: "1En1lp" }, ); + + t0 = element.toString(); $[0] = props.name; $[1] = t0; } else { t0 = $[1]; } - const element = t0; - let t1; - if ($[2] !== element) { - t1 = element.toString(); - $[2] = element; - $[3] = t1; - } else { - t1 = $[3]; - } - return t1; + return t0; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/fbt-to-string.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/fbt-to-string.expect.md index ff7fe4569f0fa..eca12d2404c61 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/fbt-to-string.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/fbt-to-string.expect.md @@ -27,27 +27,28 @@ import { c as _c } from "react/compiler-runtime"; import fbt from "fbt"; function Component(props) { - const $ = _c(4); + const $ = _c(2); let t0; if ($[0] !== props.name) { - t0 = fbt._("Hello {user name}", [fbt._param("user name", props.name)], { - hk: "2zEDKF", - }); + const element = fbt._( + "Hello {user name}", + [ + fbt._param( + "user name", + + props.name, + ), + ], + { hk: "2zEDKF" }, + ); + + t0 = element.toString(); $[0] = props.name; $[1] = t0; } else { t0 = $[1]; } - const element = t0; - let t1; - if ($[2] !== element) { - t1 = element.toString(); - $[2] = element; - $[3] = t1; - } else { - t1 = $[3]; - } - return t1; + return t0; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/function-expression-maybe-mutates-hook-return-value.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/function-expression-maybe-mutates-hook-return-value.expect.md index 0f65c54583a5b..894c1ecc1d1e0 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/function-expression-maybe-mutates-hook-return-value.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/function-expression-maybe-mutates-hook-return-value.expect.md @@ -22,28 +22,21 @@ function Component(props) { ```javascript import { c as _c } from "react/compiler-runtime"; function Component(props) { - const $ = _c(4); + const $ = _c(2); const id = useSelectedEntitytId(); let t0; if ($[0] !== id) { - t0 = () => { + const onLoad = () => { log(id); }; + + t0 = ; $[0] = id; $[1] = t0; } else { t0 = $[1]; } - const onLoad = t0; - let t1; - if ($[2] !== onLoad) { - t1 = ; - $[2] = onLoad; - $[3] = t1; - } else { - t1 = $[3]; - } - return t1; + return t0; } ``` diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/function-expression-prototype-call.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/function-expression-prototype-call.expect.md index 2df5b908902d9..714e61eb890e0 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/function-expression-prototype-call.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/function-expression-prototype-call.expect.md @@ -21,27 +21,20 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function Component(props) { - const $ = _c(4); + const $ = _c(2); let t0; if ($[0] !== props) { - t0 = function () { + const f = function () { return
{props.name}
; }; + + t0 = f.call(); $[0] = props; $[1] = t0; } else { t0 = $[1]; } - const f = t0; - let t1; - if ($[2] !== f) { - t1 = f.call(); - $[2] = f; - $[3] = t1; - } else { - t1 = $[3]; - } - return t1; + return t0; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-functionexpr-conditional-dep.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-functionexpr-conditional-dep.expect.md index e99e9e1c80226..96a1d3e5726cd 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-functionexpr-conditional-dep.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-functionexpr-conditional-dep.expect.md @@ -55,33 +55,26 @@ import { Stringify } from "shared-runtime"; * (kind: exception) Cannot read properties of null (reading 'prop') */ function Component(t0) { - const $ = _c(5); + const $ = _c(3); const { obj, isObjNull } = t0; let t1; if ($[0] !== isObjNull || $[1] !== obj) { - t1 = () => { + const callback = () => { if (!isObjNull) { return obj.prop; } else { return null; } }; + + t1 = ; $[0] = isObjNull; $[1] = obj; $[2] = t1; } else { t1 = $[2]; } - const callback = t1; - let t2; - if ($[3] !== callback) { - t2 = ; - $[3] = callback; - $[4] = t2; - } else { - t2 = $[4]; - } - return t2; + return t1; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-setstate-captured-indirectly-jsx.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-setstate-captured-indirectly-jsx.expect.md index 55cab1e9f3bd1..c0c2bff9f7a2b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-setstate-captured-indirectly-jsx.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/hoisting-setstate-captured-indirectly-jsx.expect.md @@ -27,7 +27,7 @@ function useFoo() { ```javascript import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees function useFoo() { - const $ = _c(9); + const $ = _c(7); const onClick = (response) => { setState(DISABLED_FORM); }; @@ -48,31 +48,24 @@ function useFoo() { const handleLogout = t1; let t2; if ($[2] !== handleLogout) { - t2 = () => handleLogout()} />; + const getComponent = () => handleLogout()} />; + + t2 = getComponent(); $[2] = handleLogout; $[3] = t2; } else { t2 = $[3]; } - const getComponent = t2; let t3; - if ($[4] !== getComponent) { - t3 = getComponent(); - $[4] = getComponent; - $[5] = t3; - } else { - t3 = $[5]; - } - let t4; - if ($[6] !== onClick || $[7] !== t3) { - t4 = [t3, onClick]; - $[6] = onClick; - $[7] = t3; - $[8] = t4; + if ($[4] !== onClick || $[5] !== t2) { + t3 = [t2, onClick]; + $[4] = onClick; + $[5] = t2; + $[6] = t3; } else { - t4 = $[8]; + t3 = $[6]; } - return t4; + return t3; } ``` diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-chained-callbacks.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-chained-callbacks.expect.md index 9bf77fd1276c0..96ec12d5eaba9 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-chained-callbacks.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/array-map-named-chained-callbacks.expect.md @@ -42,74 +42,58 @@ import { c as _c } from "react/compiler-runtime"; /** * conservative and assume that all named lambdas are conditionally called. */ function useFoo(t0) { - const $ = _c(17); + const $ = _c(13); const { arr1, arr2 } = t0; let t1; if ($[0] !== arr1[0]) { - t1 = () => arr1[0].value; + const getVal1 = () => arr1[0].value; + t1 = (e) => getVal1() + e.value; $[0] = arr1[0]; $[1] = t1; } else { t1 = $[1]; } - const getVal1 = t1; + const cb1 = t1; let t2; - if ($[2] !== getVal1) { - t2 = (e) => getVal1() + e.value; - $[2] = getVal1; - $[3] = t2; + if ($[2] !== arr1 || $[3] !== cb1) { + t2 = arr1.map(cb1); + $[2] = arr1; + $[3] = cb1; + $[4] = t2; } else { - t2 = $[3]; + t2 = $[4]; } - const cb1 = t2; + const x = t2; let t3; - if ($[4] !== arr1 || $[5] !== cb1) { - t3 = arr1.map(cb1); - $[4] = arr1; - $[5] = cb1; + if ($[5] !== arr2) { + const getVal2 = () => arr2[0].value; + t3 = (e_0) => getVal2() + e_0.value; + $[5] = arr2; $[6] = t3; } else { t3 = $[6]; } - const x = t3; + const cb2 = t3; let t4; - if ($[7] !== arr2) { - t4 = () => arr2[0].value; - $[7] = arr2; - $[8] = t4; + if ($[7] !== arr1 || $[8] !== cb2) { + t4 = arr1.map(cb2); + $[7] = arr1; + $[8] = cb2; + $[9] = t4; } else { - t4 = $[8]; + t4 = $[9]; } - const getVal2 = t4; + const y = t4; let t5; - if ($[9] !== getVal2) { - t5 = (e_0) => getVal2() + e_0.value; - $[9] = getVal2; - $[10] = t5; + if ($[10] !== x || $[11] !== y) { + t5 = [x, y]; + $[10] = x; + $[11] = y; + $[12] = t5; } else { - t5 = $[10]; + t5 = $[12]; } - const cb2 = t5; - let t6; - if ($[11] !== arr1 || $[12] !== cb2) { - t6 = arr1.map(cb2); - $[11] = arr1; - $[12] = cb2; - $[13] = t6; - } else { - t6 = $[13]; - } - const y = t6; - let t7; - if ($[14] !== x || $[15] !== y) { - t7 = [x, y]; - $[14] = x; - $[15] = y; - $[16] = t7; - } else { - t7 = $[16]; - } - return t7; + return t5; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call-chain.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call-chain.expect.md index e0dc1eeb5f7c4..6114996b896aa 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call-chain.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call-chain.expect.md @@ -42,7 +42,7 @@ import { useRef } from "react"; import { Stringify } from "shared-runtime"; function Component(t0) { - const $ = _c(9); + const $ = _c(7); const { a, b } = t0; let t1; if ($[0] !== a.value) { @@ -70,29 +70,22 @@ function Component(t0) { const hasLogged = useRef(false); let t3; if ($[4] !== logA || $[5] !== logB) { - t3 = () => { + const log = () => { if (!hasLogged.current) { logA(); logB(); hasLogged.current = true; } }; + + t3 = ; $[4] = logA; $[5] = logB; $[6] = t3; } else { t3 = $[6]; } - const log = t3; - let t4; - if ($[7] !== log) { - t4 = ; - $[7] = log; - $[8] = t4; - } else { - t4 = $[8]; - } - return t4; + return t3; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-lowercase-localvar-memberexpr-in-lambda.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-lowercase-localvar-memberexpr-in-lambda.expect.md index 248234793931e..31aa7c77a1eae 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-lowercase-localvar-memberexpr-in-lambda.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-lowercase-localvar-memberexpr-in-lambda.expect.md @@ -24,28 +24,20 @@ import { c as _c } from "react/compiler-runtime"; import * as SharedRuntime from "shared-runtime"; import { invoke } from "shared-runtime"; function useComponentFactory(t0) { - const $ = _c(4); + const $ = _c(2); const { name } = t0; let t1; if ($[0] !== name) { - t1 = () => ( + const cb = () => ( hello world {name} ); + t1 = invoke(cb); $[0] = name; $[1] = t1; } else { t1 = $[1]; } - const cb = t1; - let t2; - if ($[2] !== cb) { - t2 = invoke(cb); - $[2] = cb; - $[3] = t2; - } else { - t2 = $[3]; - } - return t2; + return t1; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/array-map-captures-receiver-noAlias.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/array-map-captures-receiver-noAlias.expect.md index e687c995d077f..70872381885fd 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/array-map-captures-receiver-noAlias.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/array-map-captures-receiver-noAlias.expect.md @@ -25,34 +25,18 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; // @enableNewMutationAliasingModel function Component(props) { - const $ = _c(6); + const $ = _c(2); let t0; if ($[0] !== props.a) { - t0 = { a: props.a }; + const item = { a: props.a }; + const items = [item]; + t0 = items.map(_temp); $[0] = props.a; $[1] = t0; } else { t0 = $[1]; } - 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; + const mapped = t0; return mapped; } function _temp(item_0) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/prop-capturing-function-1.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/prop-capturing-function-1.expect.md index a7f50325dd75c..9bfd9cf06adc1 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/prop-capturing-function-1.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/prop-capturing-function-1.expect.md @@ -23,28 +23,20 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function component(a, b) { - const $ = _c(5); + const $ = _c(3); let t0; if ($[0] !== a || $[1] !== b) { - t0 = { a, b }; + const z = { a, b }; + t0 = function () { + console.log(z); + }; $[0] = a; $[1] = b; $[2] = t0; } else { t0 = $[2]; } - const z = t0; - let t1; - if ($[3] !== z) { - t1 = function () { - console.log(z); - }; - $[3] = z; - $[4] = t1; - } else { - t1 = $[4]; - } - const x = t1; + const x = t0; return x; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/reduce-reactive-deps/infer-function-uncond-access-local-var.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/reduce-reactive-deps/infer-function-uncond-access-local-var.expect.md index 741a30d7de24d..41bab7ccc9d64 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/reduce-reactive-deps/infer-function-uncond-access-local-var.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/reduce-reactive-deps/infer-function-uncond-access-local-var.expect.md @@ -29,7 +29,7 @@ import { c as _c } from "react/compiler-runtime"; // @enablePropagateDepsInHIR import { mutate, shallowCopy, Stringify } from "shared-runtime"; function useFoo(t0) { - const $ = _c(6); + const $ = _c(4); const { a } = t0; let local; if ($[0] !== a) { @@ -42,22 +42,14 @@ function useFoo(t0) { } let t1; if ($[2] !== local.b.c) { - t1 = () => local.b.c; + const fn = () => local.b.c; + t1 = ; $[2] = local.b.c; $[3] = t1; } else { t1 = $[3]; } - const fn = t1; - let t2; - if ($[4] !== fn) { - t2 = ; - $[4] = fn; - $[5] = t2; - } else { - t2 = $[5]; - } - return t2; + return t1; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/reduce-reactive-deps/infer-nested-function-uncond-access-local-var.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/reduce-reactive-deps/infer-nested-function-uncond-access-local-var.expect.md index 53d3d04531bda..8a090ef896609 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/reduce-reactive-deps/infer-nested-function-uncond-access-local-var.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/reduce-reactive-deps/infer-nested-function-uncond-access-local-var.expect.md @@ -29,7 +29,7 @@ import { c as _c } from "react/compiler-runtime"; // @enablePropagateDepsInHIR import { shallowCopy, Stringify, mutate } from "shared-runtime"; function useFoo(t0) { - const $ = _c(6); + const $ = _c(4); const { a } = t0; let local; if ($[0] !== a) { @@ -42,22 +42,14 @@ function useFoo(t0) { } let t1; if ($[2] !== local) { - t1 = () => [() => local.b.c]; + const fn = () => [() => local.b.c]; + t1 = ; $[2] = local; $[3] = t1; } else { t1 = $[3]; } - const fn = t1; - let t2; - if ($[4] !== fn) { - t2 = ; - $[4] = fn; - $[5] = t2; - } else { - t2 = $[5]; - } - return t2; + return t1; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/reduce-reactive-deps/infer-nested-function-uncond-access.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/reduce-reactive-deps/infer-nested-function-uncond-access.expect.md index 22b17977cbcbe..7ffc258b3d6dc 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/reduce-reactive-deps/infer-nested-function-uncond-access.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/reduce-reactive-deps/infer-nested-function-uncond-access.expect.md @@ -31,26 +31,19 @@ import { c as _c } from "react/compiler-runtime"; // @enablePropagateDepsInHIR import { Stringify } from "shared-runtime"; function useFoo(t0) { - const $ = _c(4); + const $ = _c(2); const { a } = t0; let t1; if ($[0] !== a.b.c) { - t1 = () => () => ({ value: a.b.c }); + const fn = () => () => ({ value: a.b.c }); + + t1 = ; $[0] = a.b.c; $[1] = t1; } else { t1 = $[1]; } - const fn = t1; - let t2; - if ($[2] !== fn) { - t2 = ; - $[2] = fn; - $[3] = t2; - } else { - t2 = $[3]; - } - return t2; + return t1; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/reduce-reactive-deps/infer-object-method-uncond-access.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/reduce-reactive-deps/infer-object-method-uncond-access.expect.md index f8a8af1fd4f41..1c01c96b83717 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/reduce-reactive-deps/infer-object-method-uncond-access.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/reduce-reactive-deps/infer-object-method-uncond-access.expect.md @@ -31,30 +31,23 @@ import { c as _c } from "react/compiler-runtime"; // @enablePropagateDepsInHIR import { identity, Stringify } from "shared-runtime"; function useFoo(t0) { - const $ = _c(4); + const $ = _c(2); const { a } = t0; let t1; if ($[0] !== a) { - t1 = { + const x = { fn() { return identity(a.b.c); }, }; + + t1 = ; $[0] = a; $[1] = t1; } else { t1 = $[1]; } - const x = t1; - let t2; - if ($[2] !== x) { - t2 = ; - $[2] = x; - $[3] = t2; - } else { - t2 = $[3]; - } - return t2; + return t1; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/prune-scopes-whose-deps-invalidate-jsx.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/prune-scopes-whose-deps-invalidate-jsx.expect.md index 291ba39014398..840b79a06b5c5 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/prune-scopes-whose-deps-invalidate-jsx.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/prune-scopes-whose-deps-invalidate-jsx.expect.md @@ -29,7 +29,7 @@ import { c as _c } from "react/compiler-runtime"; import { useHook } from "shared-runtime"; function Component(props) { - const $ = _c(6); + const $ = _c(4); const o = {}; let t0; if ($[0] !== props.value) { @@ -44,22 +44,15 @@ function Component(props) { o.value = props.value; let t1; if ($[2] !== x) { - t1 =
{x}
; + const y =
{x}
; + + t1 =
{y}
; $[2] = x; $[3] = t1; } else { t1 = $[3]; } - const y = t1; - let t2; - if ($[4] !== y) { - t2 =
{y}
; - $[4] = y; - $[5] = t2; - } else { - t2 = $[5]; - } - return t2; + return t1; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/prune-scopes-whose-deps-may-invalidate-array.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/prune-scopes-whose-deps-may-invalidate-array.expect.md index 91883a9bdf0e9..953cbef5a7e8a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/prune-scopes-whose-deps-may-invalidate-array.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/prune-scopes-whose-deps-may-invalidate-array.expect.md @@ -31,7 +31,7 @@ import { c as _c } from "react/compiler-runtime"; import { useHook, identity } from "shared-runtime"; function Component(props) { - const $ = _c(4); + const $ = _c(2); let x = 42; if (props.cond) { x = []; @@ -41,22 +41,15 @@ function Component(props) { identity(x); let t0; if ($[0] !== x) { - t0 = [x]; + const y = [x]; + + t0 = [y]; $[0] = x; $[1] = t0; } else { t0 = $[1]; } - const y = t0; - let t1; - if ($[2] !== y) { - t1 = [y]; - $[2] = y; - $[3] = t1; - } else { - t1 = $[3]; - } - return t1; + return t0; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-ref-param.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-ref-param.expect.md index c8922ab0c46fa..96d97e99abfb5 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-ref-param.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-ref-param.expect.md @@ -66,25 +66,17 @@ function Parent(t0) { } function ChildImpl(_props, ref) { - const $ = _c(4); + const $ = _c(2); let t0; if ($[0] !== ref) { - t0 = () => ref.current; + const cb = () => ref.current; + t0 = ; $[0] = ref; $[1] = t0; } else { t0 = $[1]; } - const cb = t0; - let t1; - if ($[2] !== cb) { - t1 = ; - $[2] = cb; - $[3] = t1; - } else { - t1 = $[3]; - } - return t1; + return t0; } const Child = forwardRef(ChildImpl); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-ref.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-ref.expect.md index c3876c22924fe..85969ce25ea16 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-ref.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-ref.expect.md @@ -41,29 +41,21 @@ import { Stringify } from "shared-runtime"; * `pruneNonReactiveDependencies` */ function Component(t0) { - const $ = _c(4); + const $ = _c(2); const { cond } = t0; const ref1 = useRef(1); const ref2 = useRef(2); const ref = cond ? ref1 : ref2; let t1; if ($[0] !== ref) { - t1 = () => ref.current; + const cb = () => ref.current; + t1 = ; $[0] = ref; $[1] = t1; } else { t1 = $[1]; } - const cb = t1; - let t2; - if ($[2] !== cb) { - t2 = ; - $[2] = cb; - $[3] = t2; - } else { - t2 = $[3]; - } - return t2; + return t1; } export const FIXTURE_ENTRYPOINT = { 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 c6331bd4a0873..28263b6dbafc9 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,34 +35,23 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function Component(props) { - const $ = _c(6); - let a; + const $ = _c(2); let t0; if ($[0] !== props.b) { - a = {}; + const a = {}; const b = []; b.push(props.b); a.a = null; - t0 = [a]; + const c = [a]; + + t0 = [c, a]; $[0] = props.b; - $[1] = a; - $[2] = t0; - } else { - a = $[1]; - t0 = $[2]; - } - const c = t0; - let t1; - if ($[3] !== a || $[4] !== c) { - t1 = [c, a]; - $[3] = a; - $[4] = c; - $[5] = t1; + $[1] = t0; } else { - t1 = $[5]; + t0 = $[1]; } - return t1; + return t0; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactivity-via-aliased-mutation-array.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactivity-via-aliased-mutation-array.expect.md index b9ff24519e50f..1d58d44ef3114 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactivity-via-aliased-mutation-array.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactivity-via-aliased-mutation-array.expect.md @@ -32,18 +32,24 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function Component(props) { - const $ = _c(2); - let t0; + const $ = _c(4); + let x; if ($[0] !== props.input) { - const x = []; + x = []; const y = x; y.push(props.input); - - t0 = [x[0]]; $[0] = props.input; - $[1] = t0; + $[1] = x; + } else { + x = $[1]; + } + let t0; + if ($[2] !== x[0]) { + t0 = [x[0]]; + $[2] = x[0]; + $[3] = t0; } else { - t0 = $[1]; + t0 = $[3]; } return t0; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactivity-via-aliased-mutation-lambda.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactivity-via-aliased-mutation-lambda.expect.md index ebf30d9d28560..36d05dda69c27 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactivity-via-aliased-mutation-lambda.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactivity-via-aliased-mutation-lambda.expect.md @@ -35,22 +35,28 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function Component(props) { - const $ = _c(2); - let t0; + const $ = _c(4); + let x; if ($[0] !== props.input) { - const x = []; + x = []; const f = (arg) => { const y = x; y.push(arg); }; f(props.input); - - t0 = [x[0]]; $[0] = props.input; - $[1] = t0; + $[1] = x; + } else { + x = $[1]; + } + let t0; + if ($[2] !== x[0]) { + t0 = [x[0]]; + $[2] = x[0]; + $[3] = t0; } else { - t0 = $[1]; + t0 = $[3]; } return t0; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ref-current-aliased-not-added-to-dep-2.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ref-current-aliased-not-added-to-dep-2.expect.md index f6b90ea1d5f0a..63268de4271ae 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ref-current-aliased-not-added-to-dep-2.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ref-current-aliased-not-added-to-dep-2.expect.md @@ -18,28 +18,21 @@ function Foo({a}) { ```javascript import { c as _c } from "react/compiler-runtime"; // @validateRefAccessDuringRender:false function Foo(t0) { - const $ = _c(4); + const $ = _c(2); const { a } = t0; const ref = useRef(); const val = ref.current; let t1; if ($[0] !== a) { - t1 = { a, val }; + const x = { a, val }; + + t1 = ; $[0] = a; $[1] = t1; } else { t1 = $[1]; } - const x = t1; - let t2; - if ($[2] !== x) { - t2 = ; - $[2] = x; - $[3] = t2; - } else { - t2 = $[3]; - } - return t2; + return t1; } ``` diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ref-current-not-added-to-dep-2.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ref-current-not-added-to-dep-2.expect.md index da0b97859a5ff..76a1b00776e5a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ref-current-not-added-to-dep-2.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ref-current-not-added-to-dep-2.expect.md @@ -17,27 +17,20 @@ function Foo({a}) { ```javascript import { c as _c } from "react/compiler-runtime"; // @validateRefAccessDuringRender:false function Foo(t0) { - const $ = _c(4); + const $ = _c(2); const { a } = t0; const ref = useRef(); let t1; if ($[0] !== a) { - t1 = { a, val: ref.current }; + const x = { a, val: ref.current }; + + t1 = ; $[0] = a; $[1] = t1; } else { t1 = $[1]; } - const x = t1; - let t2; - if ($[2] !== x) { - t2 = ; - $[2] = x; - $[3] = t2; - } else { - t2 = $[3]; - } - return t2; + return t1; } ``` diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rename-source-variables-nested-function.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rename-source-variables-nested-function.expect.md index 3287997cf76fc..f9471d9950180 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rename-source-variables-nested-function.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rename-source-variables-nested-function.expect.md @@ -41,11 +41,11 @@ const $ = "module_$"; const t0 = "module_t0"; const c_0 = "module_c_0"; function useFoo(props) { - const $0 = _c(4); + const $0 = _c(2); const c_00 = $0[0] !== props.value; let t1; if (c_00) { - t1 = () => { + const a = () => { const b = () => { const c = () => { console.log($); @@ -57,22 +57,14 @@ function useFoo(props) { }; return b; }; + + t1 = a()()(); $0[0] = props.value; $0[1] = t1; } else { t1 = $0[1]; } - const a = t1; - const c_2 = $0[2] !== a; - let t2; - if (c_2) { - t2 = a()()(); - $0[2] = a; - $0[3] = t2; - } else { - t2 = $0[3]; - } - return t2; + return t1; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-missing-memoization-lack-of-phi-types-explicit-types.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-missing-memoization-lack-of-phi-types-explicit-types.expect.md index 1d5ebaad9ac0f..15bc857ac76f0 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-missing-memoization-lack-of-phi-types-explicit-types.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-missing-memoization-lack-of-phi-types-explicit-types.expect.md @@ -35,60 +35,44 @@ import { useMemo } from "react"; import { useFragment } from "shared-runtime"; function Component() { - const $ = _c(11); + const $ = _c(7); const data = useFragment(); let t0; if ($[0] !== data.nodes) { - t0 = data.nodes ?? []; + const nodes = data.nodes ?? []; + const flatMap = nodes.flatMap(_temp); + t0 = flatMap.filter(_temp2); $[0] = data.nodes; $[1] = t0; } else { t0 = $[1]; } - const nodes = t0; + const filtered = t0; let t1; - if ($[2] !== nodes) { - t1 = nodes.flatMap(_temp); - $[2] = nodes; + if ($[2] !== filtered) { + t1 = filtered.map(); + $[2] = filtered; $[3] = t1; } else { t1 = $[3]; } - const flatMap = t1; - let t2; - if ($[4] !== flatMap) { - t2 = flatMap.filter(_temp2); - $[4] = flatMap; - $[5] = t2; - } else { - t2 = $[5]; - } - const filtered = t2; - let t3; - if ($[6] !== filtered) { - t3 = filtered.map(); - $[6] = filtered; - $[7] = t3; - } else { - t3 = $[7]; - } - const map = t3; + const map = t1; const index = filtered.findIndex(_temp3); - let t4; - if ($[8] !== index || $[9] !== map) { - t4 = ( + let t2; + if ($[4] !== index || $[5] !== map) { + t2 = (
{map} {index}
); - $[8] = index; - $[9] = map; - $[10] = t4; + $[4] = index; + $[5] = map; + $[6] = t2; } else { - t4 = $[10]; + t2 = $[6]; } - return t4; + return t2; } function _temp3(x) { return x === null; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-missing-memoization-lack-of-phi-types.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-missing-memoization-lack-of-phi-types.expect.md index 8c68340b7f254..4728226c64ceb 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-missing-memoization-lack-of-phi-types.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-missing-memoization-lack-of-phi-types.expect.md @@ -32,60 +32,44 @@ import { useMemo } from "react"; import { useFragment } from "shared-runtime"; function Component() { - const $ = _c(11); + const $ = _c(7); const data = useFragment(); let t0; if ($[0] !== data.nodes) { - t0 = data.nodes ?? []; + const nodes = data.nodes ?? []; + const flatMap = nodes.flatMap(_temp); + t0 = flatMap.filter(_temp2); $[0] = data.nodes; $[1] = t0; } else { t0 = $[1]; } - const nodes = t0; + const filtered = t0; let t1; - if ($[2] !== nodes) { - t1 = nodes.flatMap(_temp); - $[2] = nodes; + if ($[2] !== filtered) { + t1 = filtered.map(); + $[2] = filtered; $[3] = t1; } else { t1 = $[3]; } - const flatMap = t1; - let t2; - if ($[4] !== flatMap) { - t2 = flatMap.filter(_temp2); - $[4] = flatMap; - $[5] = t2; - } else { - t2 = $[5]; - } - const filtered = t2; - let t3; - if ($[6] !== filtered) { - t3 = filtered.map(); - $[6] = filtered; - $[7] = t3; - } else { - t3 = $[7]; - } - const map = t3; + const map = t1; const index = filtered.findIndex(_temp3); - let t4; - if ($[8] !== index || $[9] !== map) { - t4 = ( + let t2; + if ($[4] !== index || $[5] !== map) { + t2 = (
{map} {index}
); - $[8] = index; - $[9] = map; - $[10] = t4; + $[4] = index; + $[5] = map; + $[6] = t2; } else { - t4 = $[10]; + t2 = $[6]; } - return t4; + return t2; } function _temp3(x) { return x === null; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-mutate-result-of-function-call-with-frozen-argument-in-function-expression.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-mutate-result-of-function-call-with-frozen-argument-in-function-expression.expect.md index 320b252bb5cbf..ef65f6026e5ac 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-mutate-result-of-function-call-with-frozen-argument-in-function-expression.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-mutate-result-of-function-call-with-frozen-argument-in-function-expression.expect.md @@ -30,40 +30,33 @@ import { c as _c } from "react/compiler-runtime"; import { identity, makeObject_Primitives, Stringify } from "shared-runtime"; function Example(props) { - const $ = _c(7); + const $ = _c(5); const object = props.object; let t0; if ($[0] !== object || $[1] !== props.value) { - t0 = () => { + const f = () => { const obj = identity(object); obj.property = props.value; return obj; }; + + t0 = f(); $[0] = object; $[1] = props.value; $[2] = t0; } else { t0 = $[2]; } - const f = t0; + const obj_0 = t0; let t1; - if ($[3] !== f) { - t1 = f(); - $[3] = f; + if ($[3] !== obj_0) { + t1 = ; + $[3] = obj_0; $[4] = t1; } else { t1 = $[4]; } - const obj_0 = t1; - let t2; - if ($[5] !== obj_0) { - t2 = ; - $[5] = obj_0; - $[6] = t2; - } else { - t2 = $[6]; - } - return t2; + return t1; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-mutate-result-of-method-call-on-frozen-value-in-function-expression.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-mutate-result-of-method-call-on-frozen-value-in-function-expression.expect.md index d3dbb86711a79..4df503bcd2231 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-mutate-result-of-method-call-on-frozen-value-in-function-expression.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-mutate-result-of-method-call-on-frozen-value-in-function-expression.expect.md @@ -30,40 +30,33 @@ import { c as _c } from "react/compiler-runtime"; import { makeObject_Primitives, Stringify } from "shared-runtime"; function Example(props) { - const $ = _c(7); + const $ = _c(5); const object = props.object; let t0; if ($[0] !== object || $[1] !== props.value) { - t0 = () => { + const f = () => { const obj = object.makeObject(); obj.property = props.value; return obj; }; + + t0 = f(); $[0] = object; $[1] = props.value; $[2] = t0; } else { t0 = $[2]; } - const f = t0; + const obj_0 = t0; let t1; - if ($[3] !== f) { - t1 = f(); - $[3] = f; + if ($[3] !== obj_0) { + t1 = ; + $[3] = obj_0; $[4] = t1; } else { t1 = $[4]; } - const obj_0 = t1; - let t2; - if ($[5] !== obj_0) { - t2 = ; - $[5] = obj_0; - $[6] = t2; - } else { - t2 = $[6]; - } - return t2; + return t1; } export const FIXTURE_ENTRYPOINT = { 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 7db207a56226b..4f2b711d89a58 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 @@ -45,7 +45,7 @@ import { Stringify, identity, makeArray, toJSON } from "shared-runtime"; import { useMemo } from "react"; function Component(props) { - const $ = _c(12); + const $ = _c(10); let t0; let t1; if ($[0] !== props) { @@ -71,57 +71,50 @@ function Component(props) { } let t2; if ($[3] !== t0) { - t2 = { url: t0 }; - $[3] = t0; - $[4] = t2; - } else { - t2 = $[4]; - } - const linkProps = t2; - let t3; - if ($[5] !== linkProps) { + const linkProps = { url: t0 }; + const x = {}; + let t3; let t4; let t5; let t6; let t7; - let t8; - if ($[7] === Symbol.for("react.memo_cache_sentinel")) { - t4 = [1]; - t5 = [2]; - t6 = [3]; - t7 = [4]; - t8 = [5]; - $[7] = t4; - $[8] = t5; - $[9] = t6; - $[10] = t7; - $[11] = t8; + if ($[5] === Symbol.for("react.memo_cache_sentinel")) { + t3 = [1]; + t4 = [2]; + t5 = [3]; + t6 = [4]; + t7 = [5]; + $[5] = t3; + $[6] = t4; + $[7] = t5; + $[8] = t6; + $[9] = t7; } else { - t4 = $[7]; - t5 = $[8]; - t6 = $[9]; - t7 = $[10]; - t8 = $[11]; + t3 = $[5]; + t4 = $[6]; + t5 = $[7]; + t6 = $[8]; + t7 = $[9]; } - t3 = ( + t2 = ( {makeArray(x, 2)} ); - $[5] = linkProps; - $[6] = t3; + $[3] = t0; + $[4] = t2; } else { - t3 = $[6]; + t2 = $[4]; } - return t3; + return t2; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-undefined-expression-of-jsxexpressioncontainer.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-undefined-expression-of-jsxexpressioncontainer.expect.md index 9d41b7de21f53..c5a34bc4cad86 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-undefined-expression-of-jsxexpressioncontainer.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-undefined-expression-of-jsxexpressioncontainer.expect.md @@ -48,28 +48,21 @@ import { c as _c } from "react/compiler-runtime"; import { StaticText1, Stringify, Text } from "shared-runtime"; function Component(props) { - const $ = _c(4); + const $ = _c(2); const { buttons } = props; let t0; if ($[0] !== buttons) { const [, ...nonPrimaryButtons] = buttons; - t0 = nonPrimaryButtons.map(_temp); + const renderedNonPrimaryButtons = nonPrimaryButtons.map(_temp); + + t0 = {renderedNonPrimaryButtons}; $[0] = buttons; $[1] = t0; } else { t0 = $[1]; } - const renderedNonPrimaryButtons = t0; - let t1; - if ($[2] !== renderedNonPrimaryButtons) { - t1 = {renderedNonPrimaryButtons}; - $[2] = renderedNonPrimaryButtons; - $[3] = t1; - } else { - t1 = $[3]; - } - return t1; + return t0; } function _temp(buttonProps, i) { return ( diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/try-catch-within-function-expression-returns-caught-value.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/try-catch-within-function-expression-returns-caught-value.expect.md index db8877f061bc1..1b45e08393bae 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/try-catch-within-function-expression-returns-caught-value.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/try-catch-within-function-expression-returns-caught-value.expect.md @@ -29,10 +29,10 @@ import { c as _c } from "react/compiler-runtime"; import { throwInput } from "shared-runtime"; function Component(props) { - const $ = _c(4); + const $ = _c(2); let t0; if ($[0] !== props) { - t0 = () => { + const callback = () => { try { throwInput([props.value]); } catch (t1) { @@ -40,21 +40,14 @@ function Component(props) { return e; } }; + + t0 = callback(); $[0] = props; $[1] = t0; } else { t0 = $[1]; } - const callback = t0; - let t1; - if ($[2] !== callback) { - t1 = callback(); - $[2] = callback; - $[3] = t1; - } else { - t1 = $[3]; - } - return t1; + return t0; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/type-annotation-var-array.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/type-annotation-var-array.expect.md index ce8e06fcf925f..fc829218f5a15 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/type-annotation-var-array.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/type-annotation-var-array.expect.md @@ -25,25 +25,17 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; // @enableUseTypeAnnotations function Component(props) { - const $ = _c(4); + const $ = _c(2); let t0; if ($[0] !== props.id) { - t0 = makeArray(props.id); + const x = makeArray(props.id); + t0 = x.at(0); $[0] = props.id; $[1] = t0; } else { t0 = $[1]; } - const x = t0; - let t1; - if ($[2] !== x) { - t1 = x.at(0); - $[2] = x; - $[3] = t1; - } else { - t1 = $[3]; - } - const y = t1; + const y = t0; return y; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/type-annotation-var-array_.flow.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/type-annotation-var-array_.flow.expect.md index 03d1f66740bee..5058015fd3239 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/type-annotation-var-array_.flow.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/type-annotation-var-array_.flow.expect.md @@ -29,25 +29,17 @@ import { c as _c } from "react/compiler-runtime"; import { identity } from "shared-runtime"; function Component(props) { - const $ = _c(4); + const $ = _c(2); let t0; if ($[0] !== props.id) { - t0 = makeArray(props.id); + const x = makeArray(props.id); + t0 = x.at(0); $[0] = props.id; $[1] = t0; } else { t0 = $[1]; } - const x = t0; - let t1; - if ($[2] !== x) { - t1 = x.at(0); - $[2] = x; - $[3] = t1; - } else { - t1 = $[3]; - } - const y = t1; + const y = t0; return y; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/use-memo-simple.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/use-memo-simple.expect.md index d721128cb7088..b9e7e1bfe1079 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/use-memo-simple.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/use-memo-simple.expect.md @@ -22,25 +22,17 @@ export const FIXTURE_ENTRYPOINT = { import { c as _c } from "react/compiler-runtime"; function Component(props) { "use memo"; - const $ = _c(4); + const $ = _c(2); let t0; if ($[0] !== props.foo) { - t0 = [props.foo]; + const x = [props.foo]; + t0 =
"foo"
; $[0] = props.foo; $[1] = t0; } else { t0 = $[1]; } - const x = t0; - let t1; - if ($[2] !== x) { - t1 =
"foo"
; - $[2] = x; - $[3] = t1; - } else { - t1 = $[3]; - } - return t1; + return t0; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useContext-read-context-in-callback-if-condition.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useContext-read-context-in-callback-if-condition.expect.md index 1d09cbb3597b4..611606ea38d2c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useContext-read-context-in-callback-if-condition.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useContext-read-context-in-callback-if-condition.expect.md @@ -39,41 +39,34 @@ import { Stringify } from "shared-runtime"; const FooContext = createContext({ current: true }); function Component(props) { - const $ = _c(6); + const $ = _c(4); const foo = useContext(FooContext); let t0; if ($[0] !== foo.current) { - t0 = () => { + const getValue = () => { if (foo.current) { return {}; } else { return null; } }; + + t0 = getValue(); $[0] = foo.current; $[1] = t0; } else { t0 = $[1]; } - const getValue = t0; + const value = t0; let t1; - if ($[2] !== getValue) { - t1 = getValue(); - $[2] = getValue; + if ($[2] !== value) { + t1 = ; + $[2] = value; $[3] = t1; } else { t1 = $[3]; } - const value = t1; - let t2; - if ($[4] !== value) { - t2 = ; - $[4] = value; - $[5] = t2; - } else { - t2 = $[5]; - } - return t2; + return t1; } export const FIXTURE_ENTRYPOINT = {