@samitouri / QOS-React / commits / eece73262e

Start of making globals and hook declarations configurable

Some refactoring to allow the environment options to be passed in from the outside.

Joe Savona committed Feb 16, 2023 at 14:06 UTC eece73262e134a1351d3069a3b79da014e078e46
6 files changed +53 -12
compiler/forget/src/Babel/BabelPlugin.ts
+3 -3
@@ -7,12 +7,12 @@
7
8 /// <reference path="./plugin-syntax-jsx.d.ts" />
9
10 -import * as t from "@babel/types";
10 import type * as BabelCore from "@babel/core";
11 import generate from "@babel/generator";
12 import jsx from "@babel/plugin-syntax-jsx";
14 -import { CompilerFlags, parseCompilerFlags } from "../CompilerFlags";
13 +import * as t from "@babel/types";
14 import prettier from "prettier";
15 +import { CompilerFlags, parseCompilerFlags } from "../CompilerFlags";
16 import { compile } from "../CompilerPipeline";
17
18 type BabelPluginPass = {
@@ -47,7 +47,7 @@ export default function ReactForgetBabelPlugin(
47 if (fn.scope.getProgramParent() !== fn.scope.parent) {
48 return;
49 }
50 - const ast = compile(fn);
50 + const ast = compile(fn, null);
51
52 // We are generating a new FunctionDeclaration node, so we must skip over it or this
53 // traversal will loop infinitely.
compiler/forget/src/CompilerPipeline.ts
+9 -4
@@ -12,6 +12,7 @@ import {
12 mergeConsecutiveBlocks,
13 ReactiveFunction,
14 } from "./HIR";
15 +import { EnvironmentOptions } from "./HIR/Environment";
16 import {
17 analyseFunctions,
18 dropMemoCalls,
@@ -47,9 +48,10 @@ export type CompilerPipelineValue =
48 | { kind: "reactive"; name: string; value: ReactiveFunction };
49
50 export function* run(
50 - func: NodePath<t.FunctionDeclaration>
51 + func: NodePath<t.FunctionDeclaration>,
52 + options?: EnvironmentOptions | null
53 ): Generator<CompilerPipelineValue, t.Function> {
52 - const hir = lower(func).unwrap();
54 + const hir = lower(func, options ?? null).unwrap();
55 yield log({ kind: "hir", name: "HIR", value: hir });
56
57 mergeConsecutiveBlocks(hir);
@@ -186,8 +188,11 @@ export function* run(
188 return ast;
189 }
190
189 -export function compile(func: NodePath<t.FunctionDeclaration>): t.Function {
190 - let generator = run(func);
191 +export function compile(
192 + func: NodePath<t.FunctionDeclaration>,
193 + options?: EnvironmentOptions | null
194 +): t.Function {
195 + let generator = run(func, options);
196 while (true) {
197 const next = generator.next();
198 if (next.done) {
compiler/forget/src/HIR/BuildHIR.ts
+4 -3
@@ -11,7 +11,7 @@ import invariant from "invariant";
11 import { CompilerError, ErrorSeverity } from "../CompilerError";
12 import { Err, Ok, Result } from "../Utils/Result";
13 import { assertExhaustive } from "../Utils/utils";
14 -import { Environment } from "./Environment";
14 +import { Environment, EnvironmentOptions } from "./Environment";
15 import {
16 BlockId,
17 BranchTerminal,
@@ -51,9 +51,10 @@ import HIRBuilder from "./HIRBuilder";
51 */
52 export function lower(
53 func: NodePath<t.Function>,
54 + options: EnvironmentOptions | null,
55 capturedRefs: t.Identifier[] = []
56 ): Result<HIRFunction, CompilerError> {
56 - const env = new Environment();
57 + const env = new Environment(options);
58 const builder = new HIRBuilder(env, capturedRefs);
59 const context: Place[] = [];
60
@@ -1334,7 +1335,7 @@ function lowerExpression(
1335 //
1336 // This isn't a problem in practice because use Babel's scope analysis to
1337 // identify the correct references.
1337 - const lowering = lower(expr, [
1338 + const lowering = lower(expr, builder.environment.options, [
1339 ...builder.context,
1340 ...captured.identifiers,
1341 ]);
compiler/forget/src/HIR/Environment.ts
+31
@@ -1,9 +1,40 @@
1 import { IdentifierId, makeIdentifierId } from "./HIR";
2 +import { BUILTIN_HOOKS, Hook } from "./Hooks";
3 +
4 +const HOOK_PATTERN = /^_?use/;
5 +
6 +export type EnvironmentOptions = {
7 + customHooks: Map<string, Hook>;
8 +};
9 +
10 +const DEFAULT_OPTIONS: EnvironmentOptions = {
11 + customHooks: new Map(),
12 +};
13
14 export class Environment {
15 + #options: EnvironmentOptions;
16 #nextIdentifer: number = 0;
17
18 + constructor(options: EnvironmentOptions | null) {
19 + this.#options = options ?? DEFAULT_OPTIONS;
20 + }
21 +
22 + get options(): EnvironmentOptions {
23 + return this.#options;
24 + }
25 +
26 get nextIdentifierId(): IdentifierId {
27 return makeIdentifierId(this.#nextIdentifer++);
28 }
29 +
30 + getHookDeclaration(name: string): Hook | null {
31 + if (!name.match(HOOK_PATTERN)) {
32 + return null;
33 + }
34 + const hook = BUILTIN_HOOKS.get(name) ?? this.#options.customHooks.get(name);
35 + if (hook !== undefined) {
36 + return hook;
37 + }
38 + return null;
39 + }
40 }
compiler/forget/src/HIR/HIRBuilder.ts
+4
@@ -93,6 +93,10 @@ export default class HIRBuilder {
93 return this.#context;
94 }
95
96 + get environment(): Environment {
97 + return this.#env;
98 + }
99 +
100 constructor(env: Environment, context: t.Identifier[]) {
101 this.#env = env;
102 this.#context = context;
compiler/forget/src/HIR/Hooks.ts
+2 -2
@@ -7,7 +7,7 @@
7
8 import { Effect, Place, ValueKind } from "./HIR";
9
10 -export const HOOKS: Map<string, Hook> = new Map([
10 +export const BUILTIN_HOOKS: Map<string, Hook> = new Map([
11 [
12 "useState",
13 {
@@ -68,7 +68,7 @@ export function parseHookCall(place: Place): Hook | null {
68 if (name === null || !name.match(/^_?use/)) {
69 return null;
70 }
71 - const hook = HOOKS.get(name);
71 + const hook = BUILTIN_HOOKS.get(name);
72 if (hook != null) {
73 return hook;
74 }