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
/**