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 90921454c864f..8a97eea217b33 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts
+++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts
@@ -104,6 +104,7 @@ import {validateNoSetStateInPassiveEffects} from '../Validation/ValidateNoSetSta
import {validateNoJSXInTryStatement} from '../Validation/ValidateNoJSXInTryStatement';
import {propagateScopeDependenciesHIR} from '../HIR/PropagateScopeDependenciesHIR';
import {outlineJSX} from '../Optimization/OutlineJsx';
+import {optimizePropsMethodCalls} from '../Optimization/OptimizePropsMethodCalls';
export type CompilerPipelineValue =
| {kind: 'ast'; name: string; value: CodegenFunction}
@@ -209,6 +210,9 @@ function* runWithEnvironment(
lowerContextAccess(hir, env.config.lowerContextAccess);
}
+ optimizePropsMethodCalls(hir);
+ yield log({kind: 'hir', name: 'OptimizePropsMethodCalls', value: hir});
+
analyseFunctions(hir);
yield log({kind: 'hir', name: 'AnalyseFunctions', value: hir});
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 954fb6f40053a..475d8e8bec574 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts
+++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts
@@ -1644,6 +1644,10 @@ export function isArrayType(id: Identifier): boolean {
return id.type.kind === 'Object' && id.type.shapeId === 'BuiltInArray';
}
+export function isPropsType(id: Identifier): boolean {
+ return id.type.kind === 'Object' && id.type.shapeId === 'BuiltInProps';
+}
+
export function isRefValueType(id: Identifier): boolean {
return id.type.kind === 'Object' && id.type.shapeId === 'BuiltInRefValue';
}
diff --git a/compiler/packages/babel-plugin-react-compiler/src/Optimization/OptimizePropsMethodCalls.ts b/compiler/packages/babel-plugin-react-compiler/src/Optimization/OptimizePropsMethodCalls.ts
new file mode 100644
index 0000000000000..ab686ca219315
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/Optimization/OptimizePropsMethodCalls.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 {HIRFunction, isPropsType} from '../HIR';
+
+/**
+ * Converts method calls into regular calls where the receiver is the props object:
+ *
+ * Example:
+ *
+ * ```
+ * // INPUT
+ * props.foo();
+ *
+ * // OUTPUT
+ * const t0 = props.foo;
+ * t0();
+ * ```
+ *
+ * Counter example:
+ *
+ * Here the receiver is `props.foo`, not the props object, so we don't rewrite it:
+ *
+ * // INPUT
+ * props.foo.bar();
+ *
+ * // OUTPUT
+ * props.foo.bar();
+ * ```
+ */
+export function optimizePropsMethodCalls(fn: HIRFunction): void {
+ for (const [, block] of fn.body.blocks) {
+ for (let i = 0; i < block.instructions.length; i++) {
+ const instr = block.instructions[i]!;
+ if (
+ instr.value.kind === 'MethodCall' &&
+ isPropsType(instr.value.receiver.identifier)
+ ) {
+ instr.value = {
+ kind: 'CallExpression',
+ callee: instr.value.property,
+ args: instr.value.args,
+ loc: instr.value.loc,
+ };
+ }
+ }
+ }
+}
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-outlining-child-stored-in-id.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-outlining-child-stored-in-id.expect.md
index b84229156bc14..962778c5f7601 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-outlining-child-stored-in-id.expect.md
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-outlining-child-stored-in-id.expect.md
@@ -50,11 +50,11 @@ export const FIXTURE_ENTRYPOINT = {
```javascript
import { c as _c } from "react/compiler-runtime"; // @enableJsxOutlining
function Component(arr) {
- const $ = _c(3);
+ const $ = _c(6);
const x = useX();
let t0;
if ($[0] !== arr || $[1] !== x) {
- t0 = arr.map((i) => {
+ t0 = (i) => {
arr.map((i_0, id) => {
const T0 = _temp;
const child =