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(