diff --git a/compiler/packages/babel-plugin-react-forget/src/Entrypoint/Pipeline.ts b/compiler/packages/babel-plugin-react-forget/src/Entrypoint/Pipeline.ts index b6f64c7279..bf77ccba7c 100644 --- a/compiler/packages/babel-plugin-react-forget/src/Entrypoint/Pipeline.ts +++ b/compiler/packages/babel-plugin-react-forget/src/Entrypoint/Pipeline.ts @@ -49,6 +49,7 @@ import { mergeOverlappingReactiveScopes, mergeReactiveScopesThatInvalidateTogether, promoteUsedTemporaries, + propagateEarlyReturns, propagateScopeDependencies, pruneAllReactiveScopes, pruneHoistedContexts, @@ -283,7 +284,7 @@ function* runWithEnvironment( pruneNonEscapingScopes(reactiveFunction); yield log({ kind: "reactive", - name: "PruneNonEscapingDependencies", + name: "PruneNonEscapingScopes", value: reactiveFunction, }); @@ -294,6 +295,13 @@ function* runWithEnvironment( value: reactiveFunction, }); + propagateEarlyReturns(reactiveFunction); + yield log({ + kind: "reactive", + name: "PropagateEarlyReturns", + value: reactiveFunction, + }); + pruneUnusedScopes(reactiveFunction); yield log({ kind: "reactive", diff --git a/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/PropagateEarlyReturns.ts b/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/PropagateEarlyReturns.ts new file mode 100644 index 0000000000..b2a2d43e13 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/PropagateEarlyReturns.ts @@ -0,0 +1,120 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { visitReactiveFunction } from "."; +import { CompilerError } from ".."; +import { + ReactiveFunction, + ReactiveScopeBlock, + ReactiveTerminalStatement, +} from "../HIR"; +import { ReactiveFunctionVisitor } from "./visitors"; + +/** + * TODO: Actualy propagate early return information, for now we throw a Todo bailout. + * + * This pass ensures that reactive blocks honor the control flow behavior of the + * original code including early return semantics. Specifically, if a reactive + * scope early returned during the previous execution and the inputs to that block + * have not changed, then the code should early return (with the same value) again. + * + * Example: + * + * ```javascript + * let x = []; + * if (props.cond) { + * x.push(12); + * return x; + * } else { + * return foo(); + * } + * ``` + * + * Imagine that this code is called twice in a row with props.cond = true. Both + * times it should return the same object (===), an array `[12]`. + * + * The compilation strategy is as follows. For each top-level reactive scope + * that contains (transitively) an early return: + * + * - Label the scope + * - Synthesize a new temporary, eg `t0`, and set it as a declaration of the scope. + * This will represent the possibly-unset return value for that scope. + * - Make the first instruction of the scope a reassignment of that temporary, + * assigning a sentinel value (can reuse the same symbol as we use for cache slots). + * This assignment ensures that if we don't take an early return, that the value + * is the sentinel. + * - Replace all `return` statements with: + * - An assignment of the temporary with the value being returned. + * - An assignment of the temporary into a cache slot, so it can be retrieved in the + * scope's "else" branch. + * - A `break` to the reactive scope's label. + * - Finally, add code _after_ the reactive scope that checks the temporary. If + * it equals the sentinel value do nothing; else return its value. + * + * For the above example that looks roughly like: + * + * ```javascript + * let t0; // temporary for early return; + * bb1: if (props.cond !== $[0]) { + * // reset the temporary + * t0 = Symbol.for('react.forget'); + * // original code + * let x = []; + * if (props.cond) { + * x.push(12); + * // replace the early return w assignment and break + * t0 = x; + * $[2] = t0; + * break bb1 + * } else { + * let t1; + * if ($[1] === Symbol.for('react.forget')) { + * t1 = foo(); + * $[1] = t1; + * } else { + * t1 = $[1]; + * } + * // Replace early return w assignment and break; + * t0 = t1; + * $[2] = t0; + * break bb1; + * } + * } else { + * t0 = $[2]; + * } + * if (t0 !== Symbol.for('react.forget')) { + * return t0; + * } + * ``` + */ +export function propagateEarlyReturns(fn: ReactiveFunction): void { + visitReactiveFunction(fn, new Visitor(), false); +} + +class Visitor extends ReactiveFunctionVisitor { + override visitScope( + scopeBlock: ReactiveScopeBlock, + _withinReactiveScope: boolean + ): void { + this.traverseScope(scopeBlock, true); + } + + override visitTerminal( + stmt: ReactiveTerminalStatement, + withinReactiveScope: boolean + ): void { + if (withinReactiveScope && stmt.terminal.kind === "return") { + CompilerError.throwTodo({ + reason: `Support early return within a reactive scope`, + loc: stmt.terminal.value.loc, + description: null, + suggestions: null, + }); + } + this.traverseTerminal(stmt, withinReactiveScope); + } +} diff --git a/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/PruneNonEscapingScopes.ts b/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/PruneNonEscapingScopes.ts index 4dce513858..4aa83893fd 100644 --- a/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/PruneNonEscapingScopes.ts +++ b/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/PruneNonEscapingScopes.ts @@ -872,19 +872,35 @@ class PruneScopesTransform extends ReactiveFunctionTransform< Set > { override transformScope( - scope: ReactiveScopeBlock, + scopeBlock: ReactiveScopeBlock, state: Set ): Transformed { - this.visitScope(scope, state); + this.visitScope(scopeBlock, state); + + /** + * Scopes may initially appear "empty" because the value being memoized + * is early-returned from within the scope. For now we intentionaly keep + * these scopes, and let them get pruned later by PruneUnusedScopes + * _after_ handling the early-return case in PropagateEarlyReturns. + */ + if ( + scopeBlock.scope.declarations.size === 0 && + scopeBlock.scope.reassignments.size === 0 + ) { + return { kind: "keep" }; + } + const hasMemoizedOutput = - Array.from(scope.scope.declarations.keys()).some((id) => state.has(id)) || - Array.from(scope.scope.reassignments).some((identifier) => + Array.from(scopeBlock.scope.declarations.keys()).some((id) => + state.has(id) + ) || + Array.from(scopeBlock.scope.reassignments).some((identifier) => state.has(identifier.id) ); if (hasMemoizedOutput) { return { kind: "keep" }; } else { - return { kind: "replace-many", value: scope.instructions }; + return { kind: "replace-many", value: scopeBlock.instructions }; } } } diff --git a/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/index.ts b/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/index.ts index 91fb5c2d37..3d0fa5efa0 100644 --- a/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/index.ts +++ b/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/index.ts @@ -23,6 +23,7 @@ export { mergeOverlappingReactiveScopes } from "./MergeOverlappingReactiveScopes export { mergeReactiveScopesThatInvalidateTogether } from "./MergeReactiveScopesThatInvalidateTogether"; export { printReactiveFunction } from "./PrintReactiveFunction"; export { promoteUsedTemporaries } from "./PromoteUsedTemporaries"; +export { propagateEarlyReturns } from "./PropagateEarlyReturns"; export { propagateScopeDependencies } from "./PropagateScopeDependencies"; export { pruneAllReactiveScopes } from "./PruneAllReactiveScopes"; export { pruneHoistedContexts } from "./PruneHoistedContexts"; diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/conditional-break.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/conditional-break.expect.md deleted file mode 100644 index 76fee65043..0000000000 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/conditional-break.expect.md +++ /dev/null @@ -1,158 +0,0 @@ - -## Input - -```javascript -/** - * props.b does *not* influence `a` - */ -function ComponentA(props) { - const a_DEBUG = []; - a_DEBUG.push(props.a); - if (props.b) { - return null; - } - a_DEBUG.push(props.d); - return a_DEBUG; -} - -/** - * props.b *does* influence `a` - */ -function ComponentB(props) { - const a = []; - a.push(props.a); - if (props.b) { - a.push(props.c); - } - a.push(props.d); - return a; -} - -/** - * props.b *does* influence `a`, but only in a way that is never observable - */ -function ComponentC(props) { - const a = []; - a.push(props.a); - if (props.b) { - a.push(props.c); - return null; - } - a.push(props.d); - return a; -} - -/** - * props.b *does* influence `a` - */ -function ComponentD(props) { - const a = []; - a.push(props.a); - if (props.b) { - a.push(props.c); - return a; - } - a.push(props.d); - return a; -} - -``` - -## Code - -```javascript -import { unstable_useMemoCache as useMemoCache } from "react"; -/** - * props.b does *not* influence `a` - */ -function ComponentA(props) { - const $ = useMemoCache(4); - let a_DEBUG; - if ($[0] !== props.a || $[1] !== props.b || $[2] !== props.d) { - a_DEBUG = []; - a_DEBUG.push(props.a); - if (props.b) { - return null; - } - - a_DEBUG.push(props.d); - $[0] = props.a; - $[1] = props.b; - $[2] = props.d; - $[3] = a_DEBUG; - } else { - a_DEBUG = $[3]; - } - return a_DEBUG; -} - -/** - * props.b *does* influence `a` - */ -function ComponentB(props) { - const $ = useMemoCache(2); - let a; - if ($[0] !== props) { - a = []; - a.push(props.a); - if (props.b) { - a.push(props.c); - } - - a.push(props.d); - $[0] = props; - $[1] = a; - } else { - a = $[1]; - } - return a; -} - -/** - * props.b *does* influence `a`, but only in a way that is never observable - */ -function ComponentC(props) { - const $ = useMemoCache(2); - let a; - if ($[0] !== props) { - a = []; - a.push(props.a); - if (props.b) { - a.push(props.c); - return null; - } - - a.push(props.d); - $[0] = props; - $[1] = a; - } else { - a = $[1]; - } - return a; -} - -/** - * props.b *does* influence `a` - */ -function ComponentD(props) { - const $ = useMemoCache(2); - let a; - if ($[0] !== props) { - a = []; - a.push(props.a); - if (props.b) { - a.push(props.c); - return a; - } - - a.push(props.d); - $[0] = props; - $[1] = a; - } else { - a = $[1]; - } - return a; -} - -``` - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--conditional-break.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--conditional-break.expect.md new file mode 100644 index 0000000000..d75dac1d28 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--conditional-break.expect.md @@ -0,0 +1,68 @@ + +## Input + +```javascript +/** + * props.b does *not* influence `a` + */ +function ComponentA(props) { + const a_DEBUG = []; + a_DEBUG.push(props.a); + if (props.b) { + return null; + } + a_DEBUG.push(props.d); + return a_DEBUG; +} + +/** + * props.b *does* influence `a` + */ +function ComponentB(props) { + const a = []; + a.push(props.a); + if (props.b) { + a.push(props.c); + } + a.push(props.d); + return a; +} + +/** + * props.b *does* influence `a`, but only in a way that is never observable + */ +function ComponentC(props) { + const a = []; + a.push(props.a); + if (props.b) { + a.push(props.c); + return null; + } + a.push(props.d); + return a; +} + +/** + * props.b *does* influence `a` + */ +function ComponentD(props) { + const a = []; + a.push(props.a); + if (props.b) { + a.push(props.c); + return a; + } + a.push(props.d); + return a; +} + +``` + + +## Error + +``` +[ReactForget] Todo: Support early return within a reactive scope (8:8) +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/conditional-break.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--conditional-break.js similarity index 100% rename from compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/conditional-break.js rename to compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--conditional-break.js diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--early-return-nested-early-return-within-reactive-scope.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--early-return-nested-early-return-within-reactive-scope.expect.md new file mode 100644 index 0000000000..8a7d2bb558 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--early-return-nested-early-return-within-reactive-scope.expect.md @@ -0,0 +1,36 @@ + +## Input + +```javascript +function Component(props) { + let x = []; + if (props.cond) { + x.push(props.a); + if (props.b) { + const y = [props.b]; + x.push(y); + // oops no memo! + return x; + } + // oops no memo! + return x; + } else { + return foo(); + } +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ cond: true, a: 42, b: 3.14 }], +}; + +``` + + +## Error + +``` +[ReactForget] Todo: Support early return within a reactive scope (9:9) +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--early-return-nested-early-return-within-reactive-scope.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--early-return-nested-early-return-within-reactive-scope.js new file mode 100644 index 0000000000..9f5b357bf9 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--early-return-nested-early-return-within-reactive-scope.js @@ -0,0 +1,21 @@ +function Component(props) { + let x = []; + if (props.cond) { + x.push(props.a); + if (props.b) { + const y = [props.b]; + x.push(y); + // oops no memo! + return x; + } + // oops no memo! + return x; + } else { + return foo(); + } +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ cond: true, a: 42, b: 3.14 }], +}; diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--early-return-within-reactive-scope.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--early-return-within-reactive-scope.expect.md new file mode 100644 index 0000000000..b38723ae01 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--early-return-within-reactive-scope.expect.md @@ -0,0 +1,30 @@ + +## Input + +```javascript +function Component(props) { + let x = []; + if (props.cond) { + x.push(props.a); + // oops no memo! + return x; + } else { + return foo(); + } +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ cond: true, a: 42 }], +}; + +``` + + +## Error + +``` +[ReactForget] Todo: Support early return within a reactive scope (6:6) +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--early-return-within-reactive-scope.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--early-return-within-reactive-scope.js new file mode 100644 index 0000000000..36d690edbb --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--early-return-within-reactive-scope.js @@ -0,0 +1,15 @@ +function Component(props) { + let x = []; + if (props.cond) { + x.push(props.a); + // oops no memo! + return x; + } else { + return foo(); + } +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ cond: true, a: 42 }], +}; diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--partial-early-return-within-reactive-scope.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--partial-early-return-within-reactive-scope.expect.md new file mode 100644 index 0000000000..81af828cbd --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--partial-early-return-within-reactive-scope.expect.md @@ -0,0 +1,35 @@ + +## Input + +```javascript +function Component(props) { + let x = []; + let y = null; + if (props.cond) { + x.push(props.a); + // oops no memo! + return x; + } else { + y = foo(); + if (props.b) { + return; + } + } + return y; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ cond: true, a: 42 }], +}; + +``` + + +## Error + +``` +[ReactForget] Todo: Support early return within a reactive scope (7:7) +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--partial-early-return-within-reactive-scope.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--partial-early-return-within-reactive-scope.js new file mode 100644 index 0000000000..9739a2a8de --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--partial-early-return-within-reactive-scope.js @@ -0,0 +1,20 @@ +function Component(props) { + let x = []; + let y = null; + if (props.cond) { + x.push(props.a); + // oops no memo! + return x; + } else { + y = foo(); + if (props.b) { + return; + } + } + return y; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ cond: true, a: 42 }], +}; diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--try-catch-try-value-modified-in-catch.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--try-catch-try-value-modified-in-catch.expect.md new file mode 100644 index 0000000000..3204c28e18 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--try-catch-try-value-modified-in-catch.expect.md @@ -0,0 +1,33 @@ + +## Input + +```javascript +const { throwInput } = require("shared-runtime"); + +function Component(props) { + try { + const y = []; + y.push(props.y); + throwInput(y); + } catch (e) { + e.push(props.e); + return e; + } + return null; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ y: "foo", e: "bar" }], +}; + +``` + + +## Error + +``` +[ReactForget] Todo: Support early return within a reactive scope (10:10) +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-try-value-modified-in-catch.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--try-catch-try-value-modified-in-catch.js similarity index 100% rename from compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-try-value-modified-in-catch.js rename to compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--try-catch-try-value-modified-in-catch.js diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-with-catch-param.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--try-catch-with-catch-param.expect.md similarity index 52% rename from compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-with-catch-param.expect.md rename to compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--try-catch-with-catch-param.expect.md index 2e1bad8643..401ae1961b 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-with-catch-param.expect.md +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--try-catch-with-catch-param.expect.md @@ -24,30 +24,11 @@ export const FIXTURE_ENTRYPOINT = { ``` -## Code -```javascript -const { throwInput } = require("shared-runtime"); - -function Component(props) { - const x = []; - try { - throwInput(x); - } catch (t22) { - const e = t22; - - e.push(null); - return e; - } - return x; -} - -export const FIXTURE_ENTRYPOINT = { - fn: Component, - params: [{}], -}; +## Error ``` - -### Eval output -(kind: ok) [null] \ No newline at end of file +[ReactForget] Todo: Support early return within a reactive scope (11:11) +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-with-catch-param.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--try-catch-with-catch-param.js similarity index 100% rename from compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-with-catch-param.js rename to compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--try-catch-with-catch-param.js diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--try-catch-with-return.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--try-catch-with-return.expect.md new file mode 100644 index 0000000000..adf87c123f --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--try-catch-with-return.expect.md @@ -0,0 +1,36 @@ + +## Input + +```javascript +const { shallowCopy, throwInput } = require("shared-runtime"); + +// @debug +function Component(props) { + let x = []; + try { + const y = shallowCopy({}); + if (y == null) { + return; + } + x.push(throwInput(y)); + } catch { + return null; + } + return x; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{}], +}; + +``` + + +## Error + +``` +[ReactForget] Todo: Support early return within a reactive scope +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-with-return.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--try-catch-with-return.js similarity index 100% rename from compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-with-return.js rename to compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-early-return--try-catch-with-return.js diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-try-value-modified-in-catch.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-try-value-modified-in-catch.expect.md deleted file mode 100644 index a0360ae972..0000000000 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-try-value-modified-in-catch.expect.md +++ /dev/null @@ -1,52 +0,0 @@ - -## Input - -```javascript -const { throwInput } = require("shared-runtime"); - -function Component(props) { - try { - const y = []; - y.push(props.y); - throwInput(y); - } catch (e) { - e.push(props.e); - return e; - } - return null; -} - -export const FIXTURE_ENTRYPOINT = { - fn: Component, - params: [{ y: "foo", e: "bar" }], -}; - -``` - -## Code - -```javascript -const { throwInput } = require("shared-runtime"); - -function Component(props) { - try { - const y = []; - y.push(props.y); - throwInput(y); - } catch (t25) { - const e = t25; - e.push(props.e); - return e; - } - return null; -} - -export const FIXTURE_ENTRYPOINT = { - fn: Component, - params: [{ y: "foo", e: "bar" }], -}; - -``` - -### Eval output -(kind: ok) ["foo","bar"] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-with-return.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-with-return.expect.md deleted file mode 100644 index 34489e339b..0000000000 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-with-return.expect.md +++ /dev/null @@ -1,66 +0,0 @@ - -## Input - -```javascript -const { shallowCopy, throwInput } = require("shared-runtime"); - -// @debug -function Component(props) { - let x = []; - try { - const y = shallowCopy({}); - if (y == null) { - return; - } - x.push(throwInput(y)); - } catch { - return null; - } - return x; -} - -export const FIXTURE_ENTRYPOINT = { - fn: Component, - params: [{}], -}; - -``` - -## Code - -```javascript -import { unstable_useMemoCache as useMemoCache } from "react"; -const { shallowCopy, throwInput } = require("shared-runtime"); - -// @debug -function Component(props) { - const $ = useMemoCache(1); - let x; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - x = []; - try { - const y = shallowCopy({}); - if (y == null) { - return; - } - - x.push(throwInput(y)); - } catch { - return null; - } - $[0] = x; - } else { - x = $[0]; - } - return x; -} - -export const FIXTURE_ENTRYPOINT = { - fn: Component, - params: [{}], -}; - -``` - -### Eval output -(kind: ok) null \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/validate-no-set-state-in-render-uncalled-function-with-mutable-range-is-valid.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/validate-no-set-state-in-render-uncalled-function-with-mutable-range-is-valid.expect.md index 2f7200016f..6fce8dfa1c 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/validate-no-set-state-in-render-uncalled-function-with-mutable-range-is-valid.expect.md +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/validate-no-set-state-in-render-uncalled-function-with-mutable-range-is-valid.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoSetStateInRender +// @validateNoSetStateInRender @enableAssumeHooksFollowRulesOfReact function Component(props) { const logEvent = useLogging(props.appId); const [currentStep, setCurrentStep] = useState(0); @@ -33,47 +33,62 @@ function Component(props) { ## Code ```javascript -import { unstable_useMemoCache as useMemoCache } from "react"; // @validateNoSetStateInRender +import { unstable_useMemoCache as useMemoCache } from "react"; // @validateNoSetStateInRender @enableAssumeHooksFollowRulesOfReact function Component(props) { - const $ = useMemoCache(3); + const $ = useMemoCache(7); const logEvent = useLogging(props.appId); const [currentStep, setCurrentStep] = useState(0); - - const onSubmit = (errorEvent) => { - logEvent(errorEvent); - setCurrentStep(1); - }; + let t0; + if ($[0] !== logEvent) { + t0 = (errorEvent) => { + logEvent(errorEvent); + setCurrentStep(1); + }; + $[0] = logEvent; + $[1] = t0; + } else { + t0 = $[1]; + } + const onSubmit = t0; switch (currentStep) { case 0: { - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = ; - $[0] = t0; + let t1; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t1 = ; + $[2] = t1; } else { - t0 = $[0]; + t1 = $[2]; } - return t0; + return t1; } case 1: { - let t1; - if ($[1] === Symbol.for("react.memo_cache_sentinel")) { - t1 = { foo: "joe" }; - $[1] = t1; + let t2; + if ($[3] === Symbol.for("react.memo_cache_sentinel")) { + t2 = { foo: "joe" }; + $[3] = t2; } else { - t1 = $[1]; + t2 = $[3]; } - return ; + let t3; + if ($[4] !== onSubmit) { + t3 = ; + $[4] = onSubmit; + $[5] = t3; + } else { + t3 = $[5]; + } + return t3; } default: { logEvent("Invalid step"); - let t2; - if ($[2] === Symbol.for("react.memo_cache_sentinel")) { - t2 = ; - $[2] = t2; + let t4; + if ($[6] === Symbol.for("react.memo_cache_sentinel")) { + t4 = ; + $[6] = t4; } else { - t2 = $[2]; + t4 = $[6]; } - return t2; + return t4; } } } diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/validate-no-set-state-in-render-uncalled-function-with-mutable-range-is-valid.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/validate-no-set-state-in-render-uncalled-function-with-mutable-range-is-valid.js index 50b5d058c3..e3404f916d 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/validate-no-set-state-in-render-uncalled-function-with-mutable-range-is-valid.js +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/validate-no-set-state-in-render-uncalled-function-with-mutable-range-is-valid.js @@ -1,4 +1,4 @@ -// @validateNoSetStateInRender +// @validateNoSetStateInRender @enableAssumeHooksFollowRulesOfReact function Component(props) { const logEvent = useLogging(props.appId); const [currentStep, setCurrentStep] = useState(0);