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.
This commit is contained in:
Joe Savona
2023-02-17 09:47:59 -08:00
parent 6b654f306c
commit ac9212f24e
5 changed files with 70 additions and 24 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ export type PluginOptions = {
*/
enableOnlyOnUseForgetDirective: boolean;
environment: EnvironmentOptions | null;
environment: Partial<EnvironmentOptions> | null;
};
export const defaultOptions: PluginOptions = {
+4 -4
View File
@@ -12,7 +12,7 @@ import {
mergeConsecutiveBlocks,
ReactiveFunction,
} from "./HIR";
import { EnvironmentOptions } from "./HIR/Environment";
import { EnvironmentOptions, mergeOptions } from "./HIR/Environment";
import {
analyseFunctions,
dropMemoCalls,
@@ -49,9 +49,9 @@ export type CompilerPipelineValue =
export function* run(
func: NodePath<t.FunctionDeclaration>,
options?: EnvironmentOptions | null
options?: Partial<EnvironmentOptions> | null
): Generator<CompilerPipelineValue, t.Function> {
const hir = lower(func, options ?? null).unwrap();
const hir = lower(func, mergeOptions(options ?? null)).unwrap();
yield log({ kind: "hir", name: "HIR", value: hir });
mergeConsecutiveBlocks(hir);
@@ -190,7 +190,7 @@ export function* run(
export function compile(
func: NodePath<t.FunctionDeclaration>,
options?: EnvironmentOptions | null
options?: Partial<EnvironmentOptions> | null
): t.Function {
let generator = run(func, options);
while (true) {
+20
View File
@@ -1,3 +1,5 @@
import { log } from "../Utils/logger";
import { DEFAULT_GLOBALS, Global } from "./Globals";
import { Effect, IdentifierId, makeIdentifierId, ValueKind } from "./HIR";
import { BUILTIN_HOOKS, Hook } from "./Hooks";
@@ -5,12 +7,23 @@ const HOOK_PATTERN = /^_?use/;
export type EnvironmentOptions = {
customHooks: Map<string, Hook>;
globals: Set<string>;
};
const DEFAULT_OPTIONS: EnvironmentOptions = {
customHooks: new Map(),
globals: DEFAULT_GLOBALS,
};
export function mergeOptions(
options: Partial<EnvironmentOptions> | null
): EnvironmentOptions {
return {
...DEFAULT_OPTIONS,
...(options ?? {}),
};
}
export class Environment {
#options: EnvironmentOptions;
#nextIdentifer: number = 0;
@@ -27,6 +40,13 @@ export class Environment {
return makeIdentifierId(this.#nextIdentifer++);
}
getGlobalDeclaration(name: string): Global | null {
if (!this.#options.globals.has(name)) {
log(() => `Undefined global '${name}'`);
}
return { name };
}
getHookDeclaration(name: string): Hook | null {
if (!name.match(HOOK_PATTERN)) {
return null;
+43 -17
View File
@@ -5,25 +5,51 @@
* LICENSE file in the root directory of this source tree.
*/
import * as t from "@babel/types";
const GLOBALS: Map<string, t.Identifier> = new Map([
["Map", t.identifier("Map")],
["Set", t.identifier("Set")],
["Math", t.identifier("Math")],
export const DEFAULT_GLOBALS: Set<string> = new Set([
"String",
"Object",
"Function",
"Array",
"Number",
"RegExp",
"Date",
"Math",
"Error",
"Function",
"TypeError",
"RangeError",
"ReferenceError",
"SyntaxError",
"URIError",
"EvalError",
"Boolean",
"DataView",
"Float32Array",
"Float64Array",
"Int8Array",
"Int16Array",
"Int32Array",
"Map",
"Set",
"WeakMap",
"Uint8Array",
"Uint8ClampedArray",
"Uint16Array",
"Uint32Array",
"ArrayBuffer",
"JSON",
"parseFloat",
"parseInt",
"console",
"isNaN",
"eval",
"isFinite",
"encodeURI",
"decodeURI",
"encodeURIComponent",
"decodeURIComponent",
]);
export type Global = {
name: string;
};
// TODO: This will work as a stopgap but it isn't really correct. We need proper handling of globals
// and module-scoped variables, which means understanding module constants and imports.
export function getGlobalDeclaration(identifierName: string): Global | null {
const ident = GLOBALS.get(identifierName);
if (ident != null) {
return ident;
}
// TODO: return null if not explicitly configured by the user
return { name: identifierName };
}
+2 -2
View File
@@ -12,7 +12,7 @@ import { CompilerError } from "../CompilerError";
import { logHIR } from "../Utils/logger";
import { assertExhaustive } from "../Utils/utils";
import { Environment } from "./Environment";
import { getGlobalDeclaration, Global } from "./Globals";
import { Global } from "./Globals";
import {
BasicBlock,
BlockId,
@@ -137,7 +137,7 @@ export default class HIRBuilder {
}
resolveGlobal(path: NodePath<t.Identifier | t.JSXIdentifier>): Global | null {
return getGlobalDeclaration(path.node.name);
return this.#env.getGlobalDeclaration(path.node.name);
}
/**