diff --git a/compiler/packages/babel-plugin-react-forget/src/HIR/Environment.ts b/compiler/packages/babel-plugin-react-forget/src/HIR/Environment.ts
index fc9ac6d7b1..134f0cb3bc 100644
--- a/compiler/packages/babel-plugin-react-forget/src/HIR/Environment.ts
+++ b/compiler/packages/babel-plugin-react-forget/src/HIR/Environment.ts
@@ -119,6 +119,10 @@ const EnvironmentConfigSchema = z.object({
* Our recommendation is to first try running your application with this flag enabled, then attempt
* to disable this flag and see what changes or breaks. This will mostly likely be effects that
* depend on referential equality, which can be refactored (TODO guide for this).
+ *
+ * NOTE: this mode treats freeze as a transitive operation for function expressions. This means
+ * that if a useEffect or useCallback references a function value, that function value will be
+ * considered frozen, and in turn all of its referenced variables will be considered frozen as well.
*/
enablePreserveExistingMemoizationGuarantees: z.boolean().default(false),
@@ -245,41 +249,6 @@ const EnvironmentConfigSchema = z.object({
*/
enableEmitInstrumentForget: ExternalFunctionSchema.nullish(),
- /*
- * Forget infers certain operations as "freezing" a value, such that those
- * values should not be subsequently mutated. By default this freeze operation
- * applies to the value itself and its direct aliases, but not values captured
- * by the value being frozen.
- *
- * In the following, passing `x` to JSX freezes it, which includes freezing `y`
- * and `z` which x may alias:
- *
- * ```
- * let x;
- * if (cond) {
- * x = y
- * } else {
- * x = z;
- * }
- *
{x}
- * ```
- *
- * However, in the following example we currently only consider x itself to be
- * frozen, not `y` or `z`:
- *
- * ```
- * let y = ...;
- * let z = ...;
- * let x = () => { return [y, z]; };
- * {x}
- * ```
- *
- * With this flag enabled, function expression dependencies (values closed over)
- * are transitively frozen when the function itself is frozen. So in this case,
- * `y` and `z` would be frozen when `x` is frozen.
- */
- enableTransitivelyFreezeFunctionExpressions: z.boolean().default(false),
-
// Enable merging consecutive scopes that invalidate together.
enableMergeConsecutiveScopes: z.boolean().default(true),
diff --git a/compiler/packages/babel-plugin-react-forget/src/Inference/InferReferenceEffects.ts b/compiler/packages/babel-plugin-react-forget/src/Inference/InferReferenceEffects.ts
index 200f3762f7..a7227a5497 100644
--- a/compiler/packages/babel-plugin-react-forget/src/Inference/InferReferenceEffects.ts
+++ b/compiler/packages/babel-plugin-react-forget/src/Inference/InferReferenceEffects.ts
@@ -354,7 +354,7 @@ class InferenceState {
reason: reasonSet,
});
- if (this.#env.config.enableTransitivelyFreezeFunctionExpressions) {
+ if (this.#env.config.enablePreserveExistingMemoizationGuarantees) {
if (value.kind === "FunctionExpression") {
for (const operand of eachInstructionValueOperand(value)) {
this.reference(operand, Effect.Freeze, ValueReason.Other);
diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/transitive-freeze-array.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/transitive-freeze-array.expect.md
index f019a12a0c..794af42f53 100644
--- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/transitive-freeze-array.expect.md
+++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/transitive-freeze-array.expect.md
@@ -2,7 +2,7 @@
## Input
```javascript
-// @enableTransitivelyFreezeFunctionExpressions
+// @enablePreserveExistingMemoizationGuarantees
const { mutate } = require("shared-runtime");
function Component(props) {
@@ -10,7 +10,7 @@ function Component(props) {
const y = {};
const items = [x, y];
items.pop();
- {items}
; // note: enableTransitivelyFreezeFunctionExpressions only visits function expressions, not arrays, so this doesn't freeze x/y
+ {items}
; // note: enablePreserveExistingMemoizationGuarantees only visits function expressions, not arrays, so this doesn't freeze x/y
mutate(y); // ok! not part of `items` anymore bc of items.pop()
return [x, y, items];
}
@@ -25,7 +25,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
-import { unstable_useMemoCache as useMemoCache } from "react"; // @enableTransitivelyFreezeFunctionExpressions
+import { unstable_useMemoCache as useMemoCache } from "react"; // @enablePreserveExistingMemoizationGuarantees
const { mutate } = require("shared-runtime");
function Component(props) {
diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/transitive-freeze-array.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/transitive-freeze-array.js
index ee9985c731..32f45d27d2 100644
--- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/transitive-freeze-array.js
+++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/transitive-freeze-array.js
@@ -1,4 +1,4 @@
-// @enableTransitivelyFreezeFunctionExpressions
+// @enablePreserveExistingMemoizationGuarantees
const { mutate } = require("shared-runtime");
function Component(props) {
@@ -6,7 +6,7 @@ function Component(props) {
const y = {};
const items = [x, y];
items.pop();
- {items}
; // note: enableTransitivelyFreezeFunctionExpressions only visits function expressions, not arrays, so this doesn't freeze x/y
+ {items}
; // note: enablePreserveExistingMemoizationGuarantees only visits function expressions, not arrays, so this doesn't freeze x/y
mutate(y); // ok! not part of `items` anymore bc of items.pop()
return [x, y, items];
}
diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/transitive-freeze-function-expressions.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/transitive-freeze-function-expressions.expect.md
index 009fe6d34d..8478234590 100644
--- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/transitive-freeze-function-expressions.expect.md
+++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/transitive-freeze-function-expressions.expect.md
@@ -2,7 +2,7 @@
## Input
```javascript
-// @enableTransitivelyFreezeFunctionExpressions
+// @enablePreserveExistingMemoizationGuarantees
function Component(props) {
const { data, loadNext, isLoadingNext } =
usePaginationFragment(props.key).items ?? [];
@@ -31,7 +31,7 @@ function Component(props) {
## Code
```javascript
-import { unstable_useMemoCache as useMemoCache } from "react"; // @enableTransitivelyFreezeFunctionExpressions
+import { unstable_useMemoCache as useMemoCache } from "react"; // @enablePreserveExistingMemoizationGuarantees
function Component(props) {
const $ = useMemoCache(10);
const { data, loadNext, isLoadingNext } =
diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/transitive-freeze-function-expressions.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/transitive-freeze-function-expressions.js
index 7a3dd5a5e5..bd6897cd32 100644
--- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/transitive-freeze-function-expressions.js
+++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/transitive-freeze-function-expressions.js
@@ -1,4 +1,4 @@
-// @enableTransitivelyFreezeFunctionExpressions
+// @enablePreserveExistingMemoizationGuarantees
function Component(props) {
const { data, loadNext, isLoadingNext } =
usePaginationFragment(props.key).items ?? [];
diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/useCallback-call-second-function-which-captures-maybe-mutable-value-preserve-memoization.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/useCallback-call-second-function-which-captures-maybe-mutable-value-preserve-memoization.expect.md
index 874a4120eb..30e27e710b 100644
--- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/useCallback-call-second-function-which-captures-maybe-mutable-value-preserve-memoization.expect.md
+++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/useCallback-call-second-function-which-captures-maybe-mutable-value-preserve-memoization.expect.md
@@ -49,28 +49,47 @@ import {
} from "shared-runtime";
function Component(props) {
- const $ = useMemoCache(1);
- const object = makeObject_Primitives();
-
- useHook();
-
- const log = () => {
- logValue(object);
- };
-
- const onClick = () => {
- log();
- };
-
- identity(object);
+ const $ = useMemoCache(4);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
- t0 = ;
+ t0 = makeObject_Primitives();
$[0] = t0;
} else {
t0 = $[0];
}
- return t0;
+ const object = t0;
+
+ useHook();
+ let t1;
+ if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
+ t1 = () => {
+ logValue(object);
+ };
+ $[1] = t1;
+ } else {
+ t1 = $[1];
+ }
+ const log = t1;
+ let t2;
+ if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
+ t2 = () => {
+ log();
+ };
+ $[2] = t2;
+ } else {
+ t2 = $[2];
+ }
+ const onClick = t2;
+
+ identity(object);
+ let t3;
+ if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
+ t3 = ;
+ $[3] = t3;
+ } else {
+ t3 = $[3];
+ }
+ return t3;
}
export const FIXTURE_ENTRYPOINT = {