@samitouri / QOS-React-2 / commits / f504eaa16e

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 ?? [];