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

Scaffolding for early return from reactive scopes

Adds a new `earlyReturnValue` property on ReactiveScope which will be set if the scope had one or more early returns, with information about the temporary identifier that the early return value will be assigned to, as well as the label to be used for breaking (to simulate the early-return). The next PR shows the intended codegen.

Joe Savona committed Dec 20, 2023 at 13:52 UTC ed9d6a2ca163422efd703a85511774d196aebf1e
4 files changed +66 -16
compiler/packages/babel-plugin-react-forget/src/HIR/HIR.ts
+25
@@ -1053,10 +1053,35 @@ export function isMutableEffect(
1053 export type ReactiveScope = {
1054 id: ScopeId;
1055 range: MutableRange;
1056 +
1057 + /**
1058 + * The inputs to this reactive scope
1059 + */
1060 dependencies: ReactiveScopeDependencies;
1061 +
1062 + /**
1063 + * The set of values produced by this scope. This may be empty
1064 + * for scopes that produce reassignments only.
1065 + */
1066 declarations: Map<IdentifierId, ReactiveScopeDeclaration>;
1067 +
1068 + /**
1069 + * A mutable range may sometimes include a reassignment of some variable.
1070 + * This is the set of identifiers which are reassigned by this scope.
1071 + */
1072 reassignments: Set<Identifier>;
1073
1074 + /**
1075 + * Reactive scopes may contain a return statement, which needs to be replayed
1076 + * whenever the inputs to the scope have not changed since the previous execution.
1077 + * If the reactive scope has an early return, this variable stores the temporary
1078 + * identifier to which the return value will be assigned. See PropagateEarlyReturns
1079 + * for more about how early returns in reactive scopes are compiled and represented.
1080 + *
1081 + * This value is null for scopes that do not contain early returns.
1082 + */
1083 + earlyReturnValue: { value: IdentifierId; loc: SourceLocation } | null;
1084 +
1085 /*
1086 * Some passes may merge scopes together. The merged set contains the
1087 * ids of scopes that were merged into this one, for passes that need
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+14 -5
@@ -451,11 +451,20 @@ function codegenReactiveScope(
451 const computationBlock = codegenBlock(cx, block);
452 computationBlock.body.push(...cacheStoreStatements);
453 const memoBlock = t.blockStatement(cacheLoadStatements);
454 - const memoStatement = t.ifStatement(
455 - testCondition,
456 - computationBlock,
457 - memoBlock
458 - );
454 +
455 + let memoStatement;
456 + if (scope.earlyReturnValue !== null) {
457 + // Has early return
458 + CompilerError.throwTodo({
459 + reason: `Codegen support for reactive scopes with early return`,
460 + loc: scope.earlyReturnValue.loc,
461 + description: null,
462 + suggestions: null,
463 + });
464 + } else {
465 + memoStatement = t.ifStatement(testCondition, computationBlock, memoBlock);
466 + }
467 +
468 if (cx.env.config.enableMemoizationComments) {
469 if (changeExpressionComments.length) {
470 t.addComment(
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/InferReactiveScopeVariables.ts
+1
@@ -199,6 +199,7 @@ export function inferReactiveScopeVariables(fn: HIRFunction): void {
199 dependencies: new Set(),
200 declarations: new Map(),
201 reassignments: new Set(),
202 + earlyReturnValue: null,
203 merged: new Set(),
204 };
205 scopes.set(groupIdentifier, scope);
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/PrintReactiveFunction.ts
+26 -11
@@ -42,17 +42,32 @@ export function printReactiveFunction(fn: ReactiveFunction): string {
42 }
43
44 export function printReactiveScopeSummary(scope: ReactiveScope): string {
45 - return `scope @${scope.id} [${scope.range.start}:${
46 - scope.range.end
47 - }] dependencies=[${Array.from(scope.dependencies)
48 - .map((dep) => printDependency(dep))
49 - .join(", ")}] declarations=[${Array.from(scope.declarations)
50 - .map(([, decl]) =>
51 - printIdentifier({ ...decl.identifier, scope: decl.scope })
52 - )
53 - .join(", ")}] reassignments=[${Array.from(scope.reassignments).map(
54 - (reassign) => printIdentifier(reassign)
55 - )}]`;
45 + const items = [];
46 + // If the scope has a return value it needs a label
47 + items.push("scope");
48 + items.push(`@${scope.id}`);
49 + items.push(`[${scope.range.start}:${scope.range.end}]`);
50 + items.push(
51 + `dependencies=[${Array.from(scope.dependencies)
52 + .map((dep) => printDependency(dep))
53 + .join(", ")}]`
54 + );
55 + items.push(
56 + `declarations=[${Array.from(scope.declarations)
57 + .map(([, decl]) =>
58 + printIdentifier({ ...decl.identifier, scope: decl.scope })
59 + )
60 + .join(", ")}]`
61 + );
62 + items.push(
63 + `reassignments=[${Array.from(scope.reassignments).map((reassign) =>
64 + printIdentifier(reassign)
65 + )}]`
66 + );
67 + if (scope.earlyReturnValue !== null) {
68 + items.push(`earlyReturn=${scope.earlyReturnValue.value}`);
69 + }
70 + return items.join(" ");
71 }
72
73 export function writeReactiveBlock(