@samitouri / QOS-React-1 / commits / fea7b5ac0d

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;