[hir] Parse and codegen FunctionExpression
Sathya Gunasekaran committed
Jan 9, 2023 at 18:22 UTC
ce7cab4eeeae907d9cac4af10792272a63b01336
10 files changed
+85
compiler/forget/src/HIR/BuildHIR.ts
+16
@@ -1148,6 +1148,22 @@ function lowerExpression(
1148
loc: exprLoc,
1149
};
1150
}
1151
+ case "FunctionExpression": {
1152
+ const expr = exprPath as NodePath<t.FunctionExpression>;
1153
+ const name: string | null = expr.get("id")?.node?.name ?? null;
1154
+ const body = expr.get("body").node;
1155
+ const params: Array<string> = expr.get("params").map((p) => {
1156
+ todoInvariant(p.isIdentifier(), "handle non identifier params");
1157
+ return p.node.name;
1158
+ });
1159
+ return {
1160
+ kind: "FunctionExpression",
1161
+ name,
1162
+ body,
1163
+ params,
1164
+ loc: exprLoc,
1165
+ };
1166
+ }
1167
default: {
1168
todo(`lowerExpression(${exprNode.type})`);
1169
// assertExhaustive(
compiler/forget/src/HIR/Codegen.ts
+7
@@ -498,6 +498,13 @@ export function codegenInstructionValue(
498
value = codegenPlace(temp, instrValue);
499
break;
500
}
501
+ case "FunctionExpression": {
502
+ const id =
503
+ instrValue.name !== null ? t.identifier(instrValue.name) : null;
504
+ const params = instrValue.params.map((p) => t.identifier(p));
505
+ value = t.functionExpression(id, params, instrValue.body);
506
+ break;
507
+ }
508
default: {
509
assertExhaustive(
510
instrValue,
compiler/forget/src/HIR/HIR.ts
+6
@@ -309,6 +309,12 @@ export type InstructionData =
309
| { kind: "ComputedStore"; object: Place; property: Place; value: Place }
310
// load `object[index]` - like PropertyLoad but with a dynamic property
311
| { kind: "ComputedLoad"; object: Place; property: Place }
312
+ | {
313
+ kind: "FunctionExpression";
314
+ name: string | null;
315
+ params: Array<string>;
316
+ body: t.BlockStatement;
317
+ }
318
319
/**
320
* Catch-all for statements such as type imports, nested class declarations, etc
compiler/forget/src/HIR/PrintHIR.ts
+6
@@ -290,6 +290,12 @@ export function printInstructionValue(instrValue: InstructionValue): string {
290
)}] = ${printPlace(instrValue.value)}`;
291
break;
292
}
293
+ case "FunctionExpression": {
294
+ const params = instrValue.params.join(",");
295
+ const body = generate(instrValue.body).code;
296
+ value = `Function ${instrValue.name}(${params}){${body}}`;
297
+ break;
298
+ }
299
default: {
300
assertExhaustive(
301
instrValue,
compiler/forget/src/HIR/visitors.ts
+2
@@ -89,6 +89,7 @@ export function* eachInstructionValueOperand(
89
}
90
case "OtherStatement":
91
case "Primitive":
92
+ case "FunctionExpression":
93
case "JSXText": {
94
break;
95
}
@@ -175,6 +176,7 @@ export function mapInstructionOperands(
176
}
177
case "OtherStatement":
178
case "Primitive":
179
+ case "FunctionExpression":
180
case "JSXText": {
181
break;
182
}
compiler/forget/src/Inference/InferReferenceEffects.ts
+6
@@ -551,6 +551,12 @@ function inferBlock(env: Environment, block: BasicBlock) {
551
lvalueEffect = Effect.Store;
552
break;
553
}
554
+ case "FunctionExpression": {
555
+ valueKind = ValueKind.Mutable;
556
+ effectKind = Effect.Read;
557
+ lvalueEffect = Effect.Store;
558
+ break;
559
+ }
560
case "UnaryExpression": {
561
// TODO check that value must be a primitive, or make conditional based on the operator
562
valueKind = ValueKind.Immutable;
compiler/forget/src/ReactiveScopes/InferReactiveScopeVariables.ts
+1
@@ -173,6 +173,7 @@ function mayAllocate(value: InstructionValue): boolean {
173
case "NewExpression":
174
case "ObjectExpression":
175
case "OtherStatement":
176
+ case "FunctionExpression":
177
case "UnaryExpression": {
178
return true;
179
}
compiler/forget/src/ReactiveScopes/PropagateScopeDependencies.ts
+1
@@ -313,6 +313,7 @@ function valueKind(value: InstructionValue): DeclKind {
313
case "JsxFragment":
314
case "NewExpression":
315
case "ObjectExpression":
316
+ case "FunctionExpression":
317
case "OtherStatement":
318
case "UnaryExpression": {
319
return DeclKind.Dynamic;
compiler/forget/src/__tests__/fixtures/hir/simple-function-1.expect.md
new
+34
@@ -0,0 +1,34 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function component() {
6
+ let x = function (a) {
7
+ a.foo();
8
+ };
9
+ return x;
10
+}
11
+
12
+```
13
+
14
+## Code
15
+
16
+```javascript
17
+function component() {
18
+ const $ = React.useMemoCache();
19
+ let x;
20
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
21
+ x = function (a) {
22
+ a.foo();
23
+ };
24
+
25
+ $[0] = x;
26
+ } else {
27
+ x = $[0];
28
+ }
29
+
30
+ return x;
31
+}
32
+
33
+```
34
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/simple-function-1.js
new
+6
@@ -0,0 +1,6 @@
1
+function component() {
2
+ let x = function (a) {
3
+ a.foo();
4
+ };
5
+ return x;
6
+}