@samitouri / QOS-React-2 / commits / ac9212f24e

Make globals configurable; populate a reasonable default list

This is a precursor to validating that all identifiers are defined - we need to know about gobals and module declarations, so this PR adds the ability to configure a Set<string> of defined globals. The default list is inspired by the globals that prepack defines, which just comes from the spec definition.

Joe Savona committed Feb 17, 2023 at 09:47 UTC ac9212f24e254456de3fbe23b6b9b71f1bbc8d8a
5 files changed +70 -24
compiler/forget/src/Babel/PluginOptions.ts
+1 -1
@@ -13,7 +13,7 @@ export type PluginOptions = {
13 */
14 enableOnlyOnUseForgetDirective: boolean;
15
16 - environment: EnvironmentOptions | null;
16 + environment: Partial<EnvironmentOptions> | null;
17 };
18
19 export const defaultOptions: PluginOptions = {
compiler/forget/src/CompilerPipeline.ts
+4 -4
@@ -12,7 +12,7 @@ import {
12 mergeConsecutiveBlocks,
13 ReactiveFunction,
14 } from "./HIR";
15 -import { EnvironmentOptions } from "./HIR/Environment";
15 +import { EnvironmentOptions, mergeOptions } from "./HIR/Environment";
16 import {
17 analyseFunctions,
18 dropMemoCalls,
@@ -49,9 +49,9 @@ export type CompilerPipelineValue =
49
50 export function* run(
51 func: NodePath<t.FunctionDeclaration>,
52 - options?: EnvironmentOptions | null
52 + options?: Partial<EnvironmentOptions> | null
53 ): Generator<CompilerPipelineValue, t.Function> {
54 - const hir = lower(func, options ?? null).unwrap();
54 + const hir = lower(func, mergeOptions(options ?? null)).unwrap();
55 yield log({ kind: "hir", name: "HIR", value: hir });
56
57 mergeConsecutiveBlocks(hir);
@@ -190,7 +190,7 @@ export function* run(
190
191 export function compile(
192 func: NodePath<t.FunctionDeclaration>,
193 - options?: EnvironmentOptions | null
193 + options?: Partial<EnvironmentOptions> | null
194 ): t.Function {
195 let generator = run(func, options);
196 while (true) {
compiler/forget/src/HIR/Environment.ts
+20
@@ -1,3 +1,5 @@
1 +import { log } from "../Utils/logger";
2 +import { DEFAULT_GLOBALS, Global } from "./Globals";
3 import { Effect, IdentifierId, makeIdentifierId, ValueKind } from "./HIR";
4 import { BUILTIN_HOOKS, Hook } from "./Hooks";
5
@@ -5,12 +7,23 @@ const HOOK_PATTERN = /^_?use/;
7
8 export type EnvironmentOptions = {
9 customHooks: Map<string, Hook>;
10 + globals: Set<string>;
11 };
12
13 const DEFAULT_OPTIONS: EnvironmentOptions = {
14 customHooks: new Map(),
15 + globals: DEFAULT_GLOBALS,
16 };
17
18 +export function mergeOptions(
19 + options: Partial<EnvironmentOptions> | null
20 +): EnvironmentOptions {
21 + return {
22 + ...DEFAULT_OPTIONS,
23 + ...(options ?? {}),
24 + };
25 +}
26 +
27 export class Environment {
28 #options: EnvironmentOptions;
29 #nextIdentifer: number = 0;
@@ -27,6 +40,13 @@ export class Environment {
40 return makeIdentifierId(this.#nextIdentifer++);
41 }
42
43 + getGlobalDeclaration(name: string): Global | null {
44 + if (!this.#options.globals.has(name)) {
45 + log(() => `Undefined global '${name}'`);
46 + }
47 + return { name };
48 + }
49 +
50 getHookDeclaration(name: string): Hook | null {
51 if (!name.match(HOOK_PATTERN)) {
52 return null;
compiler/forget/src/HIR/Globals.ts
+43 -17
@@ -5,25 +5,51 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import * as t from "@babel/types";
9 -
10 -const GLOBALS: Map<string, t.Identifier> = new Map([
11 - ["Map", t.identifier("Map")],
12 - ["Set", t.identifier("Set")],
13 - ["Math", t.identifier("Math")],
8 +export const DEFAULT_GLOBALS: Set<string> = new Set([
9 + "String",
10 + "Object",
11 + "Function",
12 + "Array",
13 + "Number",
14 + "RegExp",
15 + "Date",
16 + "Math",
17 + "Error",
18 + "Function",
19 + "TypeError",
20 + "RangeError",
21 + "ReferenceError",
22 + "SyntaxError",
23 + "URIError",
24 + "EvalError",
25 + "Boolean",
26 + "DataView",
27 + "Float32Array",
28 + "Float64Array",
29 + "Int8Array",
30 + "Int16Array",
31 + "Int32Array",
32 + "Map",
33 + "Set",
34 + "WeakMap",
35 + "Uint8Array",
36 + "Uint8ClampedArray",
37 + "Uint16Array",
38 + "Uint32Array",
39 + "ArrayBuffer",
40 + "JSON",
41 + "parseFloat",
42 + "parseInt",
43 + "console",
44 + "isNaN",
45 + "eval",
46 + "isFinite",
47 + "encodeURI",
48 + "decodeURI",
49 + "encodeURIComponent",
50 + "decodeURIComponent",
51 ]);
52
53 export type Global = {
54 name: string;
55 };
19 -
20 -// TODO: This will work as a stopgap but it isn't really correct. We need proper handling of globals
21 -// and module-scoped variables, which means understanding module constants and imports.
22 -export function getGlobalDeclaration(identifierName: string): Global | null {
23 - const ident = GLOBALS.get(identifierName);
24 - if (ident != null) {
25 - return ident;
26 - }
27 - // TODO: return null if not explicitly configured by the user
28 - return { name: identifierName };
29 -}
compiler/forget/src/HIR/HIRBuilder.ts
+2 -2
@@ -12,7 +12,7 @@ import { CompilerError } from "../CompilerError";
12 import { logHIR } from "../Utils/logger";
13 import { assertExhaustive } from "../Utils/utils";
14 import { Environment } from "./Environment";
15 -import { getGlobalDeclaration, Global } from "./Globals";
15 +import { Global } from "./Globals";
16 import {
17 BasicBlock,
18 BlockId,
@@ -137,7 +137,7 @@ export default class HIRBuilder {
137 }
138
139 resolveGlobal(path: NodePath<t.Identifier | t.JSXIdentifier>): Global | null {
140 - return getGlobalDeclaration(path.node.name);
140 + return this.#env.getGlobalDeclaration(path.node.name);
141 }
142
143 /**