diff --git a/compiler/packages/babel-plugin-react-forget/src/HIR/FindContextIdentifiers.ts b/compiler/packages/babel-plugin-react-forget/src/HIR/FindContextIdentifiers.ts index dd8f0bfc8a..4e776f904f 100644 --- a/compiler/packages/babel-plugin-react-forget/src/HIR/FindContextIdentifiers.ts +++ b/compiler/packages/babel-plugin-react-forget/src/HIR/FindContextIdentifiers.ts @@ -8,9 +8,20 @@ import type { NodePath } from "@babel/traverse"; import type * as t from "@babel/types"; import { CompilerError } from "../CompilerError"; -import { Set_union } from "../Utils/utils"; +import { getOrInsertDefault } from "../Utils/utils"; import { GeneratedSource } from "./HIR"; +type IdentifierInfo = { + reassigned: boolean; + reassignedByInnerFn: boolean; + referencedByInnerFn: boolean; +}; +const DEFAULT_IDENTIFIER_INFO: IdentifierInfo = { + reassigned: false, + reassignedByInnerFn: false, + referencedByInnerFn: false, +}; + type BabelFunction = | NodePath | NodePath @@ -18,8 +29,7 @@ type BabelFunction = | NodePath; type FindContextIdentifierState = { currentFn: Array; - reassigned: Set; - referenced: Set; + identifiers: Map; }; const withFunctionScope = { @@ -39,8 +49,7 @@ export function findContextIdentifiers( ): Set { const state: FindContextIdentifierState = { currentFn: [], - reassigned: new Set(), - referenced: new Set(), + identifiers: new Map(), }; func.traverse( @@ -54,38 +63,59 @@ export function findContextIdentifiers( state: FindContextIdentifierState ): void { const left = path.get("left"); - handleAssignment(state.reassigned, left); + const currentFn = state.currentFn.at(-1) ?? null; + handleAssignment(currentFn, state.identifiers, left); }, Identifier( path: NodePath, state: FindContextIdentifierState ): void { - const currentFn = state.currentFn.at(-1); - if (currentFn !== undefined) - handleIdentifier(currentFn, state.referenced, path); + const currentFn = state.currentFn.at(-1) ?? null; + if (path.isReferencedIdentifier()) { + handleIdentifier(currentFn, state.identifiers, path); + } }, }, state ); - return Set_union(state.reassigned, state.referenced); + + const result = new Set(); + for (const [id, info] of state.identifiers.entries()) { + if (info.reassignedByInnerFn) { + result.add(id); + } else if (info.reassigned && info.referencedByInnerFn) { + result.add(id); + } + } + return result; } function handleIdentifier( - currentFn: BabelFunction, - referenced: Set, + currentFn: BabelFunction | null, + identifiers: Map, path: NodePath ): void { const name = path.node.name; const binding = path.scope.getBinding(name); - const bindingAboveLambdaScope = currentFn.scope.parent.getBinding(name); + if (binding == null) { + return; + } + const identifier = getOrInsertDefault(identifiers, binding.identifier, { + ...DEFAULT_IDENTIFIER_INFO, + }); - if (binding != null && binding === bindingAboveLambdaScope) { - referenced.add(binding.identifier); + if (currentFn != null) { + const bindingAboveLambdaScope = currentFn.scope.parent.getBinding(name); + + if (binding === bindingAboveLambdaScope) { + identifier.referencedByInnerFn = true; + } } } function handleAssignment( - reassigned: Set, + currentFn: BabelFunction | null, + identifiers: Map, lvalPath: NodePath ): void { /* @@ -98,8 +128,20 @@ function handleAssignment( const path = lvalPath as NodePath; const name = path.node.name; const binding = path.scope.getBinding(name); - if (binding != null) { - reassigned.add(binding.identifier); + if (binding == null) { + break; + } + const state = getOrInsertDefault(identifiers, binding.identifier, { + ...DEFAULT_IDENTIFIER_INFO, + }); + state.reassigned = true; + + if (currentFn != null) { + const bindingAboveLambdaScope = currentFn.scope.parent.getBinding(name); + + if (binding === bindingAboveLambdaScope) { + state.reassignedByInnerFn = true; + } } break; } @@ -107,7 +149,7 @@ function handleAssignment( const path = lvalPath as NodePath; for (const element of path.get("elements")) { if (nonNull(element)) { - handleAssignment(reassigned, element); + handleAssignment(currentFn, identifiers, element); } } break; @@ -123,7 +165,7 @@ function handleAssignment( loc: valuePath.node.loc ?? GeneratedSource, suggestions: null, }); - handleAssignment(reassigned, valuePath); + handleAssignment(currentFn, identifiers, valuePath); } else { CompilerError.invariant(property.isRestElement(), { reason: `[FindContextIdentifiers] Invalid assumptions for babel types.`, @@ -131,7 +173,7 @@ function handleAssignment( loc: property.node.loc ?? GeneratedSource, suggestions: null, }); - handleAssignment(reassigned, property); + handleAssignment(currentFn, identifiers, property); } } break; @@ -139,12 +181,12 @@ function handleAssignment( case "AssignmentPattern": { const path = lvalPath as NodePath; const left = path.get("left"); - handleAssignment(reassigned, left); + handleAssignment(currentFn, identifiers, left); break; } case "RestElement": { const path = lvalPath as NodePath; - handleAssignment(reassigned, path.get("argument")); + handleAssignment(currentFn, identifiers, path.get("argument")); break; } case "MemberExpression": { diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-3.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-3.expect.md index 71a776211f..e28a4fbd6c 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-3.expect.md +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-3.expect.md @@ -17,8 +17,10 @@ function bar(a, b) { export const FIXTURE_ENTRYPOINT = { fn: bar, - params: ["TodoAdd"], - isComponent: "TodoAdd", + params: [ + [1, 2], + [2, 3], + ], }; ``` @@ -52,9 +54,13 @@ function bar(a, b) { export const FIXTURE_ENTRYPOINT = { fn: bar, - params: ["TodoAdd"], - isComponent: "TodoAdd", + params: [ + [1, 2], + [2, 3], + ], }; ``` - \ No newline at end of file + +### Eval output +(kind: ok) 2 \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-3.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-3.js index 23400ae571..3e92916e67 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-3.js +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-3.js @@ -13,6 +13,8 @@ function bar(a, b) { export const FIXTURE_ENTRYPOINT = { fn: bar, - params: ["TodoAdd"], - isComponent: "TodoAdd", + params: [ + [1, 2], + [2, 3], + ], }; diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/chained-assignment-context-variable.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/chained-assignment-context-variable.expect.md index ce24959203..68a3948a6f 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/chained-assignment-context-variable.expect.md +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/chained-assignment-context-variable.expect.md @@ -2,22 +2,31 @@ ## Input ```javascript +import { makeArray } from "shared-runtime"; + function Component() { let x, y = (x = {}); const foo = () => { - x = getObject(); + x = makeArray(); }; foo(); return [y, x]; } +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{}], +}; + ``` ## Code ```javascript import { unstable_useMemoCache as useMemoCache } from "react"; +import { makeArray } from "shared-runtime"; + function Component() { const $ = useMemoCache(3); let x; @@ -26,7 +35,7 @@ function Component() { y = x = {}; const foo = () => { - x = getObject(); + x = makeArray(); }; foo(); @@ -46,5 +55,12 @@ function Component() { return t0; } +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{}], +}; + ``` - \ No newline at end of file + +### Eval output +(kind: ok) [{},[]] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/chained-assignment-context-variable.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/chained-assignment-context-variable.js index 24a48fb378..22f0f40c65 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/chained-assignment-context-variable.js +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/chained-assignment-context-variable.js @@ -1,9 +1,16 @@ +import { makeArray } from "shared-runtime"; + function Component() { let x, y = (x = {}); const foo = () => { - x = getObject(); + x = makeArray(); }; foo(); return [y, x]; } + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{}], +}; diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/constant-prop-colliding-identifier.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/constant-prop-colliding-identifier.expect.md new file mode 100644 index 0000000000..7f42633ce5 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/constant-prop-colliding-identifier.expect.md @@ -0,0 +1,44 @@ + +## Input + +```javascript +import { invoke } from "shared-runtime"; + +function Component() { + let x = 2; + const fn = () => { + return { x: "value" }; + }; + invoke(fn); + x = 3; + return x; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{}], +}; + +``` + +## Code + +```javascript +import { invoke } from "shared-runtime"; + +function Component() { + const fn = () => ({ x: "value" }); + + invoke(fn); + return 3; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{}], +}; + +``` + +### Eval output +(kind: ok) 3 \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/constant-prop-colliding-identifier.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/constant-prop-colliding-identifier.js new file mode 100644 index 0000000000..07efcd7588 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/constant-prop-colliding-identifier.js @@ -0,0 +1,16 @@ +import { invoke } from "shared-runtime"; + +function Component() { + let x = 2; + const fn = () => { + return { x: "value" }; + }; + invoke(fn); + x = 3; + return x; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{}], +}; diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-only-chained-assign.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-only-chained-assign.expect.md new file mode 100644 index 0000000000..b5c01c9ccd --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-only-chained-assign.expect.md @@ -0,0 +1,65 @@ + +## Input + +```javascript +import { identity, invoke } from "shared-runtime"; + +function foo() { + let x = 2; + const fn1 = () => { + const copy1 = (x = 3); + return identity(copy1); + }; + const fn2 = () => { + const copy2 = (x = 4); + return [invoke(fn1), copy2, identity(copy2)]; + }; + return invoke(fn2); +} + +export const FIXTURE_ENTRYPOINT = { + fn: foo, + params: [], +}; + +``` + +## Code + +```javascript +import { unstable_useMemoCache as useMemoCache } from "react"; +import { identity, invoke } from "shared-runtime"; + +function foo() { + const $ = useMemoCache(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + let x; + x = 2; + const fn1 = () => { + const copy1 = (x = 3); + return identity(copy1); + }; + + const fn2 = () => { + const copy2 = (x = 4); + return [invoke(fn1), copy2, identity(copy2)]; + }; + + t0 = invoke(fn2); + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +} + +export const FIXTURE_ENTRYPOINT = { + fn: foo, + params: [], +}; + +``` + +### Eval output +(kind: ok) [3,4,4] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-only-chained-assign.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-only-chained-assign.js new file mode 100644 index 0000000000..adff996ce2 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-only-chained-assign.js @@ -0,0 +1,19 @@ +import { identity, invoke } from "shared-runtime"; + +function foo() { + let x = 2; + const fn1 = () => { + const copy1 = (x = 3); + return identity(copy1); + }; + const fn2 = () => { + const copy2 = (x = 4); + return [invoke(fn1), copy2, identity(copy2)]; + }; + return invoke(fn2); +} + +export const FIXTURE_ENTRYPOINT = { + fn: foo, + params: [], +}; diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reactive-explicit-control-flow.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reactive-explicit-control-flow.expect.md new file mode 100644 index 0000000000..8a3e2afa4e --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reactive-explicit-control-flow.expect.md @@ -0,0 +1,63 @@ + +## Input + +```javascript +import { invoke } from "shared-runtime"; + +function Component({ shouldReassign }) { + let x = null; + const reassign = () => { + if (shouldReassign) { + x = 2; + } + }; + invoke(reassign); + return x; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ shouldReassign: true }], + sequentialRenders: [{ shouldReassign: false }, { shouldReassign: true }], +}; + +``` + +## Code + +```javascript +import { unstable_useMemoCache as useMemoCache } from "react"; +import { invoke } from "shared-runtime"; + +function Component(t21) { + const $ = useMemoCache(2); + const { shouldReassign } = t21; + let x; + if ($[0] !== shouldReassign) { + x = null; + const reassign = () => { + if (shouldReassign) { + x = 2; + } + }; + + invoke(reassign); + $[0] = shouldReassign; + $[1] = x; + } else { + x = $[1]; + } + return x; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ shouldReassign: true }], + sequentialRenders: [{ shouldReassign: false }, { shouldReassign: true }], +}; + +``` + +### Eval output +(kind: ok) null +2 \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reactive-explicit-control-flow.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reactive-explicit-control-flow.js new file mode 100644 index 0000000000..7eb0235d8d --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reactive-explicit-control-flow.js @@ -0,0 +1,18 @@ +import { invoke } from "shared-runtime"; + +function Component({ shouldReassign }) { + let x = null; + const reassign = () => { + if (shouldReassign) { + x = 2; + } + }; + invoke(reassign); + return x; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ shouldReassign: true }], + sequentialRenders: [{ shouldReassign: false }, { shouldReassign: true }], +}; diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reactive-implicit-control-flow.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reactive-implicit-control-flow.expect.md new file mode 100644 index 0000000000..77fe2dc28f --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reactive-implicit-control-flow.expect.md @@ -0,0 +1,65 @@ + +## Input + +```javascript +import { conditionalInvoke } from "shared-runtime"; + +// same as context-variable-reactive-explicit-control-flow.js, but make +// the control flow implicit + +function Component({ shouldReassign }) { + let x = null; + const reassign = () => { + x = 2; + }; + conditionalInvoke(shouldReassign, reassign); + return x; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ shouldReassign: true }], + sequentialRenders: [{ shouldReassign: false }, { shouldReassign: true }], +}; + +``` + +## Code + +```javascript +import { unstable_useMemoCache as useMemoCache } from "react"; +import { conditionalInvoke } from "shared-runtime"; + +// same as context-variable-reactive-explicit-control-flow.js, but make +// the control flow implicit + +function Component(t20) { + const $ = useMemoCache(2); + const { shouldReassign } = t20; + let x; + if ($[0] !== shouldReassign) { + x = null; + const reassign = () => { + x = 2; + }; + + conditionalInvoke(shouldReassign, reassign); + $[0] = shouldReassign; + $[1] = x; + } else { + x = $[1]; + } + return x; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ shouldReassign: true }], + sequentialRenders: [{ shouldReassign: false }, { shouldReassign: true }], +}; + +``` + +### Eval output +(kind: ok) null +2 \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reactive-implicit-control-flow.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reactive-implicit-control-flow.js new file mode 100644 index 0000000000..9359315ad5 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reactive-implicit-control-flow.js @@ -0,0 +1,19 @@ +import { conditionalInvoke } from "shared-runtime"; + +// same as context-variable-reactive-explicit-control-flow.js, but make +// the control flow implicit + +function Component({ shouldReassign }) { + let x = null; + const reassign = () => { + x = 2; + }; + conditionalInvoke(shouldReassign, reassign); + return x; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ shouldReassign: true }], + sequentialRenders: [{ shouldReassign: false }, { shouldReassign: true }], +}; diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-outside-of-lambda.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-outside-of-lambda.expect.md index f429e29f1f..d5a7734709 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-outside-of-lambda.expect.md +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-outside-of-lambda.expect.md @@ -2,40 +2,48 @@ ## Input ```javascript -// @debug +import { Stringify } from "shared-runtime"; + function Component(props) { let x = null; - const onChange = (e) => { + const callback = () => { console.log(x); }; x = {}; - return ; + return ; } +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{}], +}; + ``` ## Code ```javascript -import { unstable_useMemoCache as useMemoCache } from "react"; // @debug +import { unstable_useMemoCache as useMemoCache } from "react"; +import { Stringify } from "shared-runtime"; + function Component(props) { const $ = useMemoCache(2); - let onChange; + let callback; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { let x; x = null; - onChange = (e) => { + callback = () => { console.log(x); }; x = {}; - $[0] = onChange; + $[0] = callback; } else { - onChange = $[0]; + callback = $[0]; } let t0; if ($[1] === Symbol.for("react.memo_cache_sentinel")) { - t0 = ; + t0 = ; $[1] = t0; } else { t0 = $[1]; @@ -43,5 +51,13 @@ function Component(props) { return t0; } +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{}], +}; + ``` - \ No newline at end of file + +### Eval output +(kind: ok)
{"callback":{"kind":"Function"},"shouldInvokeFns":true}
+logs: [{}] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-outside-of-lambda.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-outside-of-lambda.js index 2624dc562f..fa80497c31 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-outside-of-lambda.js +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-outside-of-lambda.js @@ -1,9 +1,15 @@ -// @debug +import { Stringify } from "shared-runtime"; + function Component(props) { let x = null; - const onChange = (e) => { + const callback = () => { console.log(x); }; x = {}; - return ; + return ; } + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{}], +}; diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-reactive-capture.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-reactive-capture.expect.md new file mode 100644 index 0000000000..9bf4d59fc4 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-reactive-capture.expect.md @@ -0,0 +1,59 @@ + +## Input + +```javascript +import { invoke } from "shared-runtime"; + +function Component({ value }) { + let x = null; + const reassign = () => { + x = value; + }; + invoke(reassign); + return x; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ value: 2 }], + sequentialRenders: [{ value: 2 }, { value: 4 }], +}; + +``` + +## Code + +```javascript +import { unstable_useMemoCache as useMemoCache } from "react"; +import { invoke } from "shared-runtime"; + +function Component(t20) { + const $ = useMemoCache(2); + const { value } = t20; + let x; + if ($[0] !== value) { + x = null; + const reassign = () => { + x = value; + }; + + invoke(reassign); + $[0] = value; + $[1] = x; + } else { + x = $[1]; + } + return x; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ value: 2 }], + sequentialRenders: [{ value: 2 }, { value: 4 }], +}; + +``` + +### Eval output +(kind: ok) 2 +4 \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-reactive-capture.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-reactive-capture.js new file mode 100644 index 0000000000..b3f7ecad39 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-reactive-capture.js @@ -0,0 +1,16 @@ +import { invoke } from "shared-runtime"; + +function Component({ value }) { + let x = null; + const reassign = () => { + x = value; + }; + invoke(reassign); + return x; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ value: 2 }], + sequentialRenders: [{ value: 2 }, { value: 4 }], +}; diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-two-lambdas.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-two-lambdas.expect.md new file mode 100644 index 0000000000..e76b34b4bf --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-two-lambdas.expect.md @@ -0,0 +1,78 @@ + +## Input + +```javascript +import { conditionalInvoke } from "shared-runtime"; + +function Component({ doReassign1, doReassign2 }) { + let x = {}; + const reassign1 = () => { + x = 2; + }; + const reassign2 = () => { + x = 3; + }; + conditionalInvoke(doReassign1, reassign1); + conditionalInvoke(doReassign2, reassign2); + return x; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ doReassign1: true, doReassign2: true }], + sequentialRenders: [ + { doReassign1: true, doReassign2: true }, + { doReassign1: true, doReassign2: false }, + { doReassign1: false, doReassign2: false }, + ], +}; + +``` + +## Code + +```javascript +import { unstable_useMemoCache as useMemoCache } from "react"; +import { conditionalInvoke } from "shared-runtime"; + +function Component(t32) { + const $ = useMemoCache(3); + const { doReassign1, doReassign2 } = t32; + let x; + if ($[0] !== doReassign1 || $[1] !== doReassign2) { + x = {}; + const reassign1 = () => { + x = 2; + }; + + const reassign2 = () => { + x = 3; + }; + + conditionalInvoke(doReassign1, reassign1); + conditionalInvoke(doReassign2, reassign2); + $[0] = doReassign1; + $[1] = doReassign2; + $[2] = x; + } else { + x = $[2]; + } + return x; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ doReassign1: true, doReassign2: true }], + sequentialRenders: [ + { doReassign1: true, doReassign2: true }, + { doReassign1: true, doReassign2: false }, + { doReassign1: false, doReassign2: false }, + ], +}; + +``` + +### Eval output +(kind: ok) 3 +2 +{} \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-two-lambdas.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-two-lambdas.js new file mode 100644 index 0000000000..4896ce91af --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/context-variable-reassigned-two-lambdas.js @@ -0,0 +1,24 @@ +import { conditionalInvoke } from "shared-runtime"; + +function Component({ doReassign1, doReassign2 }) { + let x = {}; + const reassign1 = () => { + x = 2; + }; + const reassign2 = () => { + x = 3; + }; + conditionalInvoke(doReassign1, reassign1); + conditionalInvoke(doReassign2, reassign2); + return x; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ doReassign1: true, doReassign2: true }], + sequentialRenders: [ + { doReassign1: true, doReassign2: true }, + { doReassign1: true, doReassign2: false }, + { doReassign1: false, doReassign2: false }, + ], +}; diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/function-declaration-reassign.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/function-declaration-reassign.expect.md index 7015f03687..cddbe6117b 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/function-declaration-reassign.expect.md +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/function-declaration-reassign.expect.md @@ -25,16 +25,14 @@ import { unstable_useMemoCache as useMemoCache } from "react"; function component() { const $ = useMemoCache(1); let x; + let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - x = function x(a) { - a.foo(); - }; - - x = {}; - $[0] = x; + t0 = {}; + $[0] = t0; } else { - x = $[0]; + t0 = $[0]; } + x = t0; return x; } diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reassign-object-in-context.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reassign-object-in-context.expect.md index db9ae60279..6df2d9edff 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reassign-object-in-context.expect.md +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reassign-object-in-context.expect.md @@ -13,8 +13,7 @@ function Component(props) { export const FIXTURE_ENTRYPOINT = { fn: Component, - params: ["TodoAdd"], - isComponent: "TodoAdd", + params: [{}], }; ``` @@ -42,8 +41,7 @@ function Component(props) { export const FIXTURE_ENTRYPOINT = { fn: Component, - params: ["TodoAdd"], - isComponent: "TodoAdd", + params: [{}], }; ``` diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reassign-object-in-context.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reassign-object-in-context.js index dead5a9159..2b4dbc7004 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reassign-object-in-context.js +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reassign-object-in-context.js @@ -9,6 +9,5 @@ function Component(props) { export const FIXTURE_ENTRYPOINT = { fn: Component, - params: ["TodoAdd"], - isComponent: "TodoAdd", + params: [{}], }; diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reassign-primitive-in-context.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reassign-primitive-in-context.expect.md index f522027137..73ecff0167 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reassign-primitive-in-context.expect.md +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reassign-primitive-in-context.expect.md @@ -13,8 +13,7 @@ function Component(props) { export const FIXTURE_ENTRYPOINT = { fn: Component, - params: ["TodoAdd"], - isComponent: "TodoAdd", + params: [{}], }; ``` @@ -42,9 +41,10 @@ function Component(props) { export const FIXTURE_ENTRYPOINT = { fn: Component, - params: ["TodoAdd"], - isComponent: "TodoAdd", + params: [{}], }; ``` - \ No newline at end of file + +### Eval output +(kind: ok) {} \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reassign-primitive-in-context.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reassign-primitive-in-context.js index 5236581713..d5c186005c 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reassign-primitive-in-context.js +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reassign-primitive-in-context.js @@ -9,6 +9,5 @@ function Component(props) { export const FIXTURE_ENTRYPOINT = { fn: Component, - params: ["TodoAdd"], - isComponent: "TodoAdd", + params: [{}], }; diff --git a/compiler/packages/sprout/src/SproutTodoFilter.ts b/compiler/packages/sprout/src/SproutTodoFilter.ts index 02cbe97542..3373f69b11 100644 --- a/compiler/packages/sprout/src/SproutTodoFilter.ts +++ b/compiler/packages/sprout/src/SproutTodoFilter.ts @@ -32,7 +32,6 @@ const skipFilter = new Set([ "capturing-func-mutate-nested", "capturing-func-mutate", "capturing-function-1", - "capturing-function-alias-computed-load-3", "capturing-function-alias-computed-load", "capturing-function-decl", "capturing-function-skip-computed-path", @@ -123,7 +122,6 @@ const skipFilter = new Set([ "reactive-scopes", "reactivity-analysis-interleaved-reactivity", "reassign-object-in-context", - "reassign-primitive-in-context", "reassignment-separate-scopes", "reduce-reactive-cond-memberexpr-join", "reduce-reactive-uncond-deps-nonoverlap-descendant", @@ -191,7 +189,6 @@ const skipFilter = new Set([ */ "alias-capture-in-method-receiver", "alias-nested-member-path-mutate", - "chained-assignment-context-variable", "concise-arrow-expr", "const-propagation-into-function-expression-global", "declare-reassign-variable-in-function-declaration", @@ -268,7 +265,6 @@ const skipFilter = new Set([ "computed-load-primitive-as-dependency", "computed-store-alias", "constant-propagation-into-function-expressions", - "context-variable-reassigned-outside-of-lambda", "destructuring-mixed-scope-declarations-and-locals", "destructuring-property-inference", "do-while-conditional-break", diff --git a/compiler/packages/sprout/src/shared-runtime.ts b/compiler/packages/sprout/src/shared-runtime.ts index 9ccba51bbb..c13c60fdba 100644 --- a/compiler/packages/sprout/src/shared-runtime.ts +++ b/compiler/packages/sprout/src/shared-runtime.ts @@ -127,8 +127,8 @@ export function makeObject_Primitives(): StringKeyedObject { return { a: 0, b: "value1", c: true }; } -export function makeArray(value: T): Array { - return [value]; +export function makeArray(...values: Array): Array { + return [...values]; } export function addOne(value: number): number { @@ -173,6 +173,18 @@ export function invoke, ReturnType>( return fn(...params); } +export function conditionalInvoke, ReturnType>( + shouldInvoke: boolean, + fn: (...input: T) => ReturnType, + ...params: T +) { + if (shouldInvoke) { + return fn(...params); + } else { + return null; + } +} + /** * React Components */