Invariant for use before definition of reactive values (#662)

This commit is contained in:
Jan Kassens
2022-10-13 16:17:39 -04:00
parent 0000ab6ca4
commit b1006e12cd
3 changed files with 99 additions and 0 deletions
@@ -0,0 +1,97 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import type { NodePath } from "@babel/traverse";
import * as t from "@babel/types";
import type { CompilerContext } from "../CompilerContext";
import { OutputKind } from "../CompilerOutputs";
import * as LIR from "../LIR";
import * as IR from "../IR";
import { PassKind, PassName } from "../Pass";
import { assertExhaustive } from "../Common/utils";
import invariant from "invariant";
import { isReactiveBlock, isRenderBlock } from "../LIR";
/**
* An optional debug step when the output {@link OutputKind.IR} is enabled.
* Records the output IR in the compiler context.
*/
export default {
name: PassName.DumpIR,
kind: PassKind.LIRFunc as const,
run,
};
export function run(
lirFunc: LIR.Func,
_func: NodePath<t.Function>,
_context: CompilerContext
) {
const irFunc = lirFunc.ir;
const reactiveValues = new Set<string>();
function defineReactiveVal(value: IR.ReactiveVal) {
const name = value.binding.identifier.name;
reactiveValues.add(name);
}
function useReactiveValues(values: Set<IR.ReactiveVal>) {
for (const reactiveValue of values) {
const name = reactiveValue.binding.identifier.name;
invariant(
reactiveValues.has(name),
`Reactive value "${name}" not yet defined.`
);
}
}
for (const param of irFunc.params) {
if (IR.isReactiveVal(param)) {
defineReactiveVal(param);
}
}
function visitJSX(expr: IR.ExprVal) {
if (IR.isJSXTagVal(expr)) {
expr.children.forEach((child) => visitJSX(child));
}
if (!expr.stable) {
const entry = lirFunc.memoCache.entries.get(expr);
if (entry) {
invariant(LIR.MemoCache.isExprEntry(entry), "");
const { inputs } = irFunc.depGraph.getOrCreateVertex(entry.value);
useReactiveValues(inputs);
}
}
}
for (const block of lirFunc.blocks) {
switch (block.kind) {
case LIR.BlockKind.Render:
invariant(isRenderBlock(block), "Expected render block");
for (const instr of block.body) {
for (const decl of instr.ir.decls) {
if (IR.isReactiveVal(decl)) {
defineReactiveVal(decl);
}
}
}
break;
case LIR.BlockKind.Reactive:
invariant(isReactiveBlock(block), "Expected reactive block");
useReactiveValues(block.inputs);
for (const instr of block.body) {
instr.ir.jsxTreeRoots.forEach((root) => {
visitJSX(root);
});
}
break;
default:
assertExhaustive(block.kind, `Unhandled block ${block}`);
}
}
}
+1
View File
@@ -14,3 +14,4 @@ export { default as DumpLIR } from "./DumpLIR";
export { default as JSGen } from "./JSGen";
export { default as LIRGen } from "./LIRGen";
export { default as MemoCacheAlloc } from "./MemoCacheAlloc";
export { default as SanityCheck } from "./SanityCheck";
+1
View File
@@ -53,6 +53,7 @@ export function createCompilerDriver(
passManager.addPass(BE.LIRGen);
passManager.addPass(BE.MemoCacheAlloc);
passManager.addPass(BE.DumpLIR);
passManager.addPass(BE.SanityCheck);
// JS Generation.
passManager.addPass(BE.JSGen);