Helper for lowering args
Cleans up duplicated code for processing call/constructor arguments. As a side benefit, we now support spread elements for constructor arguments (and if we want to change how we represent that, we can do it in one place).
Joe Savona committed
Mar 20, 2023 at 12:55 UTC
3e84c870f60da18222ff6faceefbacd31c58138b
8 files changed
+230
-219
compiler/forget/src/HIR/BuildHIR.ts
+34
-53
@@ -905,18 +905,7 @@ function lowerExpression(
905
return { kind: "UnsupportedNode", node: exprNode, loc: exprLoc };
906
}
907
const callee = lowerExpressionToTemporary(builder, calleePath);
908
- let args: Place[] = [];
909
- for (const argPath of expr.get("arguments")) {
910
- if (!argPath.isExpression()) {
911
- builder.errors.push({
912
- reason: `(BuildHIR::lowerExpression) Handle ${argPath.type} arguments in NewExpression`,
913
- severity: ErrorSeverity.Todo,
914
- nodePath: argPath,
915
- });
916
- continue;
917
- }
918
- args.push(lowerExpressionToTemporary(builder, argPath));
919
- }
908
+ const args = lowerArguments(builder, expr.get("arguments"));
909
910
return {
911
kind: "NewExpression",
@@ -938,26 +927,7 @@ function lowerExpression(
927
}
928
if (calleePath.isMemberExpression()) {
929
const { object, property } = lowerMemberExpression(builder, calleePath);
941
- let args: Array<Place | SpreadPattern> = [];
942
- for (const argPath of expr.get("arguments")) {
943
- if (argPath.isSpreadElement()) {
944
- args.push({
945
- kind: "Spread",
946
- place: lowerExpressionToTemporary(
947
- builder,
948
- argPath.get("argument")
949
- ),
950
- });
951
- } else if (argPath.isExpression()) {
952
- args.push(lowerExpressionToTemporary(builder, argPath));
953
- } else {
954
- builder.errors.push({
955
- reason: `(BuildHIR::lowerExpression) Handle ${argPath.type} arguments in CallExpression`,
956
- severity: ErrorSeverity.Todo,
957
- nodePath: argPath,
958
- });
959
- }
960
- }
930
+ const args = lowerArguments(builder, expr.get("arguments"));
931
if (typeof property === "string") {
932
return {
933
kind: "PropertyCall",
@@ -977,27 +947,7 @@ function lowerExpression(
947
}
948
} else {
949
const callee = lowerExpressionToTemporary(builder, calleePath);
980
- let args: Array<Place | SpreadPattern> = [];
981
- for (const argPath of expr.get("arguments")) {
982
- if (argPath.isSpreadElement()) {
983
- args.push({
984
- kind: "Spread",
985
- place: lowerExpressionToTemporary(
986
- builder,
987
- argPath.get("argument")
988
- ),
989
- });
990
- } else if (argPath.isExpression()) {
991
- args.push(lowerExpressionToTemporary(builder, argPath));
992
- } else {
993
- builder.errors.push({
994
- reason: `(BuildHIR::lowerExpression) Handle ${argPath.type} arguments in CallExpression`,
995
- severity: ErrorSeverity.Todo,
996
- nodePath: argPath,
997
- });
998
- continue;
999
- }
1000
- }
950
+ const args = lowerArguments(builder, expr.get("arguments"));
951
return {
952
kind: "CallExpression",
953
callee,
@@ -1695,6 +1645,37 @@ function lowerExpression(
1645
}
1646
}
1647
1648
+function lowerArguments(
1649
+ builder: HIRBuilder,
1650
+ expr: Array<
1651
+ NodePath<
1652
+ | t.Expression
1653
+ | t.SpreadElement
1654
+ | t.JSXNamespacedName
1655
+ | t.ArgumentPlaceholder
1656
+ >
1657
+ >
1658
+): Array<Place | SpreadPattern> {
1659
+ let args: Array<Place | SpreadPattern> = [];
1660
+ for (const argPath of expr) {
1661
+ if (argPath.isSpreadElement()) {
1662
+ args.push({
1663
+ kind: "Spread",
1664
+ place: lowerExpressionToTemporary(builder, argPath.get("argument")),
1665
+ });
1666
+ } else if (argPath.isExpression()) {
1667
+ args.push(lowerExpressionToTemporary(builder, argPath));
1668
+ } else {
1669
+ builder.errors.push({
1670
+ reason: `(BuildHIR::lowerExpression) Handle ${argPath.type} arguments in CallExpression`,
1671
+ severity: ErrorSeverity.Todo,
1672
+ nodePath: argPath,
1673
+ });
1674
+ }
1675
+ }
1676
+ return args;
1677
+}
1678
+
1679
function lowerMemberExpression(
1680
builder: HIRBuilder,
1681
expr: NodePath<t.MemberExpression | t.OptionalMemberExpression>
compiler/forget/src/HIR/HIR.ts
+1
-1
@@ -479,7 +479,7 @@ export type InstructionValue =
479
| {
480
kind: "NewExpression";
481
callee: Place;
482
- args: Array<Place>;
482
+ args: Array<Place | SpreadPattern>;
483
loc: SourceLocation;
484
}
485
| {
compiler/forget/src/HIR/PrintHIR.ts
+11
-7
@@ -256,6 +256,12 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
256
} ${printPlace(instrValue.right)}`;
257
break;
258
}
259
+ case "NewExpression": {
260
+ value = `New ${printPlace(instrValue.callee)}(${instrValue.args
261
+ .map((arg) => printPattern(arg))
262
+ .join(", ")})`;
263
+ break;
264
+ }
265
case "CallExpression": {
266
value = `Call ${printPlace(instrValue.callee)}(${instrValue.args
267
.map((arg) => printPattern(arg))
@@ -287,7 +293,11 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
293
const propItems = [];
294
for (const attribute of instrValue.props) {
295
if (attribute.kind === "JsxAttribute") {
290
- propItems.push(`${attribute.name}={${printPlace(attribute.place)}}`);
296
+ propItems.push(
297
+ `${attribute.name}={${
298
+ attribute.place !== null ? printPlace(attribute.place) : "<empty>"
299
+ }}`
300
+ );
301
} else {
302
propItems.push(`...${printPlace(attribute.argument)}`);
303
}
@@ -313,12 +323,6 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
323
.join(", ")}]`;
324
break;
325
}
316
- case "NewExpression": {
317
- value = `New ${printPlace(instrValue.callee)}(${instrValue.args
318
- .map((arg) => printPlace(arg))
319
- .join(", ")})`;
320
- break;
321
- }
326
case "UnsupportedNode": {
327
value = `UnsupportedNode(${generate(instrValue.node).code})`;
328
break;
compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+1
-1
@@ -594,7 +594,7 @@ function codegenInstructionValue(
594
}
595
case "NewExpression": {
596
const callee = codegenPlace(cx, instrValue.callee);
597
- const args = instrValue.args.map((arg) => codegenPlace(cx, arg));
597
+ const args = instrValue.args.map((arg) => codegenArgument(cx, arg));
598
value = t.newExpression(callee, args);
599
break;
600
}
compiler/forget/src/__tests__/fixtures/compiler/error.todo-kitchensink.expect.md
+146
-156
@@ -14,7 +14,6 @@ function foo([a, b], { c, d, e = "e" }, f = "f", ...args) {
14
}
15
16
const g = { b() {}, c: () => {} };
17
- new c(...args);
17
const { z, aa = "aa" } = useCustom();
18
19
<Button xlink:href="localhost:3000"></Button>;
@@ -126,234 +125,225 @@ let moduleLocal = false;
125
11 |
126
> 12 | const g = { b() {}, c: () => {} };
127
| ^^^^^^
129
- 13 | new c(...args);
130
- 14 | const { z, aa = "aa" } = useCustom();
131
- 15 |
132
-
133
-[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle SpreadElement arguments in NewExpression
134
- 11 |
135
- 12 | const g = { b() {}, c: () => {} };
136
-> 13 | new c(...args);
137
- | ^^^^^^^
138
- 14 | const { z, aa = "aa" } = useCustom();
139
- 15 |
140
- 16 | <Button xlink:href="localhost:3000"></Button>;
128
+ 13 | const { z, aa = "aa" } = useCustom();
129
+ 14 |
130
+ 15 | <Button xlink:href="localhost:3000"></Button>;
131
132
[ReactForget] TodoError: (BuildHIR::lowerAssignment) Handle AssignmentPattern assignments
133
+ 11 |
134
12 | const g = { b() {}, c: () => {} };
144
- 13 | new c(...args);
145
-> 14 | const { z, aa = "aa" } = useCustom();
135
+> 13 | const { z, aa = "aa" } = useCustom();
136
| ^^^^^^^^^
147
- 15 |
148
- 16 | <Button xlink:href="localhost:3000"></Button>;
149
- 17 | <Button haha={1}></Button>;
137
+ 14 |
138
+ 15 | <Button xlink:href="localhost:3000"></Button>;
139
+ 16 | <Button haha={1}></Button>;
140
141
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle JSXNamespacedName attribute names in JSXElement
152
- 14 | const { z, aa = "aa" } = useCustom();
153
- 15 |
154
-> 16 | <Button xlink:href="localhost:3000"></Button>;
142
+ 13 | const { z, aa = "aa" } = useCustom();
143
+ 14 |
144
+> 15 | <Button xlink:href="localhost:3000"></Button>;
145
| ^^^^^^^^^^
156
- 17 | <Button haha={1}></Button>;
157
- 18 | <Button>{/** empty */}</Button>;
158
- 19 | <DesignSystem.Button />;
146
+ 16 | <Button haha={1}></Button>;
147
+ 17 | <Button>{/** empty */}</Button>;
148
+ 18 | <DesignSystem.Button />;
149
150
[ReactForget] TodoError: (BuildHIR::lowerJsxElement) Handle JSXEmptyExpression expressions
161
- 16 | <Button xlink:href="localhost:3000"></Button>;
162
- 17 | <Button haha={1}></Button>;
163
-> 18 | <Button>{/** empty */}</Button>;
151
+ 15 | <Button xlink:href="localhost:3000"></Button>;
152
+ 16 | <Button haha={1}></Button>;
153
+> 17 | <Button>{/** empty */}</Button>;
154
| ^^^^^^^^^^^^
165
- 19 | <DesignSystem.Button />;
166
- 20 |
167
- 21 | const j = function bar([quz, qux], ...args) {};
155
+ 18 | <DesignSystem.Button />;
156
+ 19 |
157
+ 20 | const j = function bar([quz, qux], ...args) {};
158
159
[ReactForget] TodoError: (BuildHIR::lower) Handle RestElement params
170
- 19 | <DesignSystem.Button />;
171
- 20 |
172
-> 21 | const j = function bar([quz, qux], ...args) {};
160
+ 18 | <DesignSystem.Button />;
161
+ 19 |
162
+> 20 | const j = function bar([quz, qux], ...args) {};
163
| ^^^^^^^
174
- 22 |
175
- 23 | for (; i < 3; i += 1) {
176
- 24 | x.push(i);
164
+ 21 |
165
+ 22 | for (; i < 3; i += 1) {
166
+ 23 | x.push(i);
167
168
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement
179
- 21 | const j = function bar([quz, qux], ...args) {};
180
- 22 |
181
-> 23 | for (; i < 3; i += 1) {
169
+ 20 | const j = function bar([quz, qux], ...args) {};
170
+ 21 |
171
+> 22 | for (; i < 3; i += 1) {
172
| ^
183
- 24 | x.push(i);
184
- 25 | }
185
- 26 | for (; i < 3; ) {
173
+ 23 | x.push(i);
174
+ 24 | }
175
+ 25 | for (; i < 3; ) {
176
177
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement
188
- 24 | x.push(i);
189
- 25 | }
190
-> 26 | for (; i < 3; ) {
178
+ 23 | x.push(i);
179
+ 24 | }
180
+> 25 | for (; i < 3; ) {
181
| ^
192
- 27 | break;
193
- 28 | }
194
- 29 | for (;;) {
182
+ 26 | break;
183
+ 27 | }
184
+ 28 | for (;;) {
185
186
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle empty update in ForStatement
197
- 24 | x.push(i);
198
- 25 | }
199
-> 26 | for (; i < 3; ) {
187
+ 23 | x.push(i);
188
+ 24 | }
189
+> 25 | for (; i < 3; ) {
190
| ^
201
- 27 | break;
202
- 28 | }
203
- 29 | for (;;) {
191
+ 26 | break;
192
+ 27 | }
193
+ 28 | for (;;) {
194
195
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement
206
- 27 | break;
207
- 28 | }
208
-> 29 | for (;;) {
196
+ 26 | break;
197
+ 27 | }
198
+> 28 | for (;;) {
199
| ^
210
- 30 | break;
211
- 31 | }
212
- 32 |
200
+ 29 | break;
201
+ 30 | }
202
+ 31 |
203
204
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle empty update in ForStatement
215
- 27 | break;
216
- 28 | }
217
-> 29 | for (;;) {
205
+ 26 | break;
206
+ 27 | }
207
+> 28 | for (;;) {
208
| ^
219
- 30 | break;
220
- 31 | }
221
- 32 |
209
+ 29 | break;
210
+ 30 | }
211
+ 31 |
212
213
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle empty test in ForStatement
224
- 27 | break;
225
- 28 | }
226
-> 29 | for (;;) {
214
+ 26 | break;
215
+ 27 | }
216
+> 28 | for (;;) {
217
| ^
228
- 30 | break;
229
- 31 | }
230
- 32 |
218
+ 29 | break;
219
+ 30 | }
220
+ 31 |
221
222
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle tagged template with interpolations
233
- 31 | }
234
- 32 |
235
-> 33 | graphql`
223
+ 30 | }
224
+ 31 |
225
+> 32 | graphql`
226
| ^
237
- 34 | ${g}
238
- 35 | `;
239
- 36 |
227
+ 33 | ${g}
228
+ 34 | `;
229
+ 35 |
230
231
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle tagged template where cooked value is different from raw value
242
- 35 | `;
243
- 36 |
244
-> 37 | graphql`\\t\n`;
232
+ 34 | `;
233
+ 35 |
234
+> 36 | graphql`\\t\n`;
235
| ^^^^^^^^^^^^^^
246
- 38 |
247
- 39 | for (const c of [1, 2]) {
248
- 40 | }
236
+ 37 |
237
+ 38 | for (const c of [1, 2]) {
238
+ 39 | }
239
240
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle ForOfStatement statements
251
- 37 | graphql`\\t\n`;
252
- 38 |
253
-> 39 | for (const c of [1, 2]) {
241
+ 36 | graphql`\\t\n`;
242
+ 37 |
243
+> 38 | for (const c of [1, 2]) {
244
| ^
255
- 40 | }
256
- 41 |
257
- 42 | for (let x in { a: 1 }) {
245
+ 39 | }
246
+ 40 |
247
+ 41 | for (let x in { a: 1 }) {
248
249
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle ForInStatement statements
260
- 40 | }
261
- 41 |
262
-> 42 | for (let x in { a: 1 }) {
250
+ 39 | }
251
+ 40 |
252
+> 41 | for (let x in { a: 1 }) {
253
| ^
264
- 43 | }
265
- 44 |
266
- 45 | let updateIdentifier = 0;
254
+ 42 | }
255
+ 43 |
256
+ 44 | let updateIdentifier = 0;
257
258
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle prefix UpdateExpression
269
- 44 |
270
- 45 | let updateIdentifier = 0;
271
-> 46 | --updateIdentifier;
259
+ 43 |
260
+ 44 | let updateIdentifier = 0;
261
+> 45 | --updateIdentifier;
262
| ^^^^^^^^^^^^^^^^^^
273
- 47 | ++updateIdentifier;
274
- 48 | updateIdentifier.y++;
275
- 49 | updateIdentifier.y--;
263
+ 46 | ++updateIdentifier;
264
+ 47 | updateIdentifier.y++;
265
+ 48 | updateIdentifier.y--;
266
267
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle prefix UpdateExpression
278
- 45 | let updateIdentifier = 0;
279
- 46 | --updateIdentifier;
280
-> 47 | ++updateIdentifier;
268
+ 44 | let updateIdentifier = 0;
269
+ 45 | --updateIdentifier;
270
+> 46 | ++updateIdentifier;
271
| ^^^^^^^^^^^^^^^^^^
282
- 48 | updateIdentifier.y++;
283
- 49 | updateIdentifier.y--;
284
- 50 |
272
+ 47 | updateIdentifier.y++;
273
+ 48 | updateIdentifier.y--;
274
+ 49 |
275
276
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle UpdateExpression with MemberExpression argument
287
- 46 | --updateIdentifier;
288
- 47 | ++updateIdentifier;
289
-> 48 | updateIdentifier.y++;
277
+ 45 | --updateIdentifier;
278
+ 46 | ++updateIdentifier;
279
+> 47 | updateIdentifier.y++;
280
| ^^^^^^^^^^^^^^^^^^^^
291
- 49 | updateIdentifier.y--;
292
- 50 |
293
- 51 | switch (i) {
281
+ 48 | updateIdentifier.y--;
282
+ 49 |
283
+ 50 | switch (i) {
284
285
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle UpdateExpression with MemberExpression argument
296
- 47 | ++updateIdentifier;
297
- 48 | updateIdentifier.y++;
298
-> 49 | updateIdentifier.y--;
286
+ 46 | ++updateIdentifier;
287
+ 47 | updateIdentifier.y++;
288
+> 48 | updateIdentifier.y--;
289
| ^^^^^^^^^^^^^^^^^^^^
300
- 50 |
301
- 51 | switch (i) {
302
- 52 | case 1 + 1: {
290
+ 49 |
291
+ 50 | switch (i) {
292
+ 51 | case 1 + 1: {
293
294
[ReactForget] TodoError: (BuildHIR::lowerStatement) Switch case test values must be identifiers or primitives, compound values are not yet supported
305
- 54 | case foo(): {
306
- 55 | }
307
-> 56 | case x.y: {
295
+ 53 | case foo(): {
296
+ 54 | }
297
+> 55 | case x.y: {
298
| ^^^
309
- 57 | }
310
- 58 | default: {
311
- 59 | }
299
+ 56 | }
300
+ 57 | default: {
301
+ 58 | }
302
303
[ReactForget] TodoError: (BuildHIR::lowerStatement) Switch case test values must be identifiers or primitives, compound values are not yet supported
314
- 52 | case 1 + 1: {
315
- 53 | }
316
-> 54 | case foo(): {
304
+ 51 | case 1 + 1: {
305
+ 52 | }
306
+> 53 | case foo(): {
307
| ^^^^^
318
- 55 | }
319
- 56 | case x.y: {
320
- 57 | }
308
+ 54 | }
309
+ 55 | case x.y: {
310
+ 56 | }
311
312
[ReactForget] TodoError: (BuildHIR::lowerStatement) Switch case test values must be identifiers or primitives, compound values are not yet supported
323
- 50 |
324
- 51 | switch (i) {
325
-> 52 | case 1 + 1: {
313
+ 49 |
314
+ 50 | switch (i) {
315
+> 51 | case 1 + 1: {
316
| ^^^^^
327
- 53 | }
328
- 54 | case foo(): {
329
- 55 | }
317
+ 52 | }
318
+ 53 | case foo(): {
319
+ 54 | }
320
321
[ReactForget] InvalidInputError: (BuildHIR::lowerAssignment) Assigning to an identifier defined outside the function scope is not supported.
332
- 61 |
333
- 62 | // Cannot assign to globals
334
-> 63 | someUnknownGlobal = true;
322
+ 60 |
323
+ 61 | // Cannot assign to globals
324
+> 62 | someUnknownGlobal = true;
325
| ^^^^^^^^^^^^^^^^^
336
- 64 | moduleLocal = true;
337
- 65 |
338
- 66 | function component(a) {
326
+ 63 | moduleLocal = true;
327
+ 64 |
328
+ 65 | function component(a) {
329
330
[ReactForget] InvalidInputError: (BuildHIR::lowerAssignment) Assigning to an identifier defined outside the function scope is not supported.
341
- 62 | // Cannot assign to globals
342
- 63 | someUnknownGlobal = true;
343
-> 64 | moduleLocal = true;
331
+ 61 | // Cannot assign to globals
332
+ 62 | someUnknownGlobal = true;
333
+> 63 | moduleLocal = true;
334
| ^^^^^^^^^^^
345
- 65 |
346
- 66 | function component(a) {
347
- 67 | // Add support for function declarations once we support `var` hoisting.
335
+ 64 |
336
+ 65 | function component(a) {
337
+ 66 | // Add support for function declarations once we support `var` hoisting.
338
339
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle FunctionDeclaration statements
350
- 64 | moduleLocal = true;
351
- 65 |
352
-> 66 | function component(a) {
340
+ 63 | moduleLocal = true;
341
+ 64 |
342
+> 65 | function component(a) {
343
| ^
354
- 67 | // Add support for function declarations once we support `var` hoisting.
355
- 68 | function t() {}
356
- 69 | t();
344
+ 66 | // Add support for function declarations once we support `var` hoisting.
345
+ 67 | function t() {}
346
+ 68 | t();
347
```
348
349
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/error.todo-kitchensink.js
-1
@@ -10,7 +10,6 @@ function foo([a, b], { c, d, e = "e" }, f = "f", ...args) {
10
}
11
12
const g = { b() {}, c: () => {} };
13
- new c(...args);
13
const { z, aa = "aa" } = useCustom();
14
15
<Button xlink:href="localhost:3000"></Button>;
compiler/forget/src/__tests__/fixtures/compiler/new-spread.expect.md
new
+33
@@ -0,0 +1,33 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component(props) {
6
+ const x = new Foo(...props.foo, null, ...[props.bar]);
7
+ return x;
8
+}
9
+
10
+```
11
+
12
+## Code
13
+
14
+```javascript
15
+function Component(props) {
16
+ const $ = React.unstable_useMemoCache(3);
17
+ const c_0 = $[0] !== props.bar;
18
+ const c_1 = $[1] !== props.foo;
19
+ let t0;
20
+ if (c_0 || c_1) {
21
+ t0 = new Foo(...props.foo, null, ...[props.bar]);
22
+ $[0] = props.bar;
23
+ $[1] = props.foo;
24
+ $[2] = t0;
25
+ } else {
26
+ t0 = $[2];
27
+ }
28
+ const x = t0;
29
+ return x;
30
+}
31
+
32
+```
33
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/new-spread.js
new
+4
@@ -0,0 +1,4 @@
1
+function Component(props) {
2
+ const x = new Foo(...props.foo, null, ...[props.bar]);
3
+ return x;
4
+}