Add back transitive freeze functions option
Adds back a mode to transitively freeze function expressions, independently from the mode to preserve existing manual memoization. This lets us experiment with a few variants: * Preserve existing memoization * Validate existing memoization with: * `enableAssumeHooksFollowRulesOfReact` && `enableTransitivelyFreezeFunctionExpressions` * `enableAssumeHooksFollowRulesOfReact` only * neither of those flags Note that `enableTransitivelyFreezeFunctionExpressions` alone probably doesn't make sense, it's more aggressive than `enableAssumeHooksFollowRulesOfReact` so we might as well try them together.
Joe Savona committed
Dec 18, 2023 at 15:33 UTC
f504eaa16ecbfc1000d9fc9fc007b2ec313f7dda
4 files changed
+15
-4
compiler/packages/babel-plugin-react-forget/src/HIR/Environment.ts
+8
@@ -210,6 +210,14 @@ const EnvironmentConfigSchema = z.object({
210
*/
211
enableAssumeHooksFollowRulesOfReact: z.boolean().default(false),
212
213
+ /**
214
+ * When enabled, the compiler assumes that any values are not subsequently
215
+ * modified after they are captured by a function passed to React. For example,
216
+ * if a value `x` is referenced inside a function expression passed to `useEffect`,
217
+ * then this flag will assume that `x` is not subusequently modified.
218
+ */
219
+ enableTransitivelyFreezeFunctionExpressions: z.boolean().default(false),
220
+
221
/*
222
* When enabled, removes *all* memoization from the function: this includes
223
* removing manually added useMemo/useCallback as well as not adding Forget's
compiler/packages/babel-plugin-react-forget/src/Inference/InferReferenceEffects.ts
+4
-1
@@ -354,7 +354,10 @@ class InferenceState {
354
reason: reasonSet,
355
});
356
357
- if (this.#env.config.enablePreserveExistingMemoizationGuarantees) {
357
+ if (
358
+ this.#env.config.enablePreserveExistingMemoizationGuarantees ||
359
+ this.#env.config.enableTransitivelyFreezeFunctionExpressions
360
+ ) {
361
if (value.kind === "FunctionExpression") {
362
for (const operand of eachInstructionValueOperand(value)) {
363
this.reference(operand, Effect.Freeze, ValueReason.Other);
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/transitive-freeze-function-expressions.expect.md
+2
-2
@@ -2,7 +2,7 @@
2
## Input
3
4
```javascript
5
-// @enablePreserveExistingMemoizationGuarantees
5
+// @enableTransitivelyFreezeFunctionExpressions
6
function Component(props) {
7
const { data, loadNext, isLoadingNext } =
8
usePaginationFragment(props.key).items ?? [];
@@ -31,7 +31,7 @@ function Component(props) {
31
## Code
32
33
```javascript
34
-import { unstable_useMemoCache as useMemoCache } from "react"; // @enablePreserveExistingMemoizationGuarantees
34
+import { unstable_useMemoCache as useMemoCache } from "react"; // @enableTransitivelyFreezeFunctionExpressions
35
function Component(props) {
36
const $ = useMemoCache(10);
37
const { data, loadNext, isLoadingNext } =
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/transitive-freeze-function-expressions.js
+1
-1
@@ -1,4 +1,4 @@
1
-// @enablePreserveExistingMemoizationGuarantees
1
+// @enableTransitivelyFreezeFunctionExpressions
2
function Component(props) {
3
const { data, loadNext, isLoadingNext } =
4
usePaginationFragment(props.key).items ?? [];