From d852a1268ca05bd09aa37ecc187fda4e401d4d5e Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Mon, 15 Jul 2024 11:25:59 +0900 Subject: [PATCH 1/3] [compiler] General-purpose function outlining Implements general-purpose function outlining. Specifically, anonymous function expressions which have no dependencies/context variables are extracted into named top-level functions. The original function expression is replaced with a `LoadGlobal` of the generated name. Note that the architecture is designed to allow very general purpose forms of outlining, though we currently are very conservative in what we outline. Specifically, the outlining allows annotating functions with an optional ReactiveFunctionType, which if set will cause the outlined function to get compiled as that type. So we could for example outline a helper hook or helper component, set the type, and then have the hook/component get memoized as well. For now though we just outline with no type set, and generate the function as-is without running it through compilation. [ghstack-poisoned] --- .../src/Entrypoint/Pipeline.ts | 7 +++ .../src/Entrypoint/Program.ts | 32 +++++++++- .../src/HIR/Environment.ts | 37 ++++++++++++ .../src/Optimization/OutlineFunctions.ts | 52 ++++++++++++++++ .../ReactiveScopes/CodegenReactiveFunction.ts | 48 ++++++++++++++- ...n-in-effect-indirect-usecallback.expect.md | 55 ++++++++--------- ...obal-mutation-in-effect-indirect.expect.md | 55 ++++++++--------- ...-reassignment-in-effect-indirect.expect.md | 55 ++++++++--------- ...ow-global-reassignment-in-effect.expect.md | 44 +++++++------- ...ment-to-global-function-jsx-prop.expect.md | 10 ++-- .../fixtures/compiler/array-join.expect.md | 30 ++++------ ...ay-map-captures-receiver-noAlias.expect.md | 5 +- .../array-map-frozen-array-noAlias.expect.md | 5 +- .../compiler/array-map-frozen-array.expect.md | 5 +- ...le-array-mutating-lambda-noAlias.expect.md | 9 +-- ...ap-mutable-array-mutating-lambda.expect.md | 9 +-- ...n-mutating-lambda-mutated-result.expect.md | 5 +- ...ay-map-noAlias-escaping-function.expect.md | 38 ++++++------ ...rrow-function-one-line-directive.expect.md | 18 ++---- ...pturing-function-shadow-captured.expect.md | 18 ++---- ...nt-inner-function-with-many-args.expect.md | 26 ++++---- .../computed-call-evaluation-order.expect.md | 30 +++++----- ...nstant-prop-colliding-identifier.expect.md | 5 +- ...function-expressions-with-params.expect.md | 3 +- ...ault-param-calls-global-function.expect.md | 3 +- ...efault-param-with-empty-callback.expect.md | 13 +--- ...-param-with-reorderable-callback.expect.md | 15 ++--- ...-dont-refresh-const-changes-prod.expect.md | 36 +++++------ ...esh-refresh-on-const-changes-dev.expect.md | 38 +++++------- .../compiler/fbt/lambda-with-fbt.expect.md | 60 +++++++++---------- ...pression-with-store-to-parameter.expect.md | 26 ++++---- ...sting-nested-const-declaration-2.expect.md | 5 +- .../compiler/hoisting-object-method.expect.md | 5 +- ...ing-recursive-call-within-lambda.expect.md | 20 +++---- ...sting-simple-function-expression.expect.md | 5 +- .../compiler/hoisting-within-lambda.expect.md | 13 ++-- ...attribute-with-jsx-element-value.expect.md | 15 ++--- ...ute-with-jsx-fragment-value.flow.expect.md | 16 ++--- ...ction-with-param-as-captured-dep.expect.md | 3 +- .../noAlias-filter-on-array-prop.expect.md | 14 ++--- .../compiler/optional-call-logical.expect.md | 5 +- .../compiler/outlined-helper.expect.md | 22 +++---- .../useCallback-infer-scope-global.expect.md | 15 ++--- ...k-reordering-deplist-controlflow.expect.md | 25 ++++---- .../property-call-evaluation-order.expect.md | 30 +++++----- .../readonly-object-method-calls.expect.md | 5 +- .../ref-like-name-in-effect.expect.md | 3 +- .../ref-like-name-in-useCallback-2.expect.md | 3 +- .../ref-like-name-in-useCallback.expect.md | 3 +- ...ay-with-capturing-map-after-hook.expect.md | 31 +++++----- ...rray-with-mutable-map-after-hook.expect.md | 48 +++++++-------- ...epro-hoisting-variable-collision.expect.md | 22 +++---- .../compiler/repro-hoisting.expect.md | 32 ++++------ ...ay-with-immutable-map-after-hook.expect.md | 54 ++++++++--------- ...ble-range-extending-into-ternary.expect.md | 13 ++-- ...reactive-scope-with-early-return.expect.md | 27 ++++----- ...ession-of-jsxexpressioncontainer.expect.md | 40 ++++++------- .../rules-of-hooks-93dc5d5e538a.expect.md | 14 ++--- .../compiler/simple-function-1.expect.md | 16 ++--- ...o.unnecessary-lambda-memoization.expect.md | 22 +++---- ...tive-freeze-function-expressions.expect.md | 14 ++--- ...catch-within-function-expression.expect.md | 16 ++--- .../type-alias-used-as-annotation.expect.md | 7 ++- ...e-alias-used-as-annotation_.flow.expect.md | 7 ++- ...lias-used-as-variable-annotation.expect.md | 9 +-- ...sed-as-variable-annotation_.flow.expect.md | 9 +-- .../todo_type-annotations-props.expect.md | 14 ++--- .../useEffect-external-mutate.expect.md | 16 ++--- .../useEffect-global-pruned.expect.md | 30 ++++------ .../compiler/useEffect-method-call.expect.md | 16 ++--- .../useEffect-namespace-pruned.expect.md | 30 ++++------ .../value-block-mutates-outer-value.expect.md | 14 ++--- 72 files changed, 750 insertions(+), 750 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineFunctions.ts diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts index 02987fbfc63e5..47286858b5e5d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts @@ -97,6 +97,7 @@ import { validateUseMemo, } from "../Validation"; import { validateLocalsNotReassignedAfterRender } from "../Validation/ValidateLocalsNotReassignedAfterRender"; +import { outlineFunctions } from "../Optimization/OutlineFunctions"; export type CompilerPipelineValue = | { kind: "ast"; name: string; value: CodegenFunction } @@ -242,6 +243,9 @@ function* runWithEnvironment( inferReactiveScopeVariables(hir); yield log({ kind: "hir", name: "InferReactiveScopeVariables", value: hir }); + outlineFunctions(hir); + yield log({ kind: "hir", name: "OutlineFunctions", value: hir }); + alignMethodCallScopes(hir); yield log({ kind: "hir", @@ -480,6 +484,9 @@ function* runWithEnvironment( const ast = codegenFunction(reactiveFunction, uniqueIdentifiers).unwrap(); yield log({ kind: "ast", name: "Codegen", value: ast }); + for (const outlined of ast.outlined) { + yield log({ kind: "ast", name: "Codegen (outlined)", value: outlined.fn }); + } /** * This flag should be only set for unit / fixture tests to check diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts index 4c5155fe57751..d4af6e893ee93 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts @@ -88,6 +88,12 @@ export type BabelFn = | NodePath; export type CompileResult = { + /** + * Distinguishes existing functions that were compiled ('original') from + * functions which were outlined. Only original functions need to be gated + * if gating mode is enabled. + */ + kind: "original" | "outlined"; originalFn: BabelFn; compiledFn: CodegenFunction; }; @@ -267,6 +273,7 @@ export function compileProgram( const lintError = suppressionsToCompilerError(suppressions); let hasCriticalError = lintError != null; const queue: Array<{ + kind: "original" | "outlined"; fn: BabelFn; fnType: ReactFunctionType; }> = []; @@ -286,7 +293,7 @@ export function compileProgram( ALREADY_COMPILED.add(fn.node); fn.skip(); - queue.push({ fn, fnType }); + queue.push({ kind: "original", fn, fnType }); }; // Main traversal to compile with Forget @@ -396,7 +403,26 @@ export function compileProgram( if (compiled === null) { continue; } + for (const outlined of compiled.outlined) { + CompilerError.invariant(outlined.fn.outlined.length === 0, { + reason: "Unexpected nested outlined functions", + loc: outlined.fn.loc, + }); + const fn = current.fn.insertAfter( + createNewFunctionNode(current.fn, outlined.fn) + )[0]!; + fn.skip(); + ALREADY_COMPILED.add(fn.node); + if (outlined.type !== null) { + queue.push({ + kind: "outlined", + fn, + fnType: outlined.type, + }); + } + } compiledFns.push({ + kind: current.kind, compiledFn: compiled, originalFn: current.fn, }); @@ -467,10 +493,10 @@ export function compileProgram( * error elsewhere in the file, regardless of bailout mode. */ for (const result of compiledFns) { - const { originalFn, compiledFn } = result; + const { kind, originalFn, compiledFn } = result; const transformedFn = createNewFunctionNode(originalFn, compiledFn); - if (gating != null) { + if (gating != null && kind == "original") { insertGatedFunctionDeclaration(originalFn, transformedFn, gating); } else { originalFn.replaceWith(transformedFn); diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts index c96d545e705f5..93fc1772edd6b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts @@ -23,6 +23,9 @@ import { BuiltInType, Effect, FunctionType, + GeneratedSource, + HIRFunction, + Identifier, IdentifierId, NonLocalBinding, PolyType, @@ -31,7 +34,10 @@ import { ValueKind, makeBlockId, makeIdentifierId, + makeIdentifierName, + makeInstructionId, makeScopeId, + makeType, } from "./HIR"; import { BuiltInMixedReadonlyId, @@ -42,6 +48,7 @@ import { addHook, } from "./ObjectShape"; import { Scope as BabelScope } from "@babel/traverse"; +import { CodegenFunction } from "../ReactiveScopes"; export const ExternalFunctionSchema = z.object({ // Source for the imported module that exports the `importSpecifierName` functions @@ -506,6 +513,10 @@ export class Environment { #nextBlock: number = 0; #nextScope: number = 0; #scope: BabelScope; + #outlinedFunctions: Array<{ + fn: HIRFunction; + type: ReactFunctionType | null; + }> = []; logger: Logger | null; filename: string | null; code: string | null; @@ -599,6 +610,32 @@ export class Environment { return this.#hoistedIdentifiers.has(node); } + generateUniqueGlobalIdentifier(name: string | null): Identifier { + const id = this.nextIdentifierId; + const identifierNode = this.#scope.generateUidIdentifier(name ?? undefined); + return { + id, + loc: GeneratedSource, + mutableRange: { start: makeInstructionId(0), end: makeInstructionId(0) }, + name: makeIdentifierName(identifierNode.name), + scope: null, + type: { kind: "Poly" }, + }; + } + + outlineFunction(fn: HIRFunction, type: ReactFunctionType | null): void { + this.#outlinedFunctions.push({ fn, type }); + } + + takeOutlinedFunctions(): Array<{ + fn: HIRFunction; + type: ReactFunctionType | null; + }> { + const outlined = this.#outlinedFunctions; + this.#outlinedFunctions = []; + return outlined; + } + getGlobalDeclaration(binding: NonLocalBinding): Global | null { if (this.config.hookPattern != null) { const match = new RegExp(this.config.hookPattern).exec(binding.name); diff --git a/compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineFunctions.ts b/compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineFunctions.ts new file mode 100644 index 0000000000000..a41138317ba30 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineFunctions.ts @@ -0,0 +1,52 @@ +/** + * 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. + */ + +import { CompilerError } from ".."; +import { HIRFunction } from "../HIR"; + +export function outlineFunctions(fn: HIRFunction): void { + for (const [, block] of fn.body.blocks) { + for (const instr of block.instructions) { + const { value } = instr; + + if ( + value.kind === "FunctionExpression" || + value.kind === "ObjectMethod" + ) { + // Recurse in case there are inner functions which can be outlined + outlineFunctions(value.loweredFunc.func); + } + + if ( + value.kind === "FunctionExpression" && + value.loweredFunc.dependencies.length === 0 && + value.loweredFunc.func.context.length === 0 && + // TODO: handle outlining named functions + value.loweredFunc.func.id === null + ) { + const loweredFunc = value.loweredFunc.func; + + const id = fn.env.generateUniqueGlobalIdentifier(loweredFunc.id); + CompilerError.invariant(id.name !== null && id.name.kind === "named", { + reason: "Expected to generate a named variable", + loc: value.loc, + }); + loweredFunc.id = id.name.value; + + fn.env.outlineFunction(loweredFunc, null); + instr.value = { + kind: "LoadGlobal", + binding: { + kind: "Global", + name: id.name.value, + }, + loc: value.loc, + }; + } + } + } +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts index 606d778e69301..56955c0559d09 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -7,7 +7,12 @@ import * as t from "@babel/types"; import { createHmac } from "crypto"; -import { pruneHoistedContexts, pruneUnusedLValues, pruneUnusedLabels } from "."; +import { + pruneHoistedContexts, + pruneUnusedLValues, + pruneUnusedLabels, + renameVariables, +} from "."; import { CompilerError, ErrorSeverity } from "../CompilerError"; import { Environment, EnvironmentConfig, ExternalFunction } from "../HIR"; import { @@ -36,6 +41,7 @@ import { ValidIdentifierName, getHookKind, makeIdentifierName, + promoteTemporary, } from "../HIR/HIR"; import { printIdentifier, printPlace } from "../HIR/PrintHIR"; import { eachPatternOperand } from "../HIR/visitors"; @@ -45,6 +51,8 @@ import { assertExhaustive } from "../Utils/utils"; import { buildReactiveFunction } from "./BuildReactiveFunction"; import { SINGLE_CHILD_FBT_TAGS } from "./MemoizeFbtAndMacroOperandsInSameScope"; import { ReactiveFunctionVisitor, visitReactiveFunction } from "./visitors"; +import { ReactFunctionType } from "../HIR/Environment"; +import { logReactiveFunction } from "../Utils/logger"; export const MEMO_CACHE_SENTINEL = "react.memo_cache_sentinel"; export const EARLY_RETURN_SENTINEL = "react.early_return_sentinel"; @@ -85,6 +93,11 @@ export type CodegenFunction = { * because they were part of a pruned memo block. */ prunedMemoValues: number; + + outlined: Array<{ + fn: CodegenFunction; + type: ReactFunctionType | null; + }>; }; export function codegenFunction( @@ -258,6 +271,38 @@ export function codegenFunction( compiled.body.body.unshift(test); } + const outlined: CodegenFunction["outlined"] = []; + for (const { fn: outlinedFunction, type } of cx.env.takeOutlinedFunctions()) { + const reactiveFunction = buildReactiveFunction(outlinedFunction); + pruneUnusedLabels(reactiveFunction); + pruneUnusedLValues(reactiveFunction); + pruneHoistedContexts(reactiveFunction); + + // TODO: temporary function params (due to destructuring) should alwasy be + // promoted so that they can be renamed + for (const param of reactiveFunction.params) { + const place = param.kind === "Identifier" ? param : param.place; + if (place.identifier.name === null) { + promoteTemporary(place.identifier); + } + } + const identifiers = renameVariables(reactiveFunction); + logReactiveFunction("Outline", reactiveFunction); + const codegen = codegenReactiveFunction( + new Context( + cx.env, + reactiveFunction.id ?? "[[ anonymous ]]", + identifiers + ), + reactiveFunction + ); + if (codegen.isErr()) { + return codegen; + } + outlined.push({ fn: codegen.unwrap(), type }); + } + compiled.outlined = outlined; + return compileResult; } @@ -306,6 +351,7 @@ function codegenReactiveFunction( memoValues: countMemoBlockVisitor.memoValues, prunedMemoBlocks: countMemoBlockVisitor.prunedMemoBlocks, prunedMemoValues: countMemoBlockVisitor.prunedMemoValues, + outlined: [], }); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-global-mutation-in-effect-indirect-usecallback.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-global-mutation-in-effect-indirect-usecallback.expect.md index 7a7535560946f..0cdf31bbfb5cd 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-global-mutation-in-effect-indirect-usecallback.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-global-mutation-in-effect-indirect-usecallback.expect.md @@ -40,57 +40,52 @@ import { useCallback, useEffect, useState } from "react"; let someGlobal = {}; function Component() { - const $ = _c(7); + const $ = _c(6); const [state, setState] = useState(someGlobal); + + const setGlobal = _temp; let t0; + let t1; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t0 = () => { - someGlobal.value = true; - }; - $[0] = t0; - } else { - t0 = $[0]; - } - const setGlobal = t0; - let t1; - let t2; - if ($[1] === Symbol.for("react.memo_cache_sentinel")) { - t1 = () => { setGlobal(); }; - t2 = []; + t1 = []; + $[0] = t0; $[1] = t1; - $[2] = t2; } else { + t0 = $[0]; t1 = $[1]; - t2 = $[2]; } - useEffect(t1, t2); + useEffect(t0, t1); + let t2; let t3; - let t4; - if ($[3] === Symbol.for("react.memo_cache_sentinel")) { - t3 = () => { + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t2 = () => { setState(someGlobal.value); }; - t4 = [someGlobal]; + t3 = [someGlobal]; + $[2] = t2; $[3] = t3; - $[4] = t4; } else { + t2 = $[2]; t3 = $[3]; - t4 = $[4]; } - useEffect(t3, t4); + useEffect(t2, t3); - const t5 = String(state); - let t6; - if ($[5] !== t5) { - t6 =
{t5}
; + const t4 = String(state); + let t5; + if ($[4] !== t4) { + t5 =
{t4}
; + $[4] = t4; $[5] = t5; - $[6] = t6; } else { - t6 = $[6]; + t5 = $[5]; } - return t6; + return t5; +} +function _temp() { + someGlobal.value = true; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-global-mutation-in-effect-indirect.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-global-mutation-in-effect-indirect.expect.md index 43503170c20ff..25a99437061ff 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-global-mutation-in-effect-indirect.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-global-mutation-in-effect-indirect.expect.md @@ -39,57 +39,52 @@ import { useEffect, useState } from "react"; let someGlobal = {}; function Component() { - const $ = _c(7); + const $ = _c(6); const [state, setState] = useState(someGlobal); + + const setGlobal = _temp; let t0; + let t1; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t0 = () => { - someGlobal.value = true; - }; - $[0] = t0; - } else { - t0 = $[0]; - } - const setGlobal = t0; - let t1; - let t2; - if ($[1] === Symbol.for("react.memo_cache_sentinel")) { - t1 = () => { setGlobal(); }; - t2 = []; + t1 = []; + $[0] = t0; $[1] = t1; - $[2] = t2; } else { + t0 = $[0]; t1 = $[1]; - t2 = $[2]; } - useEffect(t1, t2); + useEffect(t0, t1); + let t2; let t3; - let t4; - if ($[3] === Symbol.for("react.memo_cache_sentinel")) { - t3 = () => { + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t2 = () => { setState(someGlobal.value); }; - t4 = [someGlobal]; + t3 = [someGlobal]; + $[2] = t2; $[3] = t3; - $[4] = t4; } else { + t2 = $[2]; t3 = $[3]; - t4 = $[4]; } - useEffect(t3, t4); + useEffect(t2, t3); - const t5 = String(state); - let t6; - if ($[5] !== t5) { - t6 =
{t5}
; + const t4 = String(state); + let t5; + if ($[4] !== t4) { + t5 =
{t4}
; + $[4] = t4; $[5] = t5; - $[6] = t6; } else { - t6 = $[6]; + t5 = $[5]; } - return t6; + return t5; +} +function _temp() { + someGlobal.value = true; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-global-reassignment-in-effect-indirect.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-global-reassignment-in-effect-indirect.expect.md index fec286b265aa9..47db96561eade 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-global-reassignment-in-effect-indirect.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-global-reassignment-in-effect-indirect.expect.md @@ -39,57 +39,52 @@ import { useEffect, useState } from "react"; let someGlobal = false; function Component() { - const $ = _c(7); + const $ = _c(6); const [state, setState] = useState(someGlobal); + + const setGlobal = _temp; let t0; + let t1; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t0 = () => { - someGlobal = true; - }; - $[0] = t0; - } else { - t0 = $[0]; - } - const setGlobal = t0; - let t1; - let t2; - if ($[1] === Symbol.for("react.memo_cache_sentinel")) { - t1 = () => { setGlobal(); }; - t2 = []; + t1 = []; + $[0] = t0; $[1] = t1; - $[2] = t2; } else { + t0 = $[0]; t1 = $[1]; - t2 = $[2]; } - useEffect(t1, t2); + useEffect(t0, t1); + let t2; let t3; - let t4; - if ($[3] === Symbol.for("react.memo_cache_sentinel")) { - t3 = () => { + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t2 = () => { setState(someGlobal); }; - t4 = [someGlobal]; + t3 = [someGlobal]; + $[2] = t2; $[3] = t3; - $[4] = t4; } else { + t2 = $[2]; t3 = $[3]; - t4 = $[4]; } - useEffect(t3, t4); + useEffect(t2, t3); - const t5 = String(state); - let t6; - if ($[5] !== t5) { - t6 =
{t5}
; + const t4 = String(state); + let t5; + if ($[4] !== t4) { + t5 =
{t4}
; + $[4] = t4; $[5] = t5; - $[6] = t6; } else { - t6 = $[6]; + t5 = $[5]; } - return t6; + return t5; +} +function _temp() { + someGlobal = true; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-global-reassignment-in-effect.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-global-reassignment-in-effect.expect.md index e4cd0e22c12f8..1ec48d6eda631 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-global-reassignment-in-effect.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-global-reassignment-in-effect.expect.md @@ -36,48 +36,44 @@ import { useEffect, useState } from "react"; let someGlobal = false; function Component() { - const $ = _c(6); + const $ = _c(5); const [state, setState] = useState(someGlobal); let t0; - let t1; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = () => { - someGlobal = true; - }; - - t1 = []; + t0 = []; $[0] = t0; - $[1] = t1; } else { t0 = $[0]; - t1 = $[1]; } - useEffect(t0, t1); + useEffect(_temp, t0); + let t1; let t2; - let t3; - if ($[2] === Symbol.for("react.memo_cache_sentinel")) { - t2 = () => { + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = () => { setState(someGlobal); }; - t3 = [someGlobal]; + t2 = [someGlobal]; + $[1] = t1; $[2] = t2; - $[3] = t3; } else { + t1 = $[1]; t2 = $[2]; - t3 = $[3]; } - useEffect(t2, t3); + useEffect(t1, t2); - const t4 = String(state); - let t5; - if ($[4] !== t4) { - t5 =
{t4}
; + const t3 = String(state); + let t4; + if ($[3] !== t3) { + t4 =
{t3}
; + $[3] = t3; $[4] = t4; - $[5] = t5; } else { - t5 = $[5]; + t4 = $[4]; } - return t5; + return t4; +} +function _temp() { + someGlobal = true; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-reassignment-to-global-function-jsx-prop.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-reassignment-to-global-function-jsx-prop.expect.md index eaa2834a4971d..60945024f25d2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-reassignment-to-global-function-jsx-prop.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-reassignment-to-global-function-jsx-prop.expect.md @@ -27,13 +27,9 @@ export const FIXTURE_ENTRYPOINT = { import { c as _c } from "react/compiler-runtime"; function Component() { const $ = _c(1); + const onClick = _temp; let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - const onClick = () => { - someUnknownGlobal = true; - moduleLocal = true; - }; - t0 =
; $[0] = t0; } else { @@ -41,6 +37,10 @@ function Component() { } return t0; } +function _temp() { + someUnknownGlobal = true; + moduleLocal = true; +} export const FIXTURE_ENTRYPOINT = { fn: Component, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-join.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-join.expect.md index 2d06f084ffd12..53d7ccea7d38f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-join.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-join.expect.md @@ -16,7 +16,7 @@ function Component(props) { ```javascript import { c as _c } from "react/compiler-runtime"; function Component(props) { - const $ = _c(8); + const $ = _c(7); let t0; let t1; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { @@ -37,25 +37,21 @@ function Component(props) { t2 = $[3]; } const x = t2; - let t3; - if ($[4] === Symbol.for("react.memo_cache_sentinel")) { - t3 = () => "this closure gets stringified, not called"; - $[4] = t3; - } else { - t3 = $[4]; - } - const y = x.join(t3); + const y = x.join(_temp); foo(y); - let t4; - if ($[5] !== x || $[6] !== y) { - t4 = [x, y]; - $[5] = x; - $[6] = y; - $[7] = t4; + let t3; + if ($[4] !== x || $[5] !== y) { + t3 = [x, y]; + $[4] = x; + $[5] = y; + $[6] = t3; } else { - t4 = $[7]; + t3 = $[6]; } - return t4; + return t3; +} +function _temp() { + return "this closure gets stringified, not called"; } ``` 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 e04543798748f..7b99a06518487 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 @@ -44,7 +44,7 @@ function Component(props) { const items = t1; let t2; if ($[4] !== items) { - t2 = items.map((item_0) => item_0); + t2 = items.map(_temp); $[4] = items; $[5] = t2; } else { @@ -53,6 +53,9 @@ function Component(props) { const mapped = t2; return mapped; } +function _temp(item_0) { + return item_0; +} export const FIXTURE_ENTRYPOINT = { fn: Component, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-frozen-array-noAlias.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-frozen-array-noAlias.expect.md index 375556bbeab1c..2826a5c8dbf32 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-frozen-array-noAlias.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-frozen-array-noAlias.expect.md @@ -33,7 +33,7 @@ function Component(props) { const x = t0; let t1; if ($[1] === Symbol.for("react.memo_cache_sentinel")) { - const y = x.map((item) => item); + const y = x.map(_temp); t1 = [x, y]; $[1] = t1; } else { @@ -41,6 +41,9 @@ function Component(props) { } return t1; } +function _temp(item) { + return item; +} export const FIXTURE_ENTRYPOINT = { fn: Component, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-frozen-array.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-frozen-array.expect.md index 375556bbeab1c..2826a5c8dbf32 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-frozen-array.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-frozen-array.expect.md @@ -33,7 +33,7 @@ function Component(props) { const x = t0; let t1; if ($[1] === Symbol.for("react.memo_cache_sentinel")) { - const y = x.map((item) => item); + const y = x.map(_temp); t1 = [x, y]; $[1] = t1; } else { @@ -41,6 +41,9 @@ function Component(props) { } return t1; } +function _temp(item) { + return item; +} export const FIXTURE_ENTRYPOINT = { fn: Component, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-mutable-array-mutating-lambda-noAlias.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-mutable-array-mutating-lambda-noAlias.expect.md index 96f3b935e162b..c302e82eb1d1b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-mutable-array-mutating-lambda-noAlias.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-mutable-array-mutating-lambda-noAlias.expect.md @@ -28,10 +28,7 @@ function Component(props) { let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { const x = []; - const y = x.map((item) => { - item.updated = true; - return item; - }); + const y = x.map(_temp); t0 = [x, y]; $[0] = t0; } else { @@ -39,6 +36,10 @@ function Component(props) { } return t0; } +function _temp(item) { + item.updated = true; + return item; +} export const FIXTURE_ENTRYPOINT = { fn: Component, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-mutable-array-mutating-lambda.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-mutable-array-mutating-lambda.expect.md index 96f3b935e162b..c302e82eb1d1b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-mutable-array-mutating-lambda.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-mutable-array-mutating-lambda.expect.md @@ -28,10 +28,7 @@ function Component(props) { let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { const x = []; - const y = x.map((item) => { - item.updated = true; - return item; - }); + const y = x.map(_temp); t0 = [x, y]; $[0] = t0; } else { @@ -39,6 +36,10 @@ function Component(props) { } return t0; } +function _temp(item) { + item.updated = true; + return item; +} export const FIXTURE_ENTRYPOINT = { fn: Component, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-mutable-array-non-mutating-lambda-mutated-result.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-mutable-array-non-mutating-lambda-mutated-result.expect.md index ee209592dc0c0..bc83ac17700b2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-mutable-array-non-mutating-lambda-mutated-result.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-map-mutable-array-non-mutating-lambda-mutated-result.expect.md @@ -28,7 +28,7 @@ function Component(props) { let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { const x = [{}]; - const y = x.map((item) => item); + const y = x.map(_temp); y[0].flag = true; t0 = [x, y]; $[0] = t0; @@ -37,6 +37,9 @@ function Component(props) { } return t0; } +function _temp(item) { + return item; +} export const FIXTURE_ENTRYPOINT = { fn: Component, 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 b5a581ce14d83..4960b17e4b7bf 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,33 +21,29 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function Component(props) { - const $ = _c(5); + const $ = _c(4); + const f = _temp; let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = (item) => item; - $[0] = t0; + if ($[0] !== props.items) { + t0 = [...props.items].map(f); + $[0] = props.items; + $[1] = t0; } else { - t0 = $[0]; + t0 = $[1]; } - const f = t0; + const x = t0; let t1; - if ($[1] !== props.items) { - t1 = [...props.items].map(f); - $[1] = props.items; - $[2] = t1; + if ($[2] !== x) { + t1 = [x, f]; + $[2] = x; + $[3] = t1; } else { - t1 = $[2]; + t1 = $[3]; } - const x = t1; - let t2; - if ($[3] !== x) { - t2 = [x, f]; - $[3] = x; - $[4] = t2; - } else { - t2 = $[4]; - } - return t2; + return t1; +} +function _temp(item) { + return item; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-one-line-directive.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-one-line-directive.expect.md index e752d771e4f71..f6fb5afa03593 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-one-line-directive.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-one-line-directive.expect.md @@ -21,22 +21,14 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; function useFoo() { - const $ = _c(1); - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = () => { - "worklet"; - return 1; - }; - $[0] = t0; - } else { - t0 = $[0]; - } - const update = t0; + const update = _temp; return update; } +function _temp() { + "worklet"; + return 1; +} export const FIXTURE_ENTRYPOINT = { fn: useFoo, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-shadow-captured.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-shadow-captured.expect.md index cfb776900b40e..68113a0d731f9 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-shadow-captured.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-shadow-captured.expect.md @@ -16,22 +16,14 @@ function component(a) { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; function component(a) { - const $ = _c(1); - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = function () { - let z_0; - mutate(z_0); - }; - $[0] = t0; - } else { - t0 = $[0]; - } - const x = t0; + const x = _temp; return x; } +function _temp() { + let z_0; + mutate(z_0); +} ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/component-inner-function-with-many-args.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/component-inner-function-with-many-args.expect.md index bfc1d31ffdba0..8b3c406c5cafb 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/component-inner-function-with-many-args.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/component-inner-function-with-many-args.expect.md @@ -22,24 +22,20 @@ export const FIXTURE_ENTRYPOINT = { import { c as _c } from "react/compiler-runtime"; import { Stringify } from "shared-runtime"; function Component(props) { - const $ = _c(3); + const $ = _c(2); + const cb = _temp; let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = (x, y, z) => x + y + z; - $[0] = t0; + if ($[0] !== props.id) { + t0 = ; + $[0] = props.id; + $[1] = t0; } else { - t0 = $[0]; + t0 = $[1]; } - const cb = t0; - let t1; - if ($[1] !== props.id) { - t1 = ; - $[1] = props.id; - $[2] = t1; - } else { - t1 = $[2]; - } - return t1; + return t0; +} +function _temp(x, y, z) { + return x + y + z; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/computed-call-evaluation-order.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/computed-call-evaluation-order.expect.md index 76bdcf1efe4ad..a0c23f26eb877 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/computed-call-evaluation-order.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/computed-call-evaluation-order.expect.md @@ -30,30 +30,30 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; // Should print A, B, arg, original function Component() { - const $ = _c(2); - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = (o) => { - o.f = () => console.log("new"); - }; - $[0] = t0; - } else { - t0 = $[0]; - } - const changeF = t0; + const $ = _c(1); + const changeF = _temp2; let x; - if ($[1] === Symbol.for("react.memo_cache_sentinel")) { - x = { f: () => console.log("original") }; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + x = { f: _temp3 }; (console.log("A"), x)[(console.log("B"), "f")]( (changeF(x), console.log("arg"), 1), ); - $[1] = x; + $[0] = x; } else { - x = $[1]; + x = $[0]; } return x; } +function _temp3() { + return console.log("original"); +} +function _temp2(o) { + o.f = _temp; +} +function _temp() { + return console.log("new"); +} export const FIXTURE_ENTRYPOINT = { fn: Component, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/constant-prop-colliding-identifier.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/constant-prop-colliding-identifier.expect.md index 7f42633ce5a6b..b1ac15391c88f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/constant-prop-colliding-identifier.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/constant-prop-colliding-identifier.expect.md @@ -27,11 +27,14 @@ export const FIXTURE_ENTRYPOINT = { import { invoke } from "shared-runtime"; function Component() { - const fn = () => ({ x: "value" }); + const fn = _temp; invoke(fn); return 3; } +function _temp() { + return { x: "value" }; +} export const FIXTURE_ENTRYPOINT = { fn: Component, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/deeply-nested-function-expressions-with-params.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/deeply-nested-function-expressions-with-params.expect.md index a93d5b49d66fc..880c158b721d0 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/deeply-nested-function-expressions-with-params.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/deeply-nested-function-expressions-with-params.expect.md @@ -31,7 +31,7 @@ function Foo() { let t1; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t1 = function a(t2) { - const x_0 = t2 === undefined ? () => {} : t2; + const x_0 = t2 === undefined ? _temp : t2; return (function b(t3) { const y_0 = t3 === undefined ? [] : t3; return [x_0, y_0]; @@ -44,6 +44,7 @@ function Foo() { t0 = t1; return t0; } +function _temp() {} export const FIXTURE_ENTRYPOINT = { fn: Foo, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/default-param-calls-global-function.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/default-param-calls-global-function.expect.md index 1bbfb1b7ec94d..fd6716f115f1f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/default-param-calls-global-function.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/default-param-calls-global-function.expect.md @@ -25,7 +25,7 @@ function Component(t0) { const $ = _c(2); let t1; if ($[0] !== t0) { - t1 = t0 === undefined ? identity([() => {}, true, 42, "hello"]) : t0; + t1 = t0 === undefined ? identity([_temp, true, 42, "hello"]) : t0; $[0] = t0; $[1] = t1; } else { @@ -34,6 +34,7 @@ function Component(t0) { const x = t1; return x; } +function _temp() {} export const FIXTURE_ENTRYPOINT = { fn: Component, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/default-param-with-empty-callback.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/default-param-with-empty-callback.expect.md index cc96a95553e40..43249cfc1e878 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/default-param-with-empty-callback.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/default-param-with-empty-callback.expect.md @@ -16,20 +16,11 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; function Component(t0) { - const $ = _c(2); - let t1; - if ($[0] !== t0) { - t1 = t0 === undefined ? () => {} : t0; - $[0] = t0; - $[1] = t1; - } else { - t1 = $[1]; - } - const x = t1; + const x = t0 === undefined ? _temp : t0; return x; } +function _temp() {} export const FIXTURE_ENTRYPOINT = { fn: Component, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/default-param-with-reorderable-callback.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/default-param-with-reorderable-callback.expect.md index 624ed6f67a049..73268e85d8aef 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/default-param-with-reorderable-callback.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/default-param-with-reorderable-callback.expect.md @@ -16,20 +16,13 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; function Component(t0) { - const $ = _c(2); - let t1; - if ($[0] !== t0) { - t1 = t0 === undefined ? () => [-1, true, 42, "hello"] : t0; - $[0] = t0; - $[1] = t1; - } else { - t1 = $[1]; - } - const x = t1; + const x = t0 === undefined ? _temp : t0; return x; } +function _temp() { + return [-1, true, 42, "hello"]; +} export const FIXTURE_ENTRYPOINT = { fn: Component, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fast-refresh-dont-refresh-const-changes-prod.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fast-refresh-dont-refresh-const-changes-prod.expect.md index 87084a14028c9..3dad23aba1b7a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fast-refresh-dont-refresh-const-changes-prod.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fast-refresh-dont-refresh-const-changes-prod.expect.md @@ -58,37 +58,31 @@ function unsafeUpdateConst() { } function Component() { - const $ = _c(3); + const $ = _c(2); + useState(_temp); + + unsafeUpdateConst(); let t0; + let t1; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = () => { - unsafeResetConst(); - }; - $[0] = t0; + t1 = [{ pretendConst }]; + $[0] = t1; } else { - t0 = $[0]; + t1 = $[0]; } - useState(t0); - - unsafeUpdateConst(); - let t1; + t0 = t1; + const value = t0; let t2; if ($[1] === Symbol.for("react.memo_cache_sentinel")) { - t2 = [{ pretendConst }]; + t2 = ; $[1] = t2; } else { t2 = $[1]; } - t1 = t2; - const value = t1; - let t3; - if ($[2] === Symbol.for("react.memo_cache_sentinel")) { - t3 = ; - $[2] = t3; - } else { - t3 = $[2]; - } - return t3; + return t2; +} +function _temp() { + unsafeResetConst(); } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fast-refresh-refresh-on-const-changes-dev.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fast-refresh-refresh-on-const-changes-dev.expect.md index 19317de47e2bd..556fdaab384df 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fast-refresh-refresh-on-const-changes-dev.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fast-refresh-refresh-on-const-changes-dev.expect.md @@ -61,45 +61,39 @@ function unsafeUpdateConst() { } function Component() { - const $ = _c(4); + const $ = _c(3); if ( $[0] !== "4bf230b116dd95f382060ad17350e116395e41ed757e51fd074ea0b4ed281272" ) { - for (let $i = 0; $i < 4; $i += 1) { + for (let $i = 0; $i < 3; $i += 1) { $[$i] = Symbol.for("react.memo_cache_sentinel"); } $[0] = "4bf230b116dd95f382060ad17350e116395e41ed757e51fd074ea0b4ed281272"; } + useState(_temp); + + unsafeUpdateConst(); let t0; + let t1; if ($[1] === Symbol.for("react.memo_cache_sentinel")) { - t0 = () => { - unsafeResetConst(); - }; - $[1] = t0; + t1 = [{ pretendConst }]; + $[1] = t1; } else { - t0 = $[1]; + t1 = $[1]; } - useState(t0); - - unsafeUpdateConst(); - let t1; + t0 = t1; + const value = t0; let t2; if ($[2] === Symbol.for("react.memo_cache_sentinel")) { - t2 = [{ pretendConst }]; + t2 = ; $[2] = t2; } else { t2 = $[2]; } - t1 = t2; - const value = t1; - let t3; - if ($[3] === Symbol.for("react.memo_cache_sentinel")) { - t3 = ; - $[3] = t3; - } else { - t3 = $[3]; - } - return t3; + return t2; +} +function _temp() { + unsafeResetConst(); } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/lambda-with-fbt.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/lambda-with-fbt.expect.md index 5ac0ad17a0645..473849be4ea9e 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/lambda-with-fbt.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/lambda-with-fbt.expect.md @@ -42,47 +42,41 @@ import { c as _c } from "react/compiler-runtime"; import { fbt } from "fbt"; function Component() { - const $ = _c(2); + const $ = _c(1); + const buttonLabel = _temp; let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = () => { - if (!someCondition) { - return fbt._("Purchase as a gift", null, { hk: "1gHj4g" }); - } else { - if ( - !iconOnly && - showPrice && - item?.current_gift_offer?.price?.formatted != null - ) { - return fbt._( - "Gift | {price}", - [fbt._param("price", item?.current_gift_offer?.price?.formatted)], - { hk: "3GTnGE" }, - ); - } else { - if (!iconOnly && !showPrice) { - return fbt._("Gift", null, { hk: "3fqfrk" }); - } - } - } - }; - $[0] = t0; - } else { - t0 = $[0]; - } - const buttonLabel = t0; - let t1; - if ($[1] === Symbol.for("react.memo_cache_sentinel")) { - t1 = ( + t0 = ( ; - $[2] = deeplinkItemId; - $[3] = t2; + if ($[1] !== deeplinkItemId) { + t1 = ; + $[1] = deeplinkItemId; + $[2] = t1; } else { - t2 = $[3]; + t1 = $[2]; } - return t2; + return t1; } +function _temp() {} export const FIXTURE_ENTRYPOINT = { fn: Component, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-memoize-array-with-immutable-map-after-hook.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-memoize-array-with-immutable-map-after-hook.expect.md index 33f1330d7e9ea..2b96328236d50 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-memoize-array-with-immutable-map-after-hook.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-memoize-array-with-immutable-map-after-hook.expect.md @@ -34,7 +34,7 @@ import { c as _c } from "react/compiler-runtime"; import { useEffect, useState } from "react"; function Component(props) { - const $ = _c(11); + const $ = _c(10); let t0; if ($[0] !== props.value) { t0 = [props.value]; @@ -45,47 +45,47 @@ function Component(props) { } const x = t0; let t1; - let t2; if ($[2] === Symbol.for("react.memo_cache_sentinel")) { - t1 = () => {}; - t2 = []; + t1 = []; $[2] = t1; - $[3] = t2; } else { t1 = $[2]; - t2 = $[3]; } - useEffect(t1, t2); - let t3; - if ($[4] !== x.length) { - t3 = () => { + useEffect(_temp, t1); + let t2; + if ($[3] !== x.length) { + t2 = () => { console.log(x.length); }; - $[4] = x.length; - $[5] = t3; + $[3] = x.length; + $[4] = t2; } else { - t3 = $[5]; + t2 = $[4]; } - const onClick = t3; - let t4; - if ($[6] !== x) { - t4 = x.map((item) => {item}); - $[6] = x; - $[7] = t4; + const onClick = t2; + let t3; + if ($[5] !== x) { + t3 = x.map(_temp2); + $[5] = x; + $[6] = t3; } else { - t4 = $[7]; + t3 = $[6]; } - let t5; - if ($[8] !== onClick || $[9] !== t4) { - t5 =
{t4}
; - $[8] = onClick; + let t4; + if ($[7] !== onClick || $[8] !== t3) { + t4 =
{t3}
; + $[7] = onClick; + $[8] = t3; $[9] = t4; - $[10] = t5; } else { - t5 = $[10]; + t4 = $[9]; } - return t5; + return t4; +} +function _temp2(item) { + return {item}; } +function _temp() {} export const FIXTURE_ENTRYPOINT = { fn: Component, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-mutable-range-extending-into-ternary.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-mutable-range-extending-into-ternary.expect.md index 4b9822fdb8a05..72ba9ab675ea8 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-mutable-range-extending-into-ternary.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-mutable-range-extending-into-ternary.expect.md @@ -57,15 +57,10 @@ import { useState } from "react"; function Component(props) { const items = props.items ? props.items.slice() : []; const [state] = useState(""); - return props.cond ? ( -
{state}
- ) : ( -
- {items.map((item) => ( -
{item.name}
- ))} -
- ); + return props.cond ?
{state}
:
{items.map(_temp)}
; +} +function _temp(item) { + return
{item.name}
; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-no-declarations-in-reactive-scope-with-early-return.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-no-declarations-in-reactive-scope-with-early-return.expect.md index ffd18500f1d2c..0c1bf1cd701b3 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-no-declarations-in-reactive-scope-with-early-return.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-no-declarations-in-reactive-scope-with-early-return.expect.md @@ -39,7 +39,7 @@ function Component() { ```javascript import { c as _c } from "react/compiler-runtime"; // @enableAssumeHooksFollowRulesOfReact @enableTransitivelyFreezeFunctionExpressions function Component() { - const $ = _c(9); + const $ = _c(8); const items = useItems(); let t0; let t1; @@ -74,17 +74,8 @@ function Component() { t2 = t4; break bb0; } - let t4; - if ($[6] === Symbol.for("react.memo_cache_sentinel")) { - t4 = (t5) => { - const [item_0] = t5; - return ; - }; - $[6] = t4; - } else { - t4 = $[6]; - } - t1 = filteredItems.map(t4); + + t1 = filteredItems.map(_temp); } $[0] = items; $[1] = t1; @@ -99,15 +90,19 @@ function Component() { return t2; } let t3; - if ($[7] !== t1) { + if ($[6] !== t1) { t3 = <>{t1}; - $[7] = t1; - $[8] = t3; + $[6] = t1; + $[7] = t3; } else { - t3 = $[8]; + t3 = $[7]; } return t3; } +function _temp(t0) { + const [item_0] = t0; + return ; +} ``` 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 aa5a55dfecc2f..f215141be5dd5 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,7 +48,7 @@ import { c as _c } from "react/compiler-runtime"; import { StaticText1, Stringify, Text } from "shared-runtime"; function Component(props) { - const $ = _c(7); + const $ = _c(6); const { buttons } = props; let nonPrimaryButtons; if ($[0] !== buttons) { @@ -61,24 +61,7 @@ function Component(props) { } let t0; if ($[2] !== nonPrimaryButtons) { - let t1; - if ($[4] === Symbol.for("react.memo_cache_sentinel")) { - t1 = (buttonProps, i) => ( - - ); - $[4] = t1; - } else { - t1 = $[4]; - } - t0 = nonPrimaryButtons.map(t1); + t0 = nonPrimaryButtons.map(_temp); $[2] = nonPrimaryButtons; $[3] = t0; } else { @@ -86,15 +69,26 @@ function Component(props) { } const renderedNonPrimaryButtons = t0; let t1; - if ($[5] !== renderedNonPrimaryButtons) { + if ($[4] !== renderedNonPrimaryButtons) { t1 = {renderedNonPrimaryButtons}; - $[5] = renderedNonPrimaryButtons; - $[6] = t1; + $[4] = renderedNonPrimaryButtons; + $[5] = t1; } else { - t1 = $[6]; + t1 = $[5]; } return t1; } +function _temp(buttonProps, i) { + return ( + + ); +} const styles = { leftSecondaryButton: { left: true }, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-93dc5d5e538a.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-93dc5d5e538a.expect.md index 753c8156db382..626091be1e2a5 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-93dc5d5e538a.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-93dc5d5e538a.expect.md @@ -17,22 +17,16 @@ function RegressionTest() { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // Valid because the loop doesn't change the order of hooks calls. +// Valid because the loop doesn't change the order of hooks calls. function RegressionTest() { - const $ = _c(1); const res = []; for (let i = 0; i !== 10 && true; ++i) { res.push(i); } - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = () => {}; - $[0] = t0; - } else { - t0 = $[0]; - } - React.useLayoutEffect(t0); + + React.useLayoutEffect(_temp); } +function _temp() {} ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/simple-function-1.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/simple-function-1.expect.md index 508d0f66ba23d..586b610399e67 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/simple-function-1.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/simple-function-1.expect.md @@ -20,21 +20,13 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; function component() { - const $ = _c(1); - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = function (a) { - a.foo(); - }; - $[0] = t0; - } else { - t0 = $[0]; - } - const x = t0; + const x = _temp; return x; } +function _temp(a) { + a.foo(); +} export const FIXTURE_ENTRYPOINT = { fn: component, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo.unnecessary-lambda-memoization.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo.unnecessary-lambda-memoization.expect.md index e5ece01a5ed33..a5de1b45fa237 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo.unnecessary-lambda-memoization.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo.unnecessary-lambda-memoization.expect.md @@ -24,18 +24,11 @@ function Component(props) { ```javascript import { c as _c } from "react/compiler-runtime"; function Component(props) { - const $ = _c(5); + const $ = _c(4); const data = useFreeze(); let t0; if ($[0] !== data.items) { - let t1; - if ($[2] === Symbol.for("react.memo_cache_sentinel")) { - t1 = (item) => ; - $[2] = t1; - } else { - t1 = $[2]; - } - t0 = data.items.map(t1); + t0 = data.items.map(_temp); $[0] = data.items; $[1] = t0; } else { @@ -43,15 +36,18 @@ function Component(props) { } const items = t0; let t1; - if ($[3] !== items) { + if ($[2] !== items) { t1 =
{items}
; - $[3] = items; - $[4] = t1; + $[2] = items; + $[3] = t1; } else { - t1 = $[4]; + t1 = $[3]; } return t1; } +function _temp(item) { + return ; +} ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transitive-freeze-function-expressions.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transitive-freeze-function-expressions.expect.md index ff1a2e16e722e..ca472cc6b677f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transitive-freeze-function-expressions.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transitive-freeze-function-expressions.expect.md @@ -33,7 +33,7 @@ function Component(props) { ```javascript import { c as _c } from "react/compiler-runtime"; // @enableTransitivelyFreezeFunctionExpressions function Component(props) { - const $ = _c(10); + const $ = _c(9); const { data, loadNext, isLoadingNext } = usePaginationFragment(props.key).items ?? []; let t0; @@ -74,14 +74,7 @@ function Component(props) { useEffect(t1, t2); let t3; if ($[7] !== data) { - let t4; - if ($[9] === Symbol.for("react.memo_cache_sentinel")) { - t4 = (x) => x; - $[9] = t4; - } else { - t4 = $[9]; - } - t3 = data.map(t4); + t3 = data.map(_temp); $[7] = data; $[8] = t3; } else { @@ -90,6 +83,9 @@ function Component(props) { const items = t3; return items; } +function _temp(x) { + return x; +} ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/try-catch-within-function-expression.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/try-catch-within-function-expression.expect.md index 4e9dbd5fed5f8..5f55ec6142965 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/try-catch-within-function-expression.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/try-catch-within-function-expression.expect.md @@ -26,16 +26,9 @@ export const FIXTURE_ENTRYPOINT = { import { c as _c } from "react/compiler-runtime"; function Component(props) { const $ = _c(1); + const callback = _temp; let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - const callback = () => { - try { - return []; - } catch (t1) { - return; - } - }; - t0 = callback(); $[0] = t0; } else { @@ -43,6 +36,13 @@ function Component(props) { } return t0; } +function _temp() { + try { + return []; + } catch (t0) { + return; + } +} export const FIXTURE_ENTRYPOINT = { fn: Component, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-alias-used-as-annotation.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-alias-used-as-annotation.expect.md index 02a31ec938063..c5bafae2b7574 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-alias-used-as-annotation.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-alias-used-as-annotation.expect.md @@ -25,12 +25,13 @@ export const FIXTURE_ENTRYPOINT = { // @enableAssumeHooksFollowRulesOfReact @enableTransitivelyFreezeFunctionExpressions type Bar = string; function TypeAliasUsedAsParamAnnotation() { - const fun = (f) => { - console.log(f); - }; + const fun = _temp; fun("hello, world"); } +function _temp(f) { + console.log(f); +} export const FIXTURE_ENTRYPOINT = { fn: TypeAliasUsedAsParamAnnotation, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-alias-used-as-annotation_.flow.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-alias-used-as-annotation_.flow.expect.md index 92c522c4503be..52a2217beadb3 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-alias-used-as-annotation_.flow.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-alias-used-as-annotation_.flow.expect.md @@ -23,12 +23,13 @@ export const FIXTURE_ENTRYPOINT = { ```javascript type Bar = string; function TypeAliasUsedAsAnnotation() { - const fun = (f) => { - console.log(f); - }; + const fun = _temp; fun("hello, world"); } +function _temp(f) { + console.log(f); +} export const FIXTURE_ENTRYPOINT = { fn: TypeAliasUsedAsAnnotation, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-alias-used-as-variable-annotation.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-alias-used-as-variable-annotation.expect.md index f97bcbb3a6808..a8b58d4030eea 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-alias-used-as-variable-annotation.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-alias-used-as-variable-annotation.expect.md @@ -26,13 +26,14 @@ export const FIXTURE_ENTRYPOINT = { // @enableAssumeHooksFollowRulesOfReact @enableTransitivelyFreezeFunctionExpressions type Bar = string; function TypeAliasUsedAsVariableAnnotation() { - const fun = (f) => { - const g = f; - console.log(g); - }; + const fun = _temp; fun("hello, world"); } +function _temp(f) { + const g = f; + console.log(g); +} export const FIXTURE_ENTRYPOINT = { fn: TypeAliasUsedAsVariableAnnotation, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-alias-used-as-variable-annotation_.flow.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-alias-used-as-variable-annotation_.flow.expect.md index 9362ad8655b9e..3b5af06a5d43a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-alias-used-as-variable-annotation_.flow.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-alias-used-as-variable-annotation_.flow.expect.md @@ -25,13 +25,14 @@ export const FIXTURE_ENTRYPOINT = { ```javascript type Bar = string; function TypeAliasUsedAsAnnotation() { - const fun = (f) => { - const g = f; - console.log(g); - }; + const fun = _temp; fun("hello, world"); } +function _temp(f) { + const g = f; + console.log(g); +} export const FIXTURE_ENTRYPOINT = { fn: TypeAliasUsedAsAnnotation, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/todo_type-annotations-props.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/todo_type-annotations-props.expect.md index 709f75ddb294b..ee502bc34231c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/todo_type-annotations-props.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/todo_type-annotations-props.expect.md @@ -22,17 +22,10 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; // @enableUseTypeAnnotations function useArray(items) { - const $ = _c(3); + const $ = _c(2); let t0; if ($[0] !== items) { - let t1; - if ($[2] === Symbol.for("react.memo_cache_sentinel")) { - t1 = (x) => x !== 0; - $[2] = t1; - } else { - t1 = $[2]; - } - t0 = items.filter(t1); + t0 = items.filter(_temp); $[0] = items; $[1] = t0; } else { @@ -40,6 +33,9 @@ function useArray(items) { } return t0; } +function _temp(x) { + return x !== 0; +} export const FIXTURE_ENTRYPOINT = { fn: useArray, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useEffect-external-mutate.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useEffect-external-mutate.expect.md index 4481197c91cad..6deb76d188e84 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useEffect-external-mutate.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useEffect-external-mutate.expect.md @@ -22,23 +22,15 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; import { useEffect } from "react"; let x = { a: 42 }; function Component(props) { - const $ = _c(1); - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = () => { - x.a = 10; - }; - $[0] = t0; - } else { - t0 = $[0]; - } - useEffect(t0); + useEffect(_temp); +} +function _temp() { + x.a = 10; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useEffect-global-pruned.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useEffect-global-pruned.expect.md index c598ff8367319..c81175fde94e2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useEffect-global-pruned.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useEffect-global-pruned.expect.md @@ -36,35 +36,29 @@ import { useEffect } from "react"; function someGlobal() {} function useFoo() { - const $ = _c(3); + const $ = _c(2); let t0; - let t1; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t1 = function () { - someGlobal(); - }; - $[0] = t1; - } else { - t1 = $[0]; - } - t0 = t1; + t0 = _temp; const fn = t0; + let t1; let t2; - let t3; - if ($[1] === Symbol.for("react.memo_cache_sentinel")) { - t2 = () => { + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = () => { fn(); }; - t3 = [fn]; + t2 = [fn]; + $[0] = t1; $[1] = t2; - $[2] = t3; } else { + t1 = $[0]; t2 = $[1]; - t3 = $[2]; } - useEffect(t2, t3); + useEffect(t1, t2); return null; } +function _temp() { + someGlobal(); +} export const FIXTURE_ENTRYPOINT = { fn: useFoo, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useEffect-method-call.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useEffect-method-call.expect.md index c275453aff604..638ea42aa5e16 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useEffect-method-call.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useEffect-method-call.expect.md @@ -19,20 +19,12 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; let x = {}; function Component() { - const $ = _c(1); - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = () => { - x.foo = 1; - }; - $[0] = t0; - } else { - t0 = $[0]; - } - React.useEffect(t0); + React.useEffect(_temp); +} +function _temp() { + x.foo = 1; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useEffect-namespace-pruned.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useEffect-namespace-pruned.expect.md index 0c6f4d582f607..8af9ad082ec1c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useEffect-namespace-pruned.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useEffect-namespace-pruned.expect.md @@ -36,35 +36,29 @@ import * as React from "react"; function someGlobal() {} function useFoo() { - const $ = _c(3); + const $ = _c(2); let t0; - let t1; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t1 = function () { - someGlobal(); - }; - $[0] = t1; - } else { - t1 = $[0]; - } - t0 = t1; + t0 = _temp; const fn = t0; + let t1; let t2; - let t3; - if ($[1] === Symbol.for("react.memo_cache_sentinel")) { - t2 = () => { + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = () => { fn(); }; - t3 = [fn]; + t2 = [fn]; + $[0] = t1; $[1] = t2; - $[2] = t3; } else { + t1 = $[0]; t2 = $[1]; - t3 = $[2]; } - React.useEffect(t2, t3); + React.useEffect(t1, t2); return null; } +function _temp() { + someGlobal(); +} export const FIXTURE_ENTRYPOINT = { fn: useFoo, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/value-block-mutates-outer-value.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/value-block-mutates-outer-value.expect.md index 29d19decd5ec0..c57a3079517b1 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/value-block-mutates-outer-value.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/value-block-mutates-outer-value.expect.md @@ -34,7 +34,6 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; import { makeArray, useHook } from "shared-runtime"; /** @@ -46,16 +45,8 @@ import { makeArray, useHook } from "shared-runtime"; * merged with the scope producing customList */ function Foo(t0) { - const $ = _c(1); const { defaultList, cond } = t0; - let t1; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t1 = (a, b) => a - b; - $[0] = t1; - } else { - t1 = $[0]; - } - const comparator = t1; + const comparator = _temp; useHook(); const customList = makeArray(1, 5, 2); useHook(); @@ -64,6 +55,9 @@ function Foo(t0) { : defaultList; return result; } +function _temp(a, b) { + return a - b; +} export const FIXTURE_ENTRYPOINT = { fn: Foo, From 284841811717164c6cdd2cec5c23e3c2983fb2a2 Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Mon, 15 Jul 2024 11:38:06 +0900 Subject: [PATCH 2/3] Update on "[compiler] General-purpose function outlining" Implements general-purpose function outlining. Specifically, anonymous function expressions which have no dependencies/context variables are extracted into named top-level functions. The original function expression is replaced with a `LoadGlobal` of the generated name. Note that the architecture is designed to allow very general purpose forms of outlining, though we currently are very conservative in what we outline. Specifically, the outlining allows annotating functions with an optional ReactiveFunctionType, which if set will cause the outlined function to get compiled as that type. So we could for example outline a helper hook or helper component, set the type, and then have the hook/component get memoized as well. For now though we just outline with no type set, and generate the function as-is without running it through compilation. [ghstack-poisoned] --- .../src/Entrypoint/Program.ts | 2 +- .../src/HIR/Environment.ts | 16 ++++++---------- .../src/Optimization/OutlineFunctions.ts | 10 +++------- .../ReactiveScopes/CodegenReactiveFunction.ts | 2 +- 4 files changed, 11 insertions(+), 19 deletions(-) diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts index d4af6e893ee93..a17e7fbe272d2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts @@ -496,7 +496,7 @@ export function compileProgram( const { kind, originalFn, compiledFn } = result; const transformedFn = createNewFunctionNode(originalFn, compiledFn); - if (gating != null && kind == "original") { + if (gating != null && kind === "original") { insertGatedFunctionDeclaration(originalFn, transformedFn, gating); } else { originalFn.replaceWith(transformedFn); diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts index 93fc1772edd6b..4666a12962037 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts @@ -31,6 +31,8 @@ import { PolyType, ScopeId, Type, + ValidIdentifierName, + ValidatedIdentifier, ValueKind, makeBlockId, makeIdentifierId, @@ -610,17 +612,11 @@ export class Environment { return this.#hoistedIdentifiers.has(node); } - generateUniqueGlobalIdentifier(name: string | null): Identifier { - const id = this.nextIdentifierId; + generateGloballyUniqueIdentifierName( + name: string | null + ): ValidatedIdentifier { const identifierNode = this.#scope.generateUidIdentifier(name ?? undefined); - return { - id, - loc: GeneratedSource, - mutableRange: { start: makeInstructionId(0), end: makeInstructionId(0) }, - name: makeIdentifierName(identifierNode.name), - scope: null, - type: { kind: "Poly" }, - }; + return makeIdentifierName(identifierNode.name); } outlineFunction(fn: HIRFunction, type: ReactFunctionType | null): void { diff --git a/compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineFunctions.ts b/compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineFunctions.ts index a41138317ba30..eac40e1a54541 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineFunctions.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineFunctions.ts @@ -30,19 +30,15 @@ export function outlineFunctions(fn: HIRFunction): void { ) { const loweredFunc = value.loweredFunc.func; - const id = fn.env.generateUniqueGlobalIdentifier(loweredFunc.id); - CompilerError.invariant(id.name !== null && id.name.kind === "named", { - reason: "Expected to generate a named variable", - loc: value.loc, - }); - loweredFunc.id = id.name.value; + const id = fn.env.generateGloballyUniqueIdentifierName(loweredFunc.id); + loweredFunc.id = id.value; fn.env.outlineFunction(loweredFunc, null); instr.value = { kind: "LoadGlobal", binding: { kind: "Global", - name: id.name.value, + name: id.value, }, loc: value.loc, }; diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts index 56955c0559d09..a83a06e4f9f89 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -278,7 +278,7 @@ export function codegenFunction( pruneUnusedLValues(reactiveFunction); pruneHoistedContexts(reactiveFunction); - // TODO: temporary function params (due to destructuring) should alwasy be + // TODO: temporary function params (due to destructuring) should always be // promoted so that they can be renamed for (const param of reactiveFunction.params) { const place = param.kind === "Identifier" ? param : param.place; From cdffa692cdd72583e392ae3ae8f2aa47cd0e9402 Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Mon, 15 Jul 2024 11:46:21 +0900 Subject: [PATCH 3/3] Update on "[compiler] General-purpose function outlining" Implements general-purpose function outlining. Specifically, anonymous function expressions which have no dependencies/context variables are extracted into named top-level functions. The original function expression is replaced with a `LoadGlobal` of the generated name. Note that the architecture is designed to allow very general purpose forms of outlining, though we currently are very conservative in what we outline. Specifically, the outlining allows annotating functions with an optional ReactiveFunctionType, which if set will cause the outlined function to get compiled as that type. So we could for example outline a helper hook or helper component, set the type, and then have the hook/component get memoized as well. For now though we just outline with no type set, and generate the function as-is without running it through compilation. [ghstack-poisoned] --- .../src/Entrypoint/Program.ts | 13 +++++++++---- .../src/HIR/Environment.ts | 6 ++---- .../src/ReactiveScopes/CodegenReactiveFunction.ts | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts index a17e7fbe272d2..1a52443b5e807 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts @@ -414,11 +414,16 @@ export function compileProgram( fn.skip(); ALREADY_COMPILED.add(fn.node); if (outlined.type !== null) { - queue.push({ - kind: "outlined", - fn, - fnType: outlined.type, + CompilerError.throwTodo({ + reason: `Implement support for outlining React functions (components/hooks)`, + loc: outlined.fn.loc, }); + // Above should be as simple as the following, but needs testing: + // queue.push({ + // kind: "outlined", + // fn, + // fnType: outlined.type, + // }); } } compiledFns.push({ diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts index 4666a12962037..647c78e5dd94b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts @@ -623,13 +623,11 @@ export class Environment { this.#outlinedFunctions.push({ fn, type }); } - takeOutlinedFunctions(): Array<{ + getOutlinedFunctions(): Array<{ fn: HIRFunction; type: ReactFunctionType | null; }> { - const outlined = this.#outlinedFunctions; - this.#outlinedFunctions = []; - return outlined; + return this.#outlinedFunctions; } getGlobalDeclaration(binding: NonLocalBinding): Global | null { diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts index a83a06e4f9f89..38a45d1a53426 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -272,7 +272,7 @@ export function codegenFunction( } const outlined: CodegenFunction["outlined"] = []; - for (const { fn: outlinedFunction, type } of cx.env.takeOutlinedFunctions()) { + for (const { fn: outlinedFunction, type } of cx.env.getOutlinedFunctions()) { const reactiveFunction = buildReactiveFunction(outlinedFunction); pruneUnusedLabels(reactiveFunction); pruneUnusedLValues(reactiveFunction);