From eece73262e134a1351d3069a3b79da014e078e46 Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Thu, 16 Feb 2023 14:06:15 -0800 Subject: [PATCH] Start of making globals and hook declarations configurable Some refactoring to allow the environment options to be passed in from the outside. --- compiler/forget/src/Babel/BabelPlugin.ts | 6 ++--- compiler/forget/src/CompilerPipeline.ts | 13 +++++++--- compiler/forget/src/HIR/BuildHIR.ts | 7 +++--- compiler/forget/src/HIR/Environment.ts | 31 ++++++++++++++++++++++++ compiler/forget/src/HIR/HIRBuilder.ts | 4 +++ compiler/forget/src/HIR/Hooks.ts | 4 +-- 6 files changed, 53 insertions(+), 12 deletions(-) diff --git a/compiler/forget/src/Babel/BabelPlugin.ts b/compiler/forget/src/Babel/BabelPlugin.ts index bf5f0a86ab..59a70c4bc6 100644 --- a/compiler/forget/src/Babel/BabelPlugin.ts +++ b/compiler/forget/src/Babel/BabelPlugin.ts @@ -7,12 +7,12 @@ /// -import * as t from "@babel/types"; import type * as BabelCore from "@babel/core"; import generate from "@babel/generator"; import jsx from "@babel/plugin-syntax-jsx"; -import { CompilerFlags, parseCompilerFlags } from "../CompilerFlags"; +import * as t from "@babel/types"; import prettier from "prettier"; +import { CompilerFlags, parseCompilerFlags } from "../CompilerFlags"; import { compile } from "../CompilerPipeline"; type BabelPluginPass = { @@ -47,7 +47,7 @@ export default function ReactForgetBabelPlugin( if (fn.scope.getProgramParent() !== fn.scope.parent) { return; } - const ast = compile(fn); + const ast = compile(fn, null); // We are generating a new FunctionDeclaration node, so we must skip over it or this // traversal will loop infinitely. diff --git a/compiler/forget/src/CompilerPipeline.ts b/compiler/forget/src/CompilerPipeline.ts index 5f7c8b93d6..829fa3a16e 100644 --- a/compiler/forget/src/CompilerPipeline.ts +++ b/compiler/forget/src/CompilerPipeline.ts @@ -12,6 +12,7 @@ import { mergeConsecutiveBlocks, ReactiveFunction, } from "./HIR"; +import { EnvironmentOptions } from "./HIR/Environment"; import { analyseFunctions, dropMemoCalls, @@ -47,9 +48,10 @@ export type CompilerPipelineValue = | { kind: "reactive"; name: string; value: ReactiveFunction }; export function* run( - func: NodePath + func: NodePath, + options?: EnvironmentOptions | null ): Generator { - const hir = lower(func).unwrap(); + const hir = lower(func, options ?? null).unwrap(); yield log({ kind: "hir", name: "HIR", value: hir }); mergeConsecutiveBlocks(hir); @@ -186,8 +188,11 @@ export function* run( return ast; } -export function compile(func: NodePath): t.Function { - let generator = run(func); +export function compile( + func: NodePath, + options?: EnvironmentOptions | null +): t.Function { + let generator = run(func, options); while (true) { const next = generator.next(); if (next.done) { diff --git a/compiler/forget/src/HIR/BuildHIR.ts b/compiler/forget/src/HIR/BuildHIR.ts index b2827a535f..05eb6b3585 100644 --- a/compiler/forget/src/HIR/BuildHIR.ts +++ b/compiler/forget/src/HIR/BuildHIR.ts @@ -11,7 +11,7 @@ import invariant from "invariant"; import { CompilerError, ErrorSeverity } from "../CompilerError"; import { Err, Ok, Result } from "../Utils/Result"; import { assertExhaustive } from "../Utils/utils"; -import { Environment } from "./Environment"; +import { Environment, EnvironmentOptions } from "./Environment"; import { BlockId, BranchTerminal, @@ -51,9 +51,10 @@ import HIRBuilder from "./HIRBuilder"; */ export function lower( func: NodePath, + options: EnvironmentOptions | null, capturedRefs: t.Identifier[] = [] ): Result { - const env = new Environment(); + const env = new Environment(options); const builder = new HIRBuilder(env, capturedRefs); const context: Place[] = []; @@ -1334,7 +1335,7 @@ function lowerExpression( // // This isn't a problem in practice because use Babel's scope analysis to // identify the correct references. - const lowering = lower(expr, [ + const lowering = lower(expr, builder.environment.options, [ ...builder.context, ...captured.identifiers, ]); diff --git a/compiler/forget/src/HIR/Environment.ts b/compiler/forget/src/HIR/Environment.ts index 4b72b43037..23518271d9 100644 --- a/compiler/forget/src/HIR/Environment.ts +++ b/compiler/forget/src/HIR/Environment.ts @@ -1,9 +1,40 @@ import { IdentifierId, makeIdentifierId } from "./HIR"; +import { BUILTIN_HOOKS, Hook } from "./Hooks"; + +const HOOK_PATTERN = /^_?use/; + +export type EnvironmentOptions = { + customHooks: Map; +}; + +const DEFAULT_OPTIONS: EnvironmentOptions = { + customHooks: new Map(), +}; export class Environment { + #options: EnvironmentOptions; #nextIdentifer: number = 0; + constructor(options: EnvironmentOptions | null) { + this.#options = options ?? DEFAULT_OPTIONS; + } + + get options(): EnvironmentOptions { + return this.#options; + } + get nextIdentifierId(): IdentifierId { return makeIdentifierId(this.#nextIdentifer++); } + + getHookDeclaration(name: string): Hook | null { + if (!name.match(HOOK_PATTERN)) { + return null; + } + const hook = BUILTIN_HOOKS.get(name) ?? this.#options.customHooks.get(name); + if (hook !== undefined) { + return hook; + } + return null; + } } diff --git a/compiler/forget/src/HIR/HIRBuilder.ts b/compiler/forget/src/HIR/HIRBuilder.ts index 58c8372870..8aac83fe6f 100644 --- a/compiler/forget/src/HIR/HIRBuilder.ts +++ b/compiler/forget/src/HIR/HIRBuilder.ts @@ -93,6 +93,10 @@ export default class HIRBuilder { return this.#context; } + get environment(): Environment { + return this.#env; + } + constructor(env: Environment, context: t.Identifier[]) { this.#env = env; this.#context = context; diff --git a/compiler/forget/src/HIR/Hooks.ts b/compiler/forget/src/HIR/Hooks.ts index 59e4d81339..916560fbac 100644 --- a/compiler/forget/src/HIR/Hooks.ts +++ b/compiler/forget/src/HIR/Hooks.ts @@ -7,7 +7,7 @@ import { Effect, Place, ValueKind } from "./HIR"; -export const HOOKS: Map = new Map([ +export const BUILTIN_HOOKS: Map = new Map([ [ "useState", { @@ -68,7 +68,7 @@ export function parseHookCall(place: Place): Hook | null { if (name === null || !name.match(/^_?use/)) { return null; } - const hook = HOOKS.get(name); + const hook = BUILTIN_HOOKS.get(name); if (hook != null) { return hook; }