) <-- argument only invoked if array is non-empty
+ * ```
+ */
+function useFoo({arr1, arr2}) {
+ const x = arr1.map(e => arr1[0].value + e.value);
+ const y = arr1.map(e => arr2[0].value + e.value);
+ return [x, y];
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: useFoo,
+ params: [{arr1: [], arr2: []}],
+ sequentialRenders: [
+ {arr1: [], arr2: []},
+ {arr1: [], arr2: null},
+ {arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
+ ],
+};
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
new file mode 100644
index 0000000000..e0dc1eeb5f
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call-chain.expect.md
@@ -0,0 +1,112 @@
+
+## Input
+
+```javascript
+import {useRef} from 'react';
+import {Stringify} from 'shared-runtime';
+
+function Component({a, b}) {
+ const logA = () => {
+ console.log(a.value);
+ };
+ const logB = () => {
+ console.log(b.value);
+ };
+ const hasLogged = useRef(false);
+ const log = () => {
+ if (!hasLogged.current) {
+ logA();
+ logB();
+ hasLogged.current = true;
+ }
+ };
+ return ;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{a: {value: 1}, b: {value: 2}}],
+ sequentialRenders: [
+ {a: {value: 1}, b: {value: 2}},
+ {a: {value: 3}, b: {value: 4}},
+ ],
+};
+
+```
+
+## Code
+
+```javascript
+import { c as _c } from "react/compiler-runtime";
+import { useRef } from "react";
+import { Stringify } from "shared-runtime";
+
+function Component(t0) {
+ const $ = _c(9);
+ const { a, b } = t0;
+ let t1;
+ if ($[0] !== a.value) {
+ t1 = () => {
+ console.log(a.value);
+ };
+ $[0] = a.value;
+ $[1] = t1;
+ } else {
+ t1 = $[1];
+ }
+ const logA = t1;
+ let t2;
+ if ($[2] !== b.value) {
+ t2 = () => {
+ console.log(b.value);
+ };
+ $[2] = b.value;
+ $[3] = t2;
+ } else {
+ t2 = $[3];
+ }
+ const logB = t2;
+
+ const hasLogged = useRef(false);
+ let t3;
+ if ($[4] !== logA || $[5] !== logB) {
+ t3 = () => {
+ if (!hasLogged.current) {
+ logA();
+ logB();
+ hasLogged.current = true;
+ }
+ };
+ $[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;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{ a: { value: 1 }, b: { value: 2 } }],
+ sequentialRenders: [
+ { a: { value: 1 }, b: { value: 2 } },
+ { a: { value: 3 }, b: { value: 4 } },
+ ],
+};
+
+```
+
+### Eval output
+(kind: ok) {"log":{"kind":"Function"},"shouldInvokeFns":true}
+{"log":{"kind":"Function"},"shouldInvokeFns":true}
+logs: [1,2]
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call-chain.tsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call-chain.tsx
new file mode 100644
index 0000000000..746287fe60
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call-chain.tsx
@@ -0,0 +1,29 @@
+import {useRef} from 'react';
+import {Stringify} from 'shared-runtime';
+
+function Component({a, b}) {
+ const logA = () => {
+ console.log(a.value);
+ };
+ const logB = () => {
+ console.log(b.value);
+ };
+ const hasLogged = useRef(false);
+ const log = () => {
+ if (!hasLogged.current) {
+ logA();
+ logB();
+ hasLogged.current = true;
+ }
+ };
+ return ;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{a: {value: 1}, b: {value: 2}}],
+ sequentialRenders: [
+ {a: {value: 1}, b: {value: 2}},
+ {a: {value: 3}, b: {value: 4}},
+ ],
+};
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call.expect.md
new file mode 100644
index 0000000000..0080fd0468
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call.expect.md
@@ -0,0 +1,85 @@
+
+## Input
+
+```javascript
+import {useState} from 'react';
+import {useIdentity} from 'shared-runtime';
+
+/**
+ * Assume that conditionally called functions can be invoked and that their
+ * property loads are hoistable to the function declaration site.
+ */
+function useMakeCallback({obj}: {obj: {value: number}}) {
+ const [state, setState] = useState(0);
+ const cb = () => {
+ if (obj.value !== 0) setState(obj.value);
+ };
+ useIdentity(null);
+ if (state === 0) {
+ cb();
+ }
+ return {cb};
+}
+export const FIXTURE_ENTRYPOINT = {
+ fn: useMakeCallback,
+ params: [{obj: {value: 1}}],
+ sequentialRenders: [{obj: {value: 1}}, {obj: {value: 2}}],
+};
+
+```
+
+## Code
+
+```javascript
+import { c as _c } from "react/compiler-runtime";
+import { useState } from "react";
+import { useIdentity } from "shared-runtime";
+
+/**
+ * Assume that conditionally called functions can be invoked and that their
+ * property loads are hoistable to the function declaration site.
+ */
+function useMakeCallback(t0) {
+ const $ = _c(4);
+ const { obj } = t0;
+ const [state, setState] = useState(0);
+ let t1;
+ if ($[0] !== obj.value) {
+ t1 = () => {
+ if (obj.value !== 0) {
+ setState(obj.value);
+ }
+ };
+ $[0] = obj.value;
+ $[1] = t1;
+ } else {
+ t1 = $[1];
+ }
+ const cb = t1;
+
+ useIdentity(null);
+ if (state === 0) {
+ cb();
+ }
+ let t2;
+ if ($[2] !== cb) {
+ t2 = { cb };
+ $[2] = cb;
+ $[3] = t2;
+ } else {
+ t2 = $[3];
+ }
+ return t2;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: useMakeCallback,
+ params: [{ obj: { value: 1 } }],
+ sequentialRenders: [{ obj: { value: 1 } }, { obj: { value: 2 } }],
+};
+
+```
+
+### Eval output
+(kind: ok) {"cb":"[[ function params=0 ]]"}
+{"cb":"[[ function params=0 ]]"}
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call.ts
new file mode 100644
index 0000000000..12d92b726f
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditional-call.ts
@@ -0,0 +1,23 @@
+import {useState} from 'react';
+import {useIdentity} from 'shared-runtime';
+
+/**
+ * Assume that conditionally called functions can be invoked and that their
+ * property loads are hoistable to the function declaration site.
+ */
+function useMakeCallback({obj}: {obj: {value: number}}) {
+ const [state, setState] = useState(0);
+ const cb = () => {
+ if (obj.value !== 0) setState(obj.value);
+ };
+ useIdentity(null);
+ if (state === 0) {
+ cb();
+ }
+ return {cb};
+}
+export const FIXTURE_ENTRYPOINT = {
+ fn: useMakeCallback,
+ params: [{obj: {value: 1}}],
+ sequentialRenders: [{obj: {value: 1}}, {obj: {value: 2}}],
+};
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditionally-return-fn.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditionally-return-fn.expect.md
new file mode 100644
index 0000000000..77b62bc8c2
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditionally-return-fn.expect.md
@@ -0,0 +1,87 @@
+
+## Input
+
+```javascript
+import {createHookWrapper} from 'shared-runtime';
+
+/**
+ * Assume that conditionally returned functions can be invoked and that their
+ * property loads are hoistable to the function declaration site.
+ */
+function useMakeCallback({
+ obj,
+ shouldMakeCb,
+ setState,
+}: {
+ obj: {value: number};
+ shouldMakeCb: boolean;
+ setState: (newState: number) => void;
+}) {
+ const cb = () => setState(obj.value);
+ if (shouldMakeCb) return cb;
+ else return null;
+}
+
+const setState = (arg: number) => {
+ 'use no memo';
+ return arg;
+};
+export const FIXTURE_ENTRYPOINT = {
+ fn: createHookWrapper(useMakeCallback),
+ params: [{obj: {value: 1}, shouldMakeCb: true, setState}],
+ sequentialRenders: [
+ {obj: {value: 1}, shouldMakeCb: true, setState},
+ {obj: {value: 2}, shouldMakeCb: true, setState},
+ ],
+};
+
+```
+
+## Code
+
+```javascript
+import { c as _c } from "react/compiler-runtime";
+import { createHookWrapper } from "shared-runtime";
+
+/**
+ * Assume that conditionally returned functions can be invoked and that their
+ * property loads are hoistable to the function declaration site.
+ */
+function useMakeCallback(t0) {
+ const $ = _c(3);
+ const { obj, shouldMakeCb, setState } = t0;
+ let t1;
+ if ($[0] !== obj.value || $[1] !== setState) {
+ t1 = () => setState(obj.value);
+ $[0] = obj.value;
+ $[1] = setState;
+ $[2] = t1;
+ } else {
+ t1 = $[2];
+ }
+ const cb = t1;
+ if (shouldMakeCb) {
+ return cb;
+ } else {
+ return null;
+ }
+}
+
+const setState = (arg: number) => {
+ "use no memo";
+ return arg;
+};
+export const FIXTURE_ENTRYPOINT = {
+ fn: createHookWrapper(useMakeCallback),
+ params: [{ obj: { value: 1 }, shouldMakeCb: true, setState }],
+ sequentialRenders: [
+ { obj: { value: 1 }, shouldMakeCb: true, setState },
+ { obj: { value: 2 }, shouldMakeCb: true, setState },
+ ],
+};
+
+```
+
+### Eval output
+(kind: ok) {"result":{"kind":"Function","result":1},"shouldInvokeFns":true}
+{"result":{"kind":"Function","result":2},"shouldInvokeFns":true}
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditionally-return-fn.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditionally-return-fn.ts
new file mode 100644
index 0000000000..08dde03b03
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/conditionally-return-fn.ts
@@ -0,0 +1,32 @@
+import {createHookWrapper} from 'shared-runtime';
+
+/**
+ * Assume that conditionally returned functions can be invoked and that their
+ * property loads are hoistable to the function declaration site.
+ */
+function useMakeCallback({
+ obj,
+ shouldMakeCb,
+ setState,
+}: {
+ obj: {value: number};
+ shouldMakeCb: boolean;
+ setState: (newState: number) => void;
+}) {
+ const cb = () => setState(obj.value);
+ if (shouldMakeCb) return cb;
+ else return null;
+}
+
+const setState = (arg: number) => {
+ 'use no memo';
+ return arg;
+};
+export const FIXTURE_ENTRYPOINT = {
+ fn: createHookWrapper(useMakeCallback),
+ params: [{obj: {value: 1}, shouldMakeCb: true, setState}],
+ sequentialRenders: [
+ {obj: {value: 1}, shouldMakeCb: true, setState},
+ {obj: {value: 2}, shouldMakeCb: true, setState},
+ ],
+};
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/direct-call.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/direct-call.expect.md
new file mode 100644
index 0000000000..2f31be1ffe
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/direct-call.expect.md
@@ -0,0 +1,74 @@
+
+## Input
+
+```javascript
+import {useState} from 'react';
+import {useIdentity} from 'shared-runtime';
+
+function useMakeCallback({obj}: {obj: {value: number}}) {
+ const [state, setState] = useState(0);
+ const cb = () => {
+ if (obj.value !== state) setState(obj.value);
+ };
+ useIdentity();
+ cb();
+ return [cb];
+}
+export const FIXTURE_ENTRYPOINT = {
+ fn: useMakeCallback,
+ params: [{obj: {value: 1}}],
+ sequentialRenders: [{obj: {value: 1}}, {obj: {value: 2}}],
+};
+
+```
+
+## Code
+
+```javascript
+import { c as _c } from "react/compiler-runtime";
+import { useState } from "react";
+import { useIdentity } from "shared-runtime";
+
+function useMakeCallback(t0) {
+ const $ = _c(5);
+ const { obj } = t0;
+ const [state, setState] = useState(0);
+ let t1;
+ if ($[0] !== obj.value || $[1] !== state) {
+ t1 = () => {
+ if (obj.value !== state) {
+ setState(obj.value);
+ }
+ };
+ $[0] = obj.value;
+ $[1] = state;
+ $[2] = t1;
+ } else {
+ t1 = $[2];
+ }
+ const cb = t1;
+
+ useIdentity();
+ cb();
+ let t2;
+ if ($[3] !== cb) {
+ t2 = [cb];
+ $[3] = cb;
+ $[4] = t2;
+ } else {
+ t2 = $[4];
+ }
+ return t2;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: useMakeCallback,
+ params: [{ obj: { value: 1 } }],
+ sequentialRenders: [{ obj: { value: 1 } }, { obj: { value: 2 } }],
+};
+
+```
+
+### Eval output
+(kind: ok) ["[[ function params=0 ]]"]
+["[[ function params=0 ]]"]
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/direct-call.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/direct-call.ts
new file mode 100644
index 0000000000..c2e8292297
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/direct-call.ts
@@ -0,0 +1,17 @@
+import {useState} from 'react';
+import {useIdentity} from 'shared-runtime';
+
+function useMakeCallback({obj}: {obj: {value: number}}) {
+ const [state, setState] = useState(0);
+ const cb = () => {
+ if (obj.value !== state) setState(obj.value);
+ };
+ useIdentity();
+ cb();
+ return [cb];
+}
+export const FIXTURE_ENTRYPOINT = {
+ fn: useMakeCallback,
+ params: [{obj: {value: 1}}],
+ sequentialRenders: [{obj: {value: 1}}, {obj: {value: 2}}],
+};
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/function-with-conditional-callsite-in-another-function.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/function-with-conditional-callsite-in-another-function.expect.md
new file mode 100644
index 0000000000..8301912b02
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/function-with-conditional-callsite-in-another-function.expect.md
@@ -0,0 +1,130 @@
+
+## Input
+
+```javascript
+import {createHookWrapper} from 'shared-runtime';
+
+/**
+ * (Given that the returned lambda is assumed to be invoked, see
+ * return-function)
+ *
+ * If lambda A conditionally calls lambda B, optimistically assume that property
+ * loads from lambda B has the same hoistability of ones from lambda A. This
+ * helps optimize components / hooks that create and chain many helper
+ * functions.
+ *
+ * Type systems and code readability encourage developers to colocate length and
+ * null checks values in the same function as where values are used. i.e.
+ * developers are unlikely to write the following code.
+ * ```js
+ * function useFoo(obj, objNotNullAndHasElements) {
+ * // ...
+ * const get0th = () => obj.arr[0].value;
+ * return () => objNotNullAndHasElements ? get0th : undefined;
+ * }
+ * ```
+ *
+ * In Meta code, this assumption helps reduce the number of memo dependency
+ * deopts.
+ */
+function useMakeCallback({
+ obj,
+ cond,
+ setState,
+}: {
+ obj: {value: number};
+ cond: boolean;
+ setState: (newState: number) => void;
+}) {
+ const cb = () => setState(obj.value);
+ // cb's property loads are assumed to be hoistable to the start of this lambda
+ return () => (cond ? cb() : undefined);
+}
+
+const setState = (arg: number) => {
+ 'use no memo';
+ return arg;
+};
+export const FIXTURE_ENTRYPOINT = {
+ fn: createHookWrapper(useMakeCallback),
+ params: [{obj: {value: 1}, cond: true, setState}],
+ sequentialRenders: [
+ {obj: {value: 1}, cond: true, setState},
+ {obj: {value: 2}, cond: true, setState},
+ ],
+};
+
+```
+
+## Code
+
+```javascript
+import { c as _c } from "react/compiler-runtime";
+import { createHookWrapper } from "shared-runtime";
+
+/**
+ * (Given that the returned lambda is assumed to be invoked, see
+ * return-function)
+ *
+ * If lambda A conditionally calls lambda B, optimistically assume that property
+ * loads from lambda B has the same hoistability of ones from lambda A. This
+ * helps optimize components / hooks that create and chain many helper
+ * functions.
+ *
+ * Type systems and code readability encourage developers to colocate length and
+ * null checks values in the same function as where values are used. i.e.
+ * developers are unlikely to write the following code.
+ * ```js
+ * function useFoo(obj, objNotNullAndHasElements) {
+ * // ...
+ * const get0th = () => obj.arr[0].value;
+ * return () => objNotNullAndHasElements ? get0th : undefined;
+ * }
+ * ```
+ *
+ * In Meta code, this assumption helps reduce the number of memo dependency
+ * deopts.
+ */
+function useMakeCallback(t0) {
+ const $ = _c(6);
+ const { obj, cond, setState } = t0;
+ let t1;
+ if ($[0] !== obj.value || $[1] !== setState) {
+ t1 = () => setState(obj.value);
+ $[0] = obj.value;
+ $[1] = setState;
+ $[2] = t1;
+ } else {
+ t1 = $[2];
+ }
+ const cb = t1;
+ let t2;
+ if ($[3] !== cb || $[4] !== cond) {
+ t2 = () => (cond ? cb() : undefined);
+ $[3] = cb;
+ $[4] = cond;
+ $[5] = t2;
+ } else {
+ t2 = $[5];
+ }
+ return t2;
+}
+
+const setState = (arg: number) => {
+ "use no memo";
+ return arg;
+};
+export const FIXTURE_ENTRYPOINT = {
+ fn: createHookWrapper(useMakeCallback),
+ params: [{ obj: { value: 1 }, cond: true, setState }],
+ sequentialRenders: [
+ { obj: { value: 1 }, cond: true, setState },
+ { obj: { value: 2 }, cond: true, setState },
+ ],
+};
+
+```
+
+### Eval output
+(kind: ok) {"result":{"kind":"Function","result":1},"shouldInvokeFns":true}
+{"result":{"kind":"Function","result":2},"shouldInvokeFns":true}
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/function-with-conditional-callsite-in-another-function.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/function-with-conditional-callsite-in-another-function.ts
new file mode 100644
index 0000000000..b6283aa6a6
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/function-with-conditional-callsite-in-another-function.ts
@@ -0,0 +1,51 @@
+import {createHookWrapper} from 'shared-runtime';
+
+/**
+ * (Given that the returned lambda is assumed to be invoked, see
+ * return-function)
+ *
+ * If lambda A conditionally calls lambda B, optimistically assume that property
+ * loads from lambda B has the same hoistability of ones from lambda A. This
+ * helps optimize components / hooks that create and chain many helper
+ * functions.
+ *
+ * Type systems and code readability encourage developers to colocate length and
+ * null checks values in the same function as where values are used. i.e.
+ * developers are unlikely to write the following code.
+ * ```js
+ * function useFoo(obj, objNotNullAndHasElements) {
+ * // ...
+ * const get0th = () => obj.arr[0].value;
+ * return () => objNotNullAndHasElements ? get0th : undefined;
+ * }
+ * ```
+ *
+ * In Meta code, this assumption helps reduce the number of memo dependency
+ * deopts.
+ */
+function useMakeCallback({
+ obj,
+ cond,
+ setState,
+}: {
+ obj: {value: number};
+ cond: boolean;
+ setState: (newState: number) => void;
+}) {
+ const cb = () => setState(obj.value);
+ // cb's property loads are assumed to be hoistable to the start of this lambda
+ return () => (cond ? cb() : undefined);
+}
+
+const setState = (arg: number) => {
+ 'use no memo';
+ return arg;
+};
+export const FIXTURE_ENTRYPOINT = {
+ fn: createHookWrapper(useMakeCallback),
+ params: [{obj: {value: 1}, cond: true, setState}],
+ sequentialRenders: [
+ {obj: {value: 1}, cond: true, setState},
+ {obj: {value: 2}, cond: true, setState},
+ ],
+};
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/hook-call.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/hook-call.expect.md
new file mode 100644
index 0000000000..ab8326a228
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/hook-call.expect.md
@@ -0,0 +1,80 @@
+
+## Input
+
+```javascript
+import {createHookWrapper, useIdentity} from 'shared-runtime';
+
+/**
+ * Assume that functions passed hook arguments are invoked and that their
+ * property loads are hoistable.
+ */
+function useMakeCallback({
+ obj,
+ setState,
+}: {
+ obj: {value: number};
+ setState: (newState: number) => void;
+}) {
+ const cb = useIdentity(() => setState(obj.value));
+ return cb;
+}
+
+const setState = (arg: number) => {
+ 'use no memo';
+ return arg;
+};
+export const FIXTURE_ENTRYPOINT = {
+ fn: createHookWrapper(useMakeCallback),
+ params: [{obj: {value: 1}, setState}],
+ sequentialRenders: [
+ {obj: {value: 1}, setState},
+ {obj: {value: 2}, setState},
+ ],
+};
+
+```
+
+## Code
+
+```javascript
+import { c as _c } from "react/compiler-runtime";
+import { createHookWrapper, useIdentity } from "shared-runtime";
+
+/**
+ * Assume that functions passed hook arguments are invoked and that their
+ * property loads are hoistable.
+ */
+function useMakeCallback(t0) {
+ const $ = _c(3);
+ const { obj, setState } = t0;
+ let t1;
+ if ($[0] !== obj.value || $[1] !== setState) {
+ t1 = () => setState(obj.value);
+ $[0] = obj.value;
+ $[1] = setState;
+ $[2] = t1;
+ } else {
+ t1 = $[2];
+ }
+ const cb = useIdentity(t1);
+ return cb;
+}
+
+const setState = (arg: number) => {
+ "use no memo";
+ return arg;
+};
+export const FIXTURE_ENTRYPOINT = {
+ fn: createHookWrapper(useMakeCallback),
+ params: [{ obj: { value: 1 }, setState }],
+ sequentialRenders: [
+ { obj: { value: 1 }, setState },
+ { obj: { value: 2 }, setState },
+ ],
+};
+
+```
+
+### Eval output
+(kind: ok) {"result":{"kind":"Function","result":1},"shouldInvokeFns":true}
+{"result":{"kind":"Function","result":2},"shouldInvokeFns":true}
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/hook-call.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/hook-call.ts
new file mode 100644
index 0000000000..a1ab6e18c5
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/hook-call.ts
@@ -0,0 +1,29 @@
+import {createHookWrapper, useIdentity} from 'shared-runtime';
+
+/**
+ * Assume that functions passed hook arguments are invoked and that their
+ * property loads are hoistable.
+ */
+function useMakeCallback({
+ obj,
+ setState,
+}: {
+ obj: {value: number};
+ setState: (newState: number) => void;
+}) {
+ const cb = useIdentity(() => setState(obj.value));
+ return cb;
+}
+
+const setState = (arg: number) => {
+ 'use no memo';
+ return arg;
+};
+export const FIXTURE_ENTRYPOINT = {
+ fn: createHookWrapper(useMakeCallback),
+ params: [{obj: {value: 1}, setState}],
+ sequentialRenders: [
+ {obj: {value: 1}, setState},
+ {obj: {value: 2}, setState},
+ ],
+};
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-and-passed.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-and-passed.expect.md
new file mode 100644
index 0000000000..688901a4e2
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-and-passed.expect.md
@@ -0,0 +1,80 @@
+
+## Input
+
+```javascript
+import {createHookWrapper} from 'shared-runtime';
+
+function useFoo({arr1}) {
+ const cb1 = e => arr1[0].value + e.value;
+ const x = arr1.map(cb1);
+ return [x, cb1];
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: createHookWrapper(useFoo),
+ params: [{arr1: [], arr2: []}],
+ sequentialRenders: [
+ {arr1: [], arr2: []},
+ {arr1: [], arr2: null},
+ {arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
+ ],
+};
+
+```
+
+## Code
+
+```javascript
+import { c as _c } from "react/compiler-runtime";
+import { createHookWrapper } from "shared-runtime";
+
+function useFoo(t0) {
+ const $ = _c(8);
+ const { arr1 } = t0;
+ let t1;
+ if ($[0] !== arr1[0]) {
+ t1 = (e) => arr1[0].value + e.value;
+ $[0] = arr1[0];
+ $[1] = t1;
+ } else {
+ t1 = $[1];
+ }
+ const cb1 = t1;
+ let t2;
+ if ($[2] !== arr1 || $[3] !== cb1) {
+ t2 = arr1.map(cb1);
+ $[2] = arr1;
+ $[3] = cb1;
+ $[4] = t2;
+ } else {
+ t2 = $[4];
+ }
+ const x = t2;
+ let t3;
+ if ($[5] !== cb1 || $[6] !== x) {
+ t3 = [x, cb1];
+ $[5] = cb1;
+ $[6] = x;
+ $[7] = t3;
+ } else {
+ t3 = $[7];
+ }
+ return t3;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: createHookWrapper(useFoo),
+ params: [{ arr1: [], arr2: [] }],
+ sequentialRenders: [
+ { arr1: [], arr2: [] },
+ { arr1: [], arr2: null },
+ { arr1: [{ value: 1 }, { value: 2 }], arr2: [{ value: -1 }] },
+ ],
+};
+
+```
+
+### Eval output
+(kind: ok) {"result":[[],"[[ function params=1 ]]"],"shouldInvokeFns":true}
+{"result":[[],"[[ function params=1 ]]"],"shouldInvokeFns":true}
+{"result":[[2,3],"[[ function params=1 ]]"],"shouldInvokeFns":true}
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-and-passed.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-and-passed.ts
new file mode 100644
index 0000000000..c08701022a
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-and-passed.ts
@@ -0,0 +1,17 @@
+import {createHookWrapper} from 'shared-runtime';
+
+function useFoo({arr1}) {
+ const cb1 = e => arr1[0].value + e.value;
+ const x = arr1.map(cb1);
+ return [x, cb1];
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: createHookWrapper(useFoo),
+ params: [{arr1: [], arr2: []}],
+ sequentialRenders: [
+ {arr1: [], arr2: []},
+ {arr1: [], arr2: null},
+ {arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
+ ],
+};
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-function.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-function.expect.md
new file mode 100644
index 0000000000..76228fc249
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-function.expect.md
@@ -0,0 +1,75 @@
+
+## Input
+
+```javascript
+// @flow
+import {Stringify} from 'shared-runtime';
+
+/**
+ * Assume that functions captured directly as jsx attributes are invoked and
+ * that their property loads are hoistable.
+ */
+function useMakeCallback({
+ obj,
+ setState,
+}: {
+ obj: {value: number};
+ setState: (newState: number) => void;
+}) {
+ return setState(obj.value)} shouldInvokeFns={true} />;
+}
+
+const setState = (arg: number) => {
+ 'use no memo';
+ return arg;
+};
+export const FIXTURE_ENTRYPOINT = {
+ fn: useMakeCallback,
+ params: [{obj: {value: 1}, setState}],
+ sequentialRenders: [
+ {obj: {value: 1}, setState},
+ {obj: {value: 2}, setState},
+ ],
+};
+
+```
+
+## Code
+
+```javascript
+import { c as _c } from "react/compiler-runtime";
+import { Stringify } from "shared-runtime";
+
+function useMakeCallback(t0) {
+ const $ = _c(3);
+ const { obj, setState } = t0;
+ let t1;
+ if ($[0] !== obj.value || $[1] !== setState) {
+ t1 = setState(obj.value)} shouldInvokeFns={true} />;
+ $[0] = obj.value;
+ $[1] = setState;
+ $[2] = t1;
+ } else {
+ t1 = $[2];
+ }
+ return t1;
+}
+
+const setState = (arg: number) => {
+ "use no memo";
+ return arg;
+};
+export const FIXTURE_ENTRYPOINT = {
+ fn: useMakeCallback,
+ params: [{ obj: { value: 1 }, setState }],
+ sequentialRenders: [
+ { obj: { value: 1 }, setState },
+ { obj: { value: 2 }, setState },
+ ],
+};
+
+```
+
+### Eval output
+(kind: ok) {"cb":{"kind":"Function","result":1},"shouldInvokeFns":true}
+{"cb":{"kind":"Function","result":2},"shouldInvokeFns":true}
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-function.tsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-function.tsx
new file mode 100644
index 0000000000..316a0a03fb
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/jsx-function.tsx
@@ -0,0 +1,29 @@
+// @flow
+import {Stringify} from 'shared-runtime';
+
+/**
+ * Assume that functions captured directly as jsx attributes are invoked and
+ * that their property loads are hoistable.
+ */
+function useMakeCallback({
+ obj,
+ setState,
+}: {
+ obj: {value: number};
+ setState: (newState: number) => void;
+}) {
+ return setState(obj.value)} shouldInvokeFns={true} />;
+}
+
+const setState = (arg: number) => {
+ 'use no memo';
+ return arg;
+};
+export const FIXTURE_ENTRYPOINT = {
+ fn: useMakeCallback,
+ params: [{obj: {value: 1}, setState}],
+ sequentialRenders: [
+ {obj: {value: 1}, setState},
+ {obj: {value: 2}, setState},
+ ],
+};
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/return-function.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/return-function.expect.md
new file mode 100644
index 0000000000..31e317d07e
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/return-function.expect.md
@@ -0,0 +1,78 @@
+
+## Input
+
+```javascript
+import {createHookWrapper} from 'shared-runtime';
+
+/**
+ * Assume that directly returned functions are invoked and that their property
+ * loads are hoistable.
+ */
+function useMakeCallback({
+ obj,
+ setState,
+}: {
+ obj: {value: number};
+ setState: (newState: number) => void;
+}) {
+ return () => setState(obj.value);
+}
+
+const setState = (arg: number) => {
+ 'use no memo';
+ return arg;
+};
+export const FIXTURE_ENTRYPOINT = {
+ fn: createHookWrapper(useMakeCallback),
+ params: [{obj: {value: 1}, setState}],
+ sequentialRenders: [
+ {obj: {value: 1}, setState},
+ {obj: {value: 2}, setState},
+ ],
+};
+
+```
+
+## Code
+
+```javascript
+import { c as _c } from "react/compiler-runtime";
+import { createHookWrapper } from "shared-runtime";
+
+/**
+ * Assume that directly returned functions are invoked and that their property
+ * loads are hoistable.
+ */
+function useMakeCallback(t0) {
+ const $ = _c(3);
+ const { obj, setState } = t0;
+ let t1;
+ if ($[0] !== obj.value || $[1] !== setState) {
+ t1 = () => setState(obj.value);
+ $[0] = obj.value;
+ $[1] = setState;
+ $[2] = t1;
+ } else {
+ t1 = $[2];
+ }
+ return t1;
+}
+
+const setState = (arg: number) => {
+ "use no memo";
+ return arg;
+};
+export const FIXTURE_ENTRYPOINT = {
+ fn: createHookWrapper(useMakeCallback),
+ params: [{ obj: { value: 1 }, setState }],
+ sequentialRenders: [
+ { obj: { value: 1 }, setState },
+ { obj: { value: 2 }, setState },
+ ],
+};
+
+```
+
+### Eval output
+(kind: ok) {"result":{"kind":"Function","result":1},"shouldInvokeFns":true}
+{"result":{"kind":"Function","result":2},"shouldInvokeFns":true}
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/return-function.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/return-function.ts
new file mode 100644
index 0000000000..f0e0ac77f0
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/return-function.ts
@@ -0,0 +1,28 @@
+import {createHookWrapper} from 'shared-runtime';
+
+/**
+ * Assume that directly returned functions are invoked and that their property
+ * loads are hoistable.
+ */
+function useMakeCallback({
+ obj,
+ setState,
+}: {
+ obj: {value: number};
+ setState: (newState: number) => void;
+}) {
+ return () => setState(obj.value);
+}
+
+const setState = (arg: number) => {
+ 'use no memo';
+ return arg;
+};
+export const FIXTURE_ENTRYPOINT = {
+ fn: createHookWrapper(useMakeCallback),
+ params: [{obj: {value: 1}, setState}],
+ sequentialRenders: [
+ {obj: {value: 1}, setState},
+ {obj: {value: 2}, setState},
+ ],
+};
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/use-memo-returned.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/use-memo-returned.expect.md
new file mode 100644
index 0000000000..e750b8ab84
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/use-memo-returned.expect.md
@@ -0,0 +1,82 @@
+
+## Input
+
+```javascript
+import {useState, useMemo} from 'react';
+import {useIdentity} from 'shared-runtime';
+
+/**
+ * Assume that conditionally called functions can be invoked and that their
+ * property loads are hoistable to the function declaration site.
+ */
+function useMakeCallback({
+ obj,
+ shouldSynchronizeState,
+}: {
+ obj: {value: number};
+ shouldSynchronizeState: boolean;
+}) {
+ const [state, setState] = useState(0);
+ const cb = useMemo(() => {
+ return () => {
+ if (obj.value !== 0) setState(obj.value);
+ };
+ }, [obj.value, shouldSynchronizeState]);
+ useIdentity(null);
+ return cb;
+}
+export const FIXTURE_ENTRYPOINT = {
+ fn: useMakeCallback,
+ params: [{obj: {value: 1}}],
+ sequentialRenders: [{obj: {value: 1}}, {obj: {value: 2}}],
+};
+
+```
+
+## Code
+
+```javascript
+import { c as _c } from "react/compiler-runtime";
+import { useState, useMemo } from "react";
+import { useIdentity } from "shared-runtime";
+
+/**
+ * Assume that conditionally called functions can be invoked and that their
+ * property loads are hoistable to the function declaration site.
+ */
+function useMakeCallback(t0) {
+ const $ = _c(2);
+ const { obj, shouldSynchronizeState } = t0;
+
+ const [, setState] = useState(0);
+ let t1;
+ let t2;
+ if ($[0] !== obj.value) {
+ t2 = () => {
+ if (obj.value !== 0) {
+ setState(obj.value);
+ }
+ };
+ $[0] = obj.value;
+ $[1] = t2;
+ } else {
+ t2 = $[1];
+ }
+ t1 = t2;
+ const cb = t1;
+
+ useIdentity(null);
+ return cb;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: useMakeCallback,
+ params: [{ obj: { value: 1 } }],
+ sequentialRenders: [{ obj: { value: 1 } }, { obj: { value: 2 } }],
+};
+
+```
+
+### Eval output
+(kind: ok) "[[ function params=0 ]]"
+"[[ function params=0 ]]"
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/use-memo-returned.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/use-memo-returned.ts
new file mode 100644
index 0000000000..6cb2e44a2b
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/assume-invoked/use-memo-returned.ts
@@ -0,0 +1,28 @@
+import {useState, useMemo} from 'react';
+import {useIdentity} from 'shared-runtime';
+
+/**
+ * Assume that conditionally called functions can be invoked and that their
+ * property loads are hoistable to the function declaration site.
+ */
+function useMakeCallback({
+ obj,
+ shouldSynchronizeState,
+}: {
+ obj: {value: number};
+ shouldSynchronizeState: boolean;
+}) {
+ const [state, setState] = useState(0);
+ const cb = useMemo(() => {
+ return () => {
+ if (obj.value !== 0) setState(obj.value);
+ };
+ }, [obj.value, shouldSynchronizeState]);
+ useIdentity(null);
+ return cb;
+}
+export const FIXTURE_ENTRYPOINT = {
+ fn: useMakeCallback,
+ params: [{obj: {value: 1}}],
+ sequentialRenders: [{obj: {value: 1}}, {obj: {value: 2}}],
+};
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/bug-invalid-array-map-manual.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/bug-invalid-array-map-manual.expect.md
new file mode 100644
index 0000000000..d00e71e14f
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/bug-invalid-array-map-manual.expect.md
@@ -0,0 +1,68 @@
+
+## Input
+
+```javascript
+function useFoo({arr1, arr2}) {
+ const cb = e => arr2[0].value + e.value;
+ const y = [];
+ for (let i = 0; i < arr1.length; i++) {
+ y.push(cb(arr1[i]));
+ }
+ return y;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: useFoo,
+ params: [{arr1: [], arr2: []}],
+ sequentialRenders: [
+ {arr1: [], arr2: []},
+ {arr1: [], arr2: null},
+ {arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
+ ],
+};
+
+```
+
+## Code
+
+```javascript
+import { c as _c } from "react/compiler-runtime";
+function useFoo(t0) {
+ const $ = _c(5);
+ const { arr1, arr2 } = t0;
+ let t1;
+ if ($[0] !== arr2[0].value) {
+ t1 = (e) => arr2[0].value + e.value;
+ $[0] = arr2[0].value;
+ $[1] = t1;
+ } else {
+ t1 = $[1];
+ }
+ const cb = t1;
+ let y;
+ if ($[2] !== arr1 || $[3] !== cb) {
+ y = [];
+ for (let i = 0; i < arr1.length; i++) {
+ y.push(cb(arr1[i]));
+ }
+ $[2] = arr1;
+ $[3] = cb;
+ $[4] = y;
+ } else {
+ y = $[4];
+ }
+ return y;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: useFoo,
+ params: [{ arr1: [], arr2: [] }],
+ sequentialRenders: [
+ { arr1: [], arr2: [] },
+ { arr1: [], arr2: null },
+ { arr1: [{ value: 1 }, { value: 2 }], arr2: [{ value: -1 }] },
+ ],
+};
+
+```
+
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/bug-invalid-array-map-manual.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/bug-invalid-array-map-manual.js
new file mode 100644
index 0000000000..eed7561306
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/bug-invalid-array-map-manual.js
@@ -0,0 +1,18 @@
+function useFoo({arr1, arr2}) {
+ const cb = e => arr2[0].value + e.value;
+ const y = [];
+ for (let i = 0; i < arr1.length; i++) {
+ y.push(cb(arr1[i]));
+ }
+ return y;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: useFoo,
+ params: [{arr1: [], arr2: []}],
+ sequentialRenders: [
+ {arr1: [], arr2: []},
+ {arr1: [], arr2: null},
+ {arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
+ ],
+};
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/return-object-of-functions.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/return-object-of-functions.expect.md
new file mode 100644
index 0000000000..5ccf5b5edc
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/return-object-of-functions.expect.md
@@ -0,0 +1,57 @@
+
+## Input
+
+```javascript
+/**
+ * Assume that only directly returned functions or JSX attributes are invoked.
+ * Conservatively estimate that functions wrapped in objects or other containers
+ * might never be called (and therefore their property loads are not hoistable).
+ */
+function useMakeCallback({arr}) {
+ return {
+ getElement0: () => arr[0].value,
+ getElement1: () => arr[1].value,
+ };
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: useMakeCallback,
+ params: [{arr: [1, 2]}],
+ sequentialRenders: [{arr: [1, 2]}, {arr: []}],
+};
+
+```
+
+## Code
+
+```javascript
+import { c as _c } from "react/compiler-runtime"; /**
+ * Assume that only directly returned functions or JSX attributes are invoked.
+ * Conservatively estimate that functions wrapped in objects or other containers
+ * might never be called (and therefore their property loads are not hoistable).
+ */
+function useMakeCallback(t0) {
+ const $ = _c(2);
+ const { arr } = t0;
+ let t1;
+ if ($[0] !== arr) {
+ t1 = { getElement0: () => arr[0].value, getElement1: () => arr[1].value };
+ $[0] = arr;
+ $[1] = t1;
+ } else {
+ t1 = $[1];
+ }
+ return t1;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: useMakeCallback,
+ params: [{ arr: [1, 2] }],
+ sequentialRenders: [{ arr: [1, 2] }, { arr: [] }],
+};
+
+```
+
+### Eval output
+(kind: ok) {"getElement0":"[[ function params=0 ]]","getElement1":"[[ function params=0 ]]"}
+{"getElement0":"[[ function params=0 ]]","getElement1":"[[ function params=0 ]]"}
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/return-object-of-functions.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/return-object-of-functions.js
new file mode 100644
index 0000000000..6aface49f8
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-function/nullable-objects/return-object-of-functions.js
@@ -0,0 +1,17 @@
+/**
+ * Assume that only directly returned functions or JSX attributes are invoked.
+ * Conservatively estimate that functions wrapped in objects or other containers
+ * might never be called (and therefore their property loads are not hoistable).
+ */
+function useMakeCallback({arr}) {
+ return {
+ getElement0: () => arr[0].value,
+ getElement1: () => arr[1].value,
+ };
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: useMakeCallback,
+ params: [{arr: [1, 2]}],
+ sequentialRenders: [{arr: [1, 2]}, {arr: []}],
+};
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 ca65ce72bc..53d3d04531 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
@@ -41,9 +41,9 @@ function useFoo(t0) {
local = $[1];
}
let t1;
- if ($[2] !== local.b.c) {
+ if ($[2] !== local) {
t1 = () => [() => local.b.c];
- $[2] = local.b.c;
+ $[2] = local;
$[3] = t1;
} else {
t1 = $[3];
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 7d75470550..f8a8af1fd4 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
@@ -34,13 +34,13 @@ function useFoo(t0) {
const $ = _c(4);
const { a } = t0;
let t1;
- if ($[0] !== a.b.c) {
+ if ($[0] !== a) {
t1 = {
fn() {
return identity(a.b.c);
},
};
- $[0] = a.b.c;
+ $[0] = a;
$[1] = t1;
} else {
t1 = $[1];
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-control-dependency-on-context-variable.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-control-dependency-on-context-variable.expect.md
index ceaa350012..963024e887 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-control-dependency-on-context-variable.expect.md
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactive-control-dependency-on-context-variable.expect.md
@@ -51,7 +51,7 @@ import { identity } from "shared-runtime";
function Component(props) {
const $ = _c(4);
let x;
- if ($[0] !== props.cond) {
+ if ($[0] !== props) {
const f = () => {
if (props.cond) {
x = 1;
@@ -62,7 +62,7 @@ function Component(props) {
const f2 = identity(f);
f2();
- $[0] = props.cond;
+ $[0] = props;
$[1] = x;
} else {
x = $[1];
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/context-var-granular-dep.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/context-var-granular-dep.expect.md
index d72f34b4fd..f887870197 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/context-var-granular-dep.expect.md
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reduce-reactive-deps/context-var-granular-dep.expect.md
@@ -82,9 +82,9 @@ function Component(t0) {
contextVar = $[2];
}
let t1;
- if ($[3] !== contextVar.val) {
+ if ($[3] !== contextVar) {
t1 = { cb: () => contextVar.val * 4 };
- $[3] = contextVar.val;
+ $[3] = contextVar;
$[4] = t1;
} else {
t1 = $[4];
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rename-source-variables-nested-object-method.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rename-source-variables-nested-object-method.expect.md
index d0f3d5dcfe..e406f3a7d7 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rename-source-variables-nested-object-method.expect.md
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/rename-source-variables-nested-object-method.expect.md
@@ -43,7 +43,7 @@ const t0 = "module_t0";
const c_0 = "module_c_0";
function useFoo(props) {
const $0 = _c(2);
- const c_00 = $0[0] !== props.value;
+ const c_00 = $0[0] !== props;
let t1;
if (c_00) {
const a = {
@@ -61,7 +61,7 @@ function useFoo(props) {
};
t1 = a.foo().bar();
- $0[0] = props.value;
+ $0[0] = props;
$0[1] = t1;
} else {
t1 = $0[1];
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useEffect-nested-lambdas.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useEffect-nested-lambdas.expect.md
index c3e115fa0d..0cce42e97a 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useEffect-nested-lambdas.expect.md
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useEffect-nested-lambdas.expect.md
@@ -35,7 +35,7 @@ function Component(props) {
import { c as _c } from "react/compiler-runtime"; // @enableTransitivelyFreezeFunctionExpressions:false
function Component(props) {
- const $ = _c(9);
+ const $ = _c(7);
const item = useMutable(props.itemId);
const dispatch = useDispatch();
useFreeze(dispatch);
@@ -51,7 +51,8 @@ function Component(props) {
}
const exit = t0;
let t1;
- if ($[2] !== exit || $[3] !== item.value) {
+ let t2;
+ if ($[2] !== exit || $[3] !== item) {
t1 = () => {
const cleanup = GlobalEventEmitter.addListener("onInput", () => {
if (item.value) {
@@ -60,30 +61,24 @@ function Component(props) {
});
return () => cleanup.remove();
};
+ t2 = [exit, item];
$[2] = exit;
- $[3] = item.value;
+ $[3] = item;
$[4] = t1;
+ $[5] = t2;
} else {
t1 = $[4];
- }
- let t2;
- if ($[5] !== exit || $[6] !== item) {
- t2 = [exit, item];
- $[5] = exit;
- $[6] = item;
- $[7] = t2;
- } else {
- t2 = $[7];
+ t2 = $[5];
}
useEffect(t1, t2);
maybeMutate(item);
let t3;
- if ($[8] === Symbol.for("react.memo_cache_sentinel")) {
+ if ($[6] === Symbol.for("react.memo_cache_sentinel")) {
t3 = ;
- $[8] = t3;
+ $[6] = t3;
} else {
- t3 = $[8];
+ t3 = $[6];
}
return t3;
}
diff --git a/compiler/packages/snap/src/SproutTodoFilter.ts b/compiler/packages/snap/src/SproutTodoFilter.ts
index 7c487869d3..62b8a7703f 100644
--- a/compiler/packages/snap/src/SproutTodoFilter.ts
+++ b/compiler/packages/snap/src/SproutTodoFilter.ts
@@ -450,6 +450,7 @@ const skipFilter = new Set([
'invalid-jsx-lowercase-localvar',
// bugs
+ 'inner-function/nullable-objects/bug-invalid-array-map-manual',
'bug-object-expression-computed-key-modified-during-after-construction-hoisted-sequence-expr',
`bug-capturing-func-maybealias-captured-mutate`,
'bug-aliased-capture-aliased-mutate',
From c2a196174763e0b4f16ed1c512ed4442b062395e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?=
Date: Tue, 18 Mar 2025 19:20:34 -0400
Subject: [PATCH 2/3] Minor Fixes to View Transition Fixture (#32664)
Follow up to #32656.
Remove touchAction from SwipeRecognizer. I was under the wrong
impression that this was only the touch-action applied to this
particular element, but that parents would still win but in fact this
blocks the parent from scrolling in the other direction. By specifying a
fixed direction it also blocked rage-swiping in the other direction
early on.
Disable pointer-events on view-transition so that the scroll can be hit.
This means that touches hit below the items animating above. This allows
swiping to happen again before momentum scroll has finished. Previously
they were ignored. This only works as long as the SwipeRecognizer is
itself not animating. This means you can now rage-swipe in both
directions quickly.
---
fixtures/view-transition/src/components/Chrome.css | 7 +++++++
fixtures/view-transition/src/components/SwipeRecognizer.js | 1 -
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/fixtures/view-transition/src/components/Chrome.css b/fixtures/view-transition/src/components/Chrome.css
index ade2f73952..969f1e2d32 100644
--- a/fixtures/view-transition/src/components/Chrome.css
+++ b/fixtures/view-transition/src/components/Chrome.css
@@ -7,3 +7,10 @@ body {
padding: 0;
font-family: sans-serif;
}
+
+::view-transition-group(*),
+::view-transition-image-pair(*),
+::view-transition-old(*),
+::view-transition-new(*) {
+ pointer-events: none;
+}
diff --git a/fixtures/view-transition/src/components/SwipeRecognizer.js b/fixtures/view-transition/src/components/SwipeRecognizer.js
index 8e913dfb4e..0b16967f51 100644
--- a/fixtures/view-transition/src/components/SwipeRecognizer.js
+++ b/fixtures/view-transition/src/components/SwipeRecognizer.js
@@ -92,7 +92,6 @@ export default function SwipeRecognizer({
width: axis === 'x' ? '100%' : null,
height: axis === 'y' ? '100%' : null,
overflow: 'scroll hidden',
- touchAction: 'pan-' + direction,
// Disable overscroll on Safari which moves the sticky content.
// Unfortunately, this also means that we disable chaining. We should only disable
// it if the parent is not scrollable in this axis.
From 5a40bab58c80f71bdb341bc41217dbef9660e3f2 Mon Sep 17 00:00:00 2001
From: Lauren Tan
Date: Wed, 19 Mar 2025 11:24:29 -0400
Subject: [PATCH 3/3] [ci] Properly format commit message
Using the github variable for the commit message replaces the variable inline. If the commit message contains quotes or other characters that need to be escaped, this breaks the workflow. To fix this we instead output the message as an env variable.
---
.github/workflows/runtime_commit_artifacts.yml | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/.github/workflows/runtime_commit_artifacts.yml b/.github/workflows/runtime_commit_artifacts.yml
index 5de51b2664..450e7bb773 100644
--- a/.github/workflows/runtime_commit_artifacts.yml
+++ b/.github/workflows/runtime_commit_artifacts.yml
@@ -252,9 +252,9 @@ jobs:
git config --global user.email "${{ format('{0}@users.noreply.github.com', github.triggering_actor) }}"
git config --global user.name "${{ github.triggering_actor }}"
- git commit -m "${{ github.event.workflow_run.head_commit.message || format('Manual build of {0}', github.event.workflow_run.head_sha || github.sha) }}
-
- DiffTrain build for [${{ github.event.workflow_run.head_sha || github.sha }}](https://github.com/facebook/react/commit/${{ github.event.workflow_run.head_sha || github.sha }})" || echo "No changes to commit"
+ git commit -m $COMMIT_MSG || echo "No changes to commit"
+ env:
+ COMMIT_MSG: "$(git show --no-patch --pretty=format:'%B%n%nDiffTrain build for [${{ github.event.workflow_run.head_sha || github.sha }}](https://github.com/facebook/react/commit/${{ github.event.workflow_run.head_sha || github.sha}})')"
- name: Push changes to branch
if: inputs.dry_run == false && (inputs.force == true || steps.check_should_commit.outputs.should_commit == 'true')
run: git push
@@ -419,9 +419,9 @@ jobs:
git config --global user.email "${{ format('{0}@users.noreply.github.com', github.triggering_actor) }}"
git config --global user.name "${{ github.triggering_actor }}"
- git commit -m "${{ github.event.workflow_run.head_commit.message || format('Manual build of {0}', github.event.workflow_run.head_sha || github.sha) }}
-
- DiffTrain build for [${{ github.event.workflow_run.head_sha || github.sha }}](https://github.com/facebook/react/commit/${{ github.event.workflow_run.head_sha || github.sha }})" || echo "No changes to commit"
+ git commit -m $COMMIT_MSG || echo "No changes to commit"
+ env:
+ COMMIT_MSG: "$(git show --no-patch --pretty=format:'%B%n%nDiffTrain build for [${{ github.event.workflow_run.head_sha || github.sha }}](https://github.com/facebook/react/commit/${{ github.event.workflow_run.head_sha || github.sha}})')"
- name: Push changes to branch
if: inputs.dry_run == false && (inputs.force == true || steps.check_should_commit.outputs.should_commit == 'true')
run: git push