From dcbdf064915fc2187687acc12cf62232ab2b2a47 Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Mon, 13 Nov 2023 11:03:00 -0800 Subject: [PATCH] Extra test case related to memoization "within" freeze I found an interesting edge case in the previous diff with mutation of a value that appears in the expression of an object key: ```javascript const key = {} const object = { [mutateAndReturnOtherValue(key)]: 42, }; mutate(key); ``` We analyze and represent this correctly all the way through to codegen, but then we hit the bug that @mofeiZ has noticed before: the temporary for `t = mutateAndReturnOtherValue(key)` isn't emitted immediately (bc its a temporary). It gets emitted inside the memo block for `object`, which is incorrect. I tried to reproduce that here with JSX and it works as expected. It's an interesting case though so let's land this to ensure we don't regress. --- ...mutation-during-jsx-construction.expect.md | 53 +++++++++++++++++++ .../mutation-during-jsx-construction.js | 16 ++++++ .../packages/sprout/src/shared-runtime.ts | 5 ++ 3 files changed, 74 insertions(+) create mode 100644 compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/mutation-during-jsx-construction.expect.md create mode 100644 compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/mutation-during-jsx-construction.js diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/mutation-during-jsx-construction.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/mutation-during-jsx-construction.expect.md new file mode 100644 index 0000000000..5975d82411 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/mutation-during-jsx-construction.expect.md @@ -0,0 +1,53 @@ + +## Input + +```javascript +import { identity, mutate, mutateAndReturnNewValue } from "shared-runtime"; + +function Component(props) { + const key = {}; + // Key is modified by the function, but key itself is not frozen + const element =
{props.value}
; + // Key is later mutated here: this mutation must be grouped with the + // jsx construction above + mutate(key); + return element; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ value: 42 }], +}; + +``` + +## Code + +```javascript +import { unstable_useMemoCache as useMemoCache } from "react"; +import { identity, mutate, mutateAndReturnNewValue } from "shared-runtime"; + +function Component(props) { + const $ = useMemoCache(2); + let element; + if ($[0] !== props.value) { + const key = {}; + + element =
{props.value}
; + + mutate(key); + $[0] = props.value; + $[1] = element; + } else { + element = $[1]; + } + return element; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ value: 42 }], +}; + +``` + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/mutation-during-jsx-construction.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/mutation-during-jsx-construction.js new file mode 100644 index 0000000000..1d7302bce5 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/mutation-during-jsx-construction.js @@ -0,0 +1,16 @@ +import { identity, mutate, mutateAndReturnNewValue } from "shared-runtime"; + +function Component(props) { + const key = {}; + // Key is modified by the function, but key itself is not frozen + const element =
{props.value}
; + // Key is later mutated here: this mutation must be grouped with the + // jsx construction above + mutate(key); + return element; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ value: 42 }], +}; diff --git a/compiler/packages/sprout/src/shared-runtime.ts b/compiler/packages/sprout/src/shared-runtime.ts index 928c064fee..880b5df848 100644 --- a/compiler/packages/sprout/src/shared-runtime.ts +++ b/compiler/packages/sprout/src/shared-runtime.ts @@ -73,6 +73,11 @@ export function mutateAndReturn(arg: T): T { return arg; } +export function mutateAndReturnNewValue(arg: T): string { + mutate(arg); + return "hello!"; +} + export function setProperty(arg: any, property: any): void { // don't mutate primitive if (typeof arg === null || typeof arg !== "object") {