Lookup hook declarations on environment
Changes from calling the global parseHookCall() function to looking up the hook declaration on the environment.
Joe Savona committed
Feb 16, 2023 at 14:06 UTC
8810076bc63b2fcf3a750f0be815a84272cc1ba9
8 files changed
+61
-36
compiler/forget/src/HIR/Environment.ts
+7
-2
@@ -1,4 +1,4 @@
1
-import { IdentifierId, makeIdentifierId } from "./HIR";
1
+import { Effect, IdentifierId, makeIdentifierId, ValueKind } from "./HIR";
2
import { BUILTIN_HOOKS, Hook } from "./Hooks";
3
4
const HOOK_PATTERN = /^_?use/;
@@ -35,6 +35,11 @@ export class Environment {
35
if (hook !== undefined) {
36
return hook;
37
}
38
- return null;
38
+ return {
39
+ kind: "Custom",
40
+ name,
41
+ effectKind: Effect.Mutate,
42
+ valueKind: ValueKind.Mutable,
43
+ };
44
}
45
}
compiler/forget/src/HIR/HIR.ts
+1
@@ -49,6 +49,7 @@ export type ReactiveFunction = {
49
generator: boolean;
50
async: boolean;
51
body: ReactiveBlock;
52
+ env: Environment;
53
};
54
55
export type ReactiveScopeBlock = {
compiler/forget/src/HIR/Hooks.ts
+1
-18
@@ -5,7 +5,7 @@
5
* LICENSE file in the root directory of this source tree.
6
*/
7
8
-import { Effect, Place, ValueKind } from "./HIR";
8
+import { Effect, ValueKind } from "./HIR";
9
10
export const BUILTIN_HOOKS: Map<string, Hook> = new Map([
11
[
@@ -62,20 +62,3 @@ export type Hook = {
62
effectKind: Effect;
63
valueKind: ValueKind;
64
};
65
-
66
-export function parseHookCall(place: Place): Hook | null {
67
- const name = place.identifier.name;
68
- if (name === null || !name.match(/^_?use/)) {
69
- return null;
70
- }
71
- const hook = BUILTIN_HOOKS.get(name);
72
- if (hook != null) {
73
- return hook;
74
- }
75
- return {
76
- kind: "Custom",
77
- name: place.identifier.name ?? "",
78
- effectKind: Effect.Mutate,
79
- valueKind: ValueKind.Mutable,
80
- };
81
-}
compiler/forget/src/Inference/InferReferenceEffects.ts
+12
-4
@@ -7,6 +7,7 @@
7
8
import invariant from "invariant";
9
import { CompilerError } from "../CompilerError";
10
+import { Environment } from "../HIR";
11
import {
12
BasicBlock,
13
BlockId,
@@ -19,7 +20,6 @@ import {
20
Place,
21
ValueKind,
22
} from "../HIR/HIR";
22
-import { parseHookCall } from "../HIR/Hooks";
23
import {
24
printMixedHIR,
25
printPlace,
@@ -151,7 +151,7 @@ export default function inferReferenceEffects(fn: HIRFunction) {
151
152
statesByBlock.set(blockId, incomingState);
153
const state = incomingState.clone();
154
- inferBlock(state, block);
154
+ inferBlock(fn.env, state, block);
155
156
for (const nextBlockId of eachTerminalSuccessor(block.terminal)) {
157
queue(nextBlockId, state);
@@ -551,7 +551,12 @@ function mergeValues(a: ValueKind, b: ValueKind): ValueKind {
551
* Iterates over the given @param block, defining variables and
552
* recording references on the @param state according to JS semantics.
553
*/
554
-function inferBlock(state: InferenceState, block: BasicBlock) {
554
+function inferBlock(
555
+ env: Environment,
556
+
557
+ state: InferenceState,
558
+ block: BasicBlock
559
+) {
560
for (const phi of block.phis) {
561
state.inferPhi(phi);
562
}
@@ -583,7 +588,10 @@ function inferBlock(state: InferenceState, block: BasicBlock) {
588
case "CallExpression": {
589
valueKind = ValueKind.Mutable;
590
effectKind = Effect.Mutate;
586
- const hook = parseHookCall(instrValue.callee);
591
+ const hook =
592
+ instrValue.callee.identifier.name !== null
593
+ ? env.getHookDeclaration(instrValue.callee.identifier.name)
594
+ : null;
595
if (hook !== null) {
596
effectKind = hook.effectKind;
597
valueKind = hook.valueKind;
compiler/forget/src/ReactiveScopes/BuildReactiveFunction.ts
+1
@@ -48,6 +48,7 @@ export function buildReactiveFunction(fn: HIRFunction): ReactiveFunction {
48
generator: fn.generator,
49
async: fn.async,
50
body,
51
+ env: fn.env,
52
};
53
}
54
compiler/forget/src/ReactiveScopes/FlattenScopesWithHooks.ts
+14
-4
@@ -6,13 +6,13 @@
6
*/
7
8
import {
9
+ Environment,
10
InstructionId,
11
ReactiveFunction,
12
ReactiveScopeBlock,
13
ReactiveStatement,
14
ReactiveValue,
15
} from "../HIR";
15
-import { parseHookCall } from "../HIR/Hooks";
16
import {
17
ReactiveFunctionTransform,
18
Transformed,
@@ -31,12 +31,19 @@ import {
31
* to ensure the hook call does not inadvertently become conditional.
32
*/
33
export function flattenScopesWithHooks(fn: ReactiveFunction): void {
34
- visitReactiveFunction(fn, new Transform(), { hasHook: false });
34
+ visitReactiveFunction(fn, new Transform(fn.env), { hasHook: false });
35
}
36
37
type State = { hasHook: boolean };
38
39
class Transform extends ReactiveFunctionTransform<State> {
40
+ env: Environment;
41
+
42
+ constructor(env: Environment) {
43
+ super();
44
+ this.env = env;
45
+ }
46
+
47
override transformScope(
48
scope: ReactiveScopeBlock,
49
outerState: State
@@ -56,8 +63,11 @@ class Transform extends ReactiveFunctionTransform<State> {
63
value: ReactiveValue,
64
state: State
65
): void {
59
- if (value.kind === "CallExpression") {
60
- const hook = parseHookCall(value.callee);
66
+ if (
67
+ value.kind === "CallExpression" &&
68
+ value.callee.identifier.name !== null
69
+ ) {
70
+ const hook = this.env.getHookDeclaration(value.callee.identifier.name);
71
if (hook !== null) {
72
state.hasHook = true;
73
}
compiler/forget/src/ReactiveScopes/InferReactiveIdentifiers.ts
+18
-5
@@ -6,13 +6,13 @@
6
*/
7
8
import { CompilerError } from "../CompilerError";
9
+import { Environment } from "../HIR";
10
import {
11
Effect,
12
IdentifierId,
13
ReactiveFunction,
14
ReactiveInstruction,
15
} from "../HIR/HIR";
15
-import { parseHookCall } from "../HIR/Hooks";
16
import { assertExhaustive } from "../Utils/utils";
17
import {
18
eachReactiveValueOperand,
@@ -21,7 +21,14 @@ import {
21
} from "./visitors";
22
23
type IdentifierReactivity = Map<IdentifierId, boolean>;
24
-class Environment extends ReactiveFunctionVisitor<IdentifierReactivity> {
24
+class Visitor extends ReactiveFunctionVisitor<IdentifierReactivity> {
25
+ env: Environment;
26
+
27
+ constructor(env: Environment) {
28
+ super();
29
+ this.env = env;
30
+ }
31
+
32
override visitInstruction(
33
instr: ReactiveInstruction,
34
reactivityMap: IdentifierReactivity
@@ -42,13 +49,19 @@ class Environment extends ReactiveFunctionVisitor<IdentifierReactivity> {
49
break;
50
}
51
}
45
- if (!hasReactiveInput && instr.value.kind === "CallExpression") {
52
+ if (
53
+ !hasReactiveInput &&
54
+ instr.value.kind === "CallExpression" &&
55
+ instr.value.callee.identifier.name !== null
56
+ ) {
57
// Hooks cannot be memoized. Even if they do not accept any reactive inputs,
58
// they are not guaranteed to memoize their return value, and their result
59
// must be assumed to be reactive.
60
// TODO: use types or an opt-in registry of custom hook information to
61
// allow treating safe hooks as non-reactive.
51
- const hook = parseHookCall(instr.value.callee);
62
+ const hook = this.env.getHookDeclaration(
63
+ instr.value.callee.identifier.name
64
+ );
65
if (hook !== null) {
66
hasReactiveInput = true;
67
}
@@ -130,7 +143,7 @@ class Environment extends ReactiveFunctionVisitor<IdentifierReactivity> {
143
export function inferReactiveIdentifiers(
144
fn: ReactiveFunction
145
): Set<IdentifierId> {
133
- const visitor = new Environment();
146
+ const visitor = new Visitor(fn.env);
147
const reactivityMap: IdentifierReactivity = new Map();
148
for (const param of fn.params) {
149
reactivityMap.set(param.identifier.id, true);
compiler/forget/src/TypeInference/InferTypes.ts
+7
-3
@@ -1,5 +1,6 @@
1
import * as t from "@babel/types";
2
import invariant from "invariant";
3
+import { Environment } from "../HIR";
4
import {
5
HIRFunction,
6
Instruction,
@@ -8,7 +9,6 @@ import {
9
TypeId,
10
TypeVar,
11
} from "../HIR/HIR";
11
-import { parseHookCall } from "../HIR/Hooks";
12
import { eachInstructionOperand } from "../HIR/visitors";
13
14
function isPrimitiveBinaryOp(op: t.BinaryExpression["operator"]) {
@@ -83,12 +83,13 @@ function* generate(
83
}
84
85
for (const instr of block.instructions) {
86
- yield* generateInstructionTypes(instr);
86
+ yield* generateInstructionTypes(func.env, instr);
87
}
88
}
89
}
90
91
function* generateInstructionTypes(
92
+ env: Environment,
93
instr: Instruction
94
): Generator<TypeEquation, void, undefined> {
95
const { lvalue, value } = instr;
@@ -121,7 +122,10 @@ function* generateInstructionTypes(
122
}
123
124
case "CallExpression": {
124
- const hook = parseHookCall(value.callee);
125
+ const hook =
126
+ value.callee.identifier.name !== null
127
+ ? env.getHookDeclaration(value.callee.identifier.name)
128
+ : null;
129
let type: Type;
130
if (hook !== null) {
131
type = { kind: "Hook", name: hook.name };