@samitouri / QOS-React / commits / b1006e12cd

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

Jan Kassens committed Oct 13, 2022 at 16:17 UTC b1006e12cd996debe341fa27db548d4c794bbdaa
3 files changed +99
compiler/forget/src/BackEnd/SanityCheck.ts new
+97
@@ -0,0 +1,97 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + */
7 +
8 +import type { NodePath } from "@babel/traverse";
9 +import * as t from "@babel/types";
10 +import type { CompilerContext } from "../CompilerContext";
11 +import { OutputKind } from "../CompilerOutputs";
12 +import * as LIR from "../LIR";
13 +import * as IR from "../IR";
14 +import { PassKind, PassName } from "../Pass";
15 +import { assertExhaustive } from "../Common/utils";
16 +import invariant from "invariant";
17 +import { isReactiveBlock, isRenderBlock } from "../LIR";
18 +
19 +/**
20 + * An optional debug step when the output {@link OutputKind.IR} is enabled.
21 + * Records the output IR in the compiler context.
22 + */
23 +export default {
24 + name: PassName.DumpIR,
25 + kind: PassKind.LIRFunc as const,
26 + run,
27 +};
28 +
29 +export function run(
30 + lirFunc: LIR.Func,
31 + _func: NodePath<t.Function>,
32 + _context: CompilerContext
33 +) {
34 + const irFunc = lirFunc.ir;
35 +
36 + const reactiveValues = new Set<string>();
37 + function defineReactiveVal(value: IR.ReactiveVal) {
38 + const name = value.binding.identifier.name;
39 + reactiveValues.add(name);
40 + }
41 +
42 + function useReactiveValues(values: Set<IR.ReactiveVal>) {
43 + for (const reactiveValue of values) {
44 + const name = reactiveValue.binding.identifier.name;
45 + invariant(
46 + reactiveValues.has(name),
47 + `Reactive value "${name}" not yet defined.`
48 + );
49 + }
50 + }
51 +
52 + for (const param of irFunc.params) {
53 + if (IR.isReactiveVal(param)) {
54 + defineReactiveVal(param);
55 + }
56 + }
57 +
58 + function visitJSX(expr: IR.ExprVal) {
59 + if (IR.isJSXTagVal(expr)) {
60 + expr.children.forEach((child) => visitJSX(child));
61 + }
62 + if (!expr.stable) {
63 + const entry = lirFunc.memoCache.entries.get(expr);
64 + if (entry) {
65 + invariant(LIR.MemoCache.isExprEntry(entry), "");
66 + const { inputs } = irFunc.depGraph.getOrCreateVertex(entry.value);
67 + useReactiveValues(inputs);
68 + }
69 + }
70 + }
71 +
72 + for (const block of lirFunc.blocks) {
73 + switch (block.kind) {
74 + case LIR.BlockKind.Render:
75 + invariant(isRenderBlock(block), "Expected render block");
76 + for (const instr of block.body) {
77 + for (const decl of instr.ir.decls) {
78 + if (IR.isReactiveVal(decl)) {
79 + defineReactiveVal(decl);
80 + }
81 + }
82 + }
83 + break;
84 + case LIR.BlockKind.Reactive:
85 + invariant(isReactiveBlock(block), "Expected reactive block");
86 + useReactiveValues(block.inputs);
87 + for (const instr of block.body) {
88 + instr.ir.jsxTreeRoots.forEach((root) => {
89 + visitJSX(root);
90 + });
91 + }
92 + break;
93 + default:
94 + assertExhaustive(block.kind, `Unhandled block ${block}`);
95 + }
96 + }
97 +}
compiler/forget/src/BackEnd/index.ts
+1
@@ -14,3 +14,4 @@ export { default as DumpLIR } from "./DumpLIR";
14 export { default as JSGen } from "./JSGen";
15 export { default as LIRGen } from "./LIRGen";
16 export { default as MemoCacheAlloc } from "./MemoCacheAlloc";
17 +export { default as SanityCheck } from "./SanityCheck";
compiler/forget/src/CompilerDriver.ts
+1
@@ -53,6 +53,7 @@ export function createCompilerDriver(
53 passManager.addPass(BE.LIRGen);
54 passManager.addPass(BE.MemoCacheAlloc);
55 passManager.addPass(BE.DumpLIR);
56 + passManager.addPass(BE.SanityCheck);
57
58 // JS Generation.
59 passManager.addPass(BE.JSGen);