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/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/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/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/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/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/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; +} 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/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/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/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-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!'}, + ], + }, + ], +}; 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 = { 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('
'); + }); });