@samitouri / QOS-React-2 / commits / 3e84c870f6

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 +}