Move useMemoCache outside of hook guards
The hook guards are incompatible with using a forget-runtime. Specifically, forget-runtime needs to make a call to `useState()` or some other hook to attach data to the fiber, but all the builtin hooks are overridden to disallow calling them outside of explicit boundaries. We'd either have to wrap the useMemoCache call in a push/pop to allow it to call other hooks, or as in this PR, just move it outside the enforcement.
Joe Savona committed
Feb 13, 2024 at 16:45 UTC
fea7b5ac0d04d9749acf3fe1f0134cfb56a1648e
2 files changed
+47
-39
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+46
-38
@@ -74,12 +74,40 @@ export type CodegenFunction = {
74
export function codegenFunction(
75
fn: ReactiveFunction
76
): Result<CodegenFunction, CompilerError> {
77
- const compileResult = codegenReactiveFunction(fn);
77
+ const cx = new Context(fn.env, fn.id ?? "[[ anonymous ]]", null);
78
+ const compileResult = codegenReactiveFunction(cx, fn);
79
if (compileResult.isErr()) {
80
return compileResult;
81
}
82
const compiled = compileResult.unwrap();
83
84
+ const hookGuard = fn.env.config.enableEmitHookGuards;
85
+ if (hookGuard != null) {
86
+ compiled.body = t.blockStatement([
87
+ createHookGuard(
88
+ hookGuard,
89
+ compiled.body.body,
90
+ GuardKind.PushHookGuard,
91
+ GuardKind.PopHookGuard
92
+ ),
93
+ ]);
94
+ }
95
+
96
+ const cacheCount = compiled.memoSlotsUsed;
97
+ if (cacheCount !== 0) {
98
+ // The import declaration for `useMemoCache` is inserted in the Babel plugin
99
+ compiled.body.body.unshift(
100
+ t.variableDeclaration("const", [
101
+ t.variableDeclarator(
102
+ t.identifier("$"),
103
+ t.callExpression(t.identifier("useMemoCache"), [
104
+ t.numericLiteral(cacheCount),
105
+ ])
106
+ ),
107
+ ])
108
+ );
109
+ }
110
+
111
const emitInstrumentForget = fn.env.config.enableEmitInstrumentForget;
112
if (emitInstrumentForget != null && fn.id != null) {
113
/*
@@ -98,29 +126,13 @@ export function codegenFunction(
126
compiled.body.body.unshift(test);
127
}
128
101
- const hookGuard = fn.env.config.enableEmitHookGuards;
102
- if (hookGuard != null) {
103
- compiled.body = t.blockStatement([
104
- createHookGuard(
105
- hookGuard,
106
- compiled.body.body,
107
- GuardKind.PushHookGuard,
108
- GuardKind.PopHookGuard
109
- ),
110
- ]);
111
- }
129
return compileResult;
130
}
131
115
-export function codegenReactiveFunction(
116
- fn: ReactiveFunction,
117
- parentContext: Context | null = null
132
+function codegenReactiveFunction(
133
+ cx: Context,
134
+ fn: ReactiveFunction
135
): Result<CodegenFunction, CompilerError> {
119
- const cx = new Context(
120
- fn.env,
121
- fn.id ?? "[[ anonymous ]]",
122
- parentContext?.temp ?? null
123
- );
136
for (const param of fn.params) {
137
if (param.kind === "Identifier") {
138
cx.temp.set(param.identifier.id, null);
@@ -130,7 +142,7 @@ export function codegenReactiveFunction(
142
}
143
144
const params = fn.params.map((param) => convertParameter(param));
133
- const body = codegenBlock(cx, fn.body);
145
+ const body: t.BlockStatement = codegenBlock(cx, fn.body);
146
const statements = body.body;
147
if (statements.length !== 0) {
148
const last = statements[statements.length - 1];
@@ -138,20 +150,6 @@ export function codegenReactiveFunction(
150
statements.pop();
151
}
152
}
141
- const cacheCount = cx.nextCacheIndex;
142
- if (cacheCount !== 0) {
143
- // The import declaration for `useMemoCache` is inserted in the Babel plugin
144
- statements.unshift(
145
- t.variableDeclaration("const", [
146
- t.variableDeclarator(
147
- t.identifier("$"),
148
- t.callExpression(t.identifier("useMemoCache"), [
149
- t.numericLiteral(cacheCount),
150
- ])
151
- ),
152
- ])
153
- );
154
- }
153
154
if (cx.errors.hasErrors()) {
155
return Err(cx.errors);
@@ -168,7 +166,7 @@ export function codegenReactiveFunction(
166
body,
167
generator: fn.generator,
168
async: fn.async,
171
- memoSlotsUsed: cacheCount,
169
+ memoSlotsUsed: cx.nextCacheIndex,
170
memoBlocks: countMemoBlockVisitor.count,
171
});
172
}
@@ -1321,7 +1319,14 @@ function codegenInstructionValue(
1319
pruneUnusedLabels(reactiveFunction);
1320
pruneUnusedLValues(reactiveFunction);
1321
renameVariables(reactiveFunction);
1324
- const fn = codegenReactiveFunction(reactiveFunction).unwrap();
1322
+ const fn = codegenReactiveFunction(
1323
+ new Context(
1324
+ cx.env,
1325
+ reactiveFunction.id ?? "[[ anonymous ]]",
1326
+ cx.temp
1327
+ ),
1328
+ reactiveFunction
1329
+ ).unwrap();
1330
1331
/*
1332
* ObjectMethod builder must be backwards compatible with older versions of babel.
@@ -1520,7 +1525,10 @@ function codegenInstructionValue(
1525
pruneUnusedLValues(reactiveFunction);
1526
renameVariables(reactiveFunction);
1527
pruneHoistedContexts(reactiveFunction);
1523
- const fn = codegenReactiveFunction(reactiveFunction, cx).unwrap();
1528
+ const fn = codegenReactiveFunction(
1529
+ new Context(cx.env, reactiveFunction.id ?? "[[ anonymous ]]", cx.temp),
1530
+ reactiveFunction
1531
+ ).unwrap();
1532
if (instrValue.expr.type === "ArrowFunctionExpression") {
1533
let body: t.BlockStatement | t.Expression = fn.body;
1534
if (body.body.length === 1) {
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/flag-enable-emit-hook-guards.expect.md
+1
-1
@@ -54,9 +54,9 @@ import {
54
55
const MyContext = createContext("my context value");
56
function Component(t47) {
57
+ const $ = useMemoCache(4);
58
try {
59
$dispatcherGuard(0);
59
- const $ = useMemoCache(4);
60
const { value } = t47;
61
print(identity(CONST_STRING0));
62
let t0;