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).
This commit is contained in:
Joe Savona
2023-03-20 12:55:14 -07:00
parent ff8868e8d1
commit 3e84c870f6
8 changed files with 230 additions and 219 deletions
+34 -53
View File
@@ -905,18 +905,7 @@ function lowerExpression(
return { kind: "UnsupportedNode", node: exprNode, loc: exprLoc };
}
const callee = lowerExpressionToTemporary(builder, calleePath);
let args: Place[] = [];
for (const argPath of expr.get("arguments")) {
if (!argPath.isExpression()) {
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Handle ${argPath.type} arguments in NewExpression`,
severity: ErrorSeverity.Todo,
nodePath: argPath,
});
continue;
}
args.push(lowerExpressionToTemporary(builder, argPath));
}
const args = lowerArguments(builder, expr.get("arguments"));
return {
kind: "NewExpression",
@@ -938,26 +927,7 @@ function lowerExpression(
}
if (calleePath.isMemberExpression()) {
const { object, property } = lowerMemberExpression(builder, calleePath);
let args: Array<Place | SpreadPattern> = [];
for (const argPath of expr.get("arguments")) {
if (argPath.isSpreadElement()) {
args.push({
kind: "Spread",
place: lowerExpressionToTemporary(
builder,
argPath.get("argument")
),
});
} else if (argPath.isExpression()) {
args.push(lowerExpressionToTemporary(builder, argPath));
} else {
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Handle ${argPath.type} arguments in CallExpression`,
severity: ErrorSeverity.Todo,
nodePath: argPath,
});
}
}
const args = lowerArguments(builder, expr.get("arguments"));
if (typeof property === "string") {
return {
kind: "PropertyCall",
@@ -977,27 +947,7 @@ function lowerExpression(
}
} else {
const callee = lowerExpressionToTemporary(builder, calleePath);
let args: Array<Place | SpreadPattern> = [];
for (const argPath of expr.get("arguments")) {
if (argPath.isSpreadElement()) {
args.push({
kind: "Spread",
place: lowerExpressionToTemporary(
builder,
argPath.get("argument")
),
});
} else if (argPath.isExpression()) {
args.push(lowerExpressionToTemporary(builder, argPath));
} else {
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Handle ${argPath.type} arguments in CallExpression`,
severity: ErrorSeverity.Todo,
nodePath: argPath,
});
continue;
}
}
const args = lowerArguments(builder, expr.get("arguments"));
return {
kind: "CallExpression",
callee,
@@ -1695,6 +1645,37 @@ function lowerExpression(
}
}
function lowerArguments(
builder: HIRBuilder,
expr: Array<
NodePath<
| t.Expression
| t.SpreadElement
| t.JSXNamespacedName
| t.ArgumentPlaceholder
>
>
): Array<Place | SpreadPattern> {
let args: Array<Place | SpreadPattern> = [];
for (const argPath of expr) {
if (argPath.isSpreadElement()) {
args.push({
kind: "Spread",
place: lowerExpressionToTemporary(builder, argPath.get("argument")),
});
} else if (argPath.isExpression()) {
args.push(lowerExpressionToTemporary(builder, argPath));
} else {
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Handle ${argPath.type} arguments in CallExpression`,
severity: ErrorSeverity.Todo,
nodePath: argPath,
});
}
}
return args;
}
function lowerMemberExpression(
builder: HIRBuilder,
expr: NodePath<t.MemberExpression | t.OptionalMemberExpression>
+1 -1
View File
@@ -479,7 +479,7 @@ export type InstructionValue =
| {
kind: "NewExpression";
callee: Place;
args: Array<Place>;
args: Array<Place | SpreadPattern>;
loc: SourceLocation;
}
| {
+11 -7
View File
@@ -256,6 +256,12 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
} ${printPlace(instrValue.right)}`;
break;
}
case "NewExpression": {
value = `New ${printPlace(instrValue.callee)}(${instrValue.args
.map((arg) => printPattern(arg))
.join(", ")})`;
break;
}
case "CallExpression": {
value = `Call ${printPlace(instrValue.callee)}(${instrValue.args
.map((arg) => printPattern(arg))
@@ -287,7 +293,11 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
const propItems = [];
for (const attribute of instrValue.props) {
if (attribute.kind === "JsxAttribute") {
propItems.push(`${attribute.name}={${printPlace(attribute.place)}}`);
propItems.push(
`${attribute.name}={${
attribute.place !== null ? printPlace(attribute.place) : "<empty>"
}}`
);
} else {
propItems.push(`...${printPlace(attribute.argument)}`);
}
@@ -313,12 +323,6 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
.join(", ")}]`;
break;
}
case "NewExpression": {
value = `New ${printPlace(instrValue.callee)}(${instrValue.args
.map((arg) => printPlace(arg))
.join(", ")})`;
break;
}
case "UnsupportedNode": {
value = `UnsupportedNode(${generate(instrValue.node).code})`;
break;
@@ -594,7 +594,7 @@ function codegenInstructionValue(
}
case "NewExpression": {
const callee = codegenPlace(cx, instrValue.callee);
const args = instrValue.args.map((arg) => codegenPlace(cx, arg));
const args = instrValue.args.map((arg) => codegenArgument(cx, arg));
value = t.newExpression(callee, args);
break;
}
@@ -14,7 +14,6 @@ function foo([a, b], { c, d, e = "e" }, f = "f", ...args) {
}
const g = { b() {}, c: () => {} };
new c(...args);
const { z, aa = "aa" } = useCustom();
<Button xlink:href="localhost:3000"></Button>;
@@ -126,234 +125,225 @@ let moduleLocal = false;
11 |
> 12 | const g = { b() {}, c: () => {} };
| ^^^^^^
13 | new c(...args);
14 | const { z, aa = "aa" } = useCustom();
15 |
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle SpreadElement arguments in NewExpression
11 |
12 | const g = { b() {}, c: () => {} };
> 13 | new c(...args);
| ^^^^^^^
14 | const { z, aa = "aa" } = useCustom();
15 |
16 | <Button xlink:href="localhost:3000"></Button>;
13 | const { z, aa = "aa" } = useCustom();
14 |
15 | <Button xlink:href="localhost:3000"></Button>;
[ReactForget] TodoError: (BuildHIR::lowerAssignment) Handle AssignmentPattern assignments
11 |
12 | const g = { b() {}, c: () => {} };
13 | new c(...args);
> 14 | const { z, aa = "aa" } = useCustom();
> 13 | const { z, aa = "aa" } = useCustom();
| ^^^^^^^^^
15 |
16 | <Button xlink:href="localhost:3000"></Button>;
17 | <Button haha={1}></Button>;
14 |
15 | <Button xlink:href="localhost:3000"></Button>;
16 | <Button haha={1}></Button>;
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle JSXNamespacedName attribute names in JSXElement
14 | const { z, aa = "aa" } = useCustom();
15 |
> 16 | <Button xlink:href="localhost:3000"></Button>;
13 | const { z, aa = "aa" } = useCustom();
14 |
> 15 | <Button xlink:href="localhost:3000"></Button>;
| ^^^^^^^^^^
17 | <Button haha={1}></Button>;
18 | <Button>{/** empty */}</Button>;
19 | <DesignSystem.Button />;
16 | <Button haha={1}></Button>;
17 | <Button>{/** empty */}</Button>;
18 | <DesignSystem.Button />;
[ReactForget] TodoError: (BuildHIR::lowerJsxElement) Handle JSXEmptyExpression expressions
16 | <Button xlink:href="localhost:3000"></Button>;
17 | <Button haha={1}></Button>;
> 18 | <Button>{/** empty */}</Button>;
15 | <Button xlink:href="localhost:3000"></Button>;
16 | <Button haha={1}></Button>;
> 17 | <Button>{/** empty */}</Button>;
| ^^^^^^^^^^^^
19 | <DesignSystem.Button />;
20 |
21 | const j = function bar([quz, qux], ...args) {};
18 | <DesignSystem.Button />;
19 |
20 | const j = function bar([quz, qux], ...args) {};
[ReactForget] TodoError: (BuildHIR::lower) Handle RestElement params
19 | <DesignSystem.Button />;
20 |
> 21 | const j = function bar([quz, qux], ...args) {};
18 | <DesignSystem.Button />;
19 |
> 20 | const j = function bar([quz, qux], ...args) {};
| ^^^^^^^
22 |
23 | for (; i < 3; i += 1) {
24 | x.push(i);
21 |
22 | for (; i < 3; i += 1) {
23 | x.push(i);
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement
21 | const j = function bar([quz, qux], ...args) {};
22 |
> 23 | for (; i < 3; i += 1) {
20 | const j = function bar([quz, qux], ...args) {};
21 |
> 22 | for (; i < 3; i += 1) {
| ^
24 | x.push(i);
25 | }
26 | for (; i < 3; ) {
23 | x.push(i);
24 | }
25 | for (; i < 3; ) {
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement
24 | x.push(i);
25 | }
> 26 | for (; i < 3; ) {
23 | x.push(i);
24 | }
> 25 | for (; i < 3; ) {
| ^
27 | break;
28 | }
29 | for (;;) {
26 | break;
27 | }
28 | for (;;) {
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle empty update in ForStatement
24 | x.push(i);
25 | }
> 26 | for (; i < 3; ) {
23 | x.push(i);
24 | }
> 25 | for (; i < 3; ) {
| ^
27 | break;
28 | }
29 | for (;;) {
26 | break;
27 | }
28 | for (;;) {
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement
27 | break;
28 | }
> 29 | for (;;) {
26 | break;
27 | }
> 28 | for (;;) {
| ^
30 | break;
31 | }
32 |
29 | break;
30 | }
31 |
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle empty update in ForStatement
27 | break;
28 | }
> 29 | for (;;) {
26 | break;
27 | }
> 28 | for (;;) {
| ^
30 | break;
31 | }
32 |
29 | break;
30 | }
31 |
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle empty test in ForStatement
27 | break;
28 | }
> 29 | for (;;) {
26 | break;
27 | }
> 28 | for (;;) {
| ^
30 | break;
31 | }
32 |
29 | break;
30 | }
31 |
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle tagged template with interpolations
31 | }
32 |
> 33 | graphql`
30 | }
31 |
> 32 | graphql`
| ^
34 | ${g}
35 | `;
36 |
33 | ${g}
34 | `;
35 |
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle tagged template where cooked value is different from raw value
35 | `;
36 |
> 37 | graphql`\\t\n`;
34 | `;
35 |
> 36 | graphql`\\t\n`;
| ^^^^^^^^^^^^^^
38 |
39 | for (const c of [1, 2]) {
40 | }
37 |
38 | for (const c of [1, 2]) {
39 | }
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle ForOfStatement statements
37 | graphql`\\t\n`;
38 |
> 39 | for (const c of [1, 2]) {
36 | graphql`\\t\n`;
37 |
> 38 | for (const c of [1, 2]) {
| ^
40 | }
41 |
42 | for (let x in { a: 1 }) {
39 | }
40 |
41 | for (let x in { a: 1 }) {
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle ForInStatement statements
40 | }
41 |
> 42 | for (let x in { a: 1 }) {
39 | }
40 |
> 41 | for (let x in { a: 1 }) {
| ^
43 | }
44 |
45 | let updateIdentifier = 0;
42 | }
43 |
44 | let updateIdentifier = 0;
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle prefix UpdateExpression
44 |
45 | let updateIdentifier = 0;
> 46 | --updateIdentifier;
43 |
44 | let updateIdentifier = 0;
> 45 | --updateIdentifier;
| ^^^^^^^^^^^^^^^^^^
47 | ++updateIdentifier;
48 | updateIdentifier.y++;
49 | updateIdentifier.y--;
46 | ++updateIdentifier;
47 | updateIdentifier.y++;
48 | updateIdentifier.y--;
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle prefix UpdateExpression
45 | let updateIdentifier = 0;
46 | --updateIdentifier;
> 47 | ++updateIdentifier;
44 | let updateIdentifier = 0;
45 | --updateIdentifier;
> 46 | ++updateIdentifier;
| ^^^^^^^^^^^^^^^^^^
48 | updateIdentifier.y++;
49 | updateIdentifier.y--;
50 |
47 | updateIdentifier.y++;
48 | updateIdentifier.y--;
49 |
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle UpdateExpression with MemberExpression argument
46 | --updateIdentifier;
47 | ++updateIdentifier;
> 48 | updateIdentifier.y++;
45 | --updateIdentifier;
46 | ++updateIdentifier;
> 47 | updateIdentifier.y++;
| ^^^^^^^^^^^^^^^^^^^^
49 | updateIdentifier.y--;
50 |
51 | switch (i) {
48 | updateIdentifier.y--;
49 |
50 | switch (i) {
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle UpdateExpression with MemberExpression argument
47 | ++updateIdentifier;
48 | updateIdentifier.y++;
> 49 | updateIdentifier.y--;
46 | ++updateIdentifier;
47 | updateIdentifier.y++;
> 48 | updateIdentifier.y--;
| ^^^^^^^^^^^^^^^^^^^^
50 |
51 | switch (i) {
52 | case 1 + 1: {
49 |
50 | switch (i) {
51 | case 1 + 1: {
[ReactForget] TodoError: (BuildHIR::lowerStatement) Switch case test values must be identifiers or primitives, compound values are not yet supported
54 | case foo(): {
55 | }
> 56 | case x.y: {
53 | case foo(): {
54 | }
> 55 | case x.y: {
| ^^^
57 | }
58 | default: {
59 | }
56 | }
57 | default: {
58 | }
[ReactForget] TodoError: (BuildHIR::lowerStatement) Switch case test values must be identifiers or primitives, compound values are not yet supported
52 | case 1 + 1: {
53 | }
> 54 | case foo(): {
51 | case 1 + 1: {
52 | }
> 53 | case foo(): {
| ^^^^^
55 | }
56 | case x.y: {
57 | }
54 | }
55 | case x.y: {
56 | }
[ReactForget] TodoError: (BuildHIR::lowerStatement) Switch case test values must be identifiers or primitives, compound values are not yet supported
50 |
51 | switch (i) {
> 52 | case 1 + 1: {
49 |
50 | switch (i) {
> 51 | case 1 + 1: {
| ^^^^^
53 | }
54 | case foo(): {
55 | }
52 | }
53 | case foo(): {
54 | }
[ReactForget] InvalidInputError: (BuildHIR::lowerAssignment) Assigning to an identifier defined outside the function scope is not supported.
61 |
62 | // Cannot assign to globals
> 63 | someUnknownGlobal = true;
60 |
61 | // Cannot assign to globals
> 62 | someUnknownGlobal = true;
| ^^^^^^^^^^^^^^^^^
64 | moduleLocal = true;
65 |
66 | function component(a) {
63 | moduleLocal = true;
64 |
65 | function component(a) {
[ReactForget] InvalidInputError: (BuildHIR::lowerAssignment) Assigning to an identifier defined outside the function scope is not supported.
62 | // Cannot assign to globals
63 | someUnknownGlobal = true;
> 64 | moduleLocal = true;
61 | // Cannot assign to globals
62 | someUnknownGlobal = true;
> 63 | moduleLocal = true;
| ^^^^^^^^^^^
65 |
66 | function component(a) {
67 | // Add support for function declarations once we support `var` hoisting.
64 |
65 | function component(a) {
66 | // Add support for function declarations once we support `var` hoisting.
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle FunctionDeclaration statements
64 | moduleLocal = true;
65 |
> 66 | function component(a) {
63 | moduleLocal = true;
64 |
> 65 | function component(a) {
| ^
67 | // Add support for function declarations once we support `var` hoisting.
68 | function t() {}
69 | t();
66 | // Add support for function declarations once we support `var` hoisting.
67 | function t() {}
68 | t();
```
@@ -10,7 +10,6 @@ function foo([a, b], { c, d, e = "e" }, f = "f", ...args) {
}
const g = { b() {}, c: () => {} };
new c(...args);
const { z, aa = "aa" } = useCustom();
<Button xlink:href="localhost:3000"></Button>;
@@ -0,0 +1,33 @@
## Input
```javascript
function Component(props) {
const x = new Foo(...props.foo, null, ...[props.bar]);
return x;
}
```
## Code
```javascript
function Component(props) {
const $ = React.unstable_useMemoCache(3);
const c_0 = $[0] !== props.bar;
const c_1 = $[1] !== props.foo;
let t0;
if (c_0 || c_1) {
t0 = new Foo(...props.foo, null, ...[props.bar]);
$[0] = props.bar;
$[1] = props.foo;
$[2] = t0;
} else {
t0 = $[2];
}
const x = t0;
return x;
}
```
@@ -0,0 +1,4 @@
function Component(props) {
const x = new Foo(...props.foo, null, ...[props.bar]);
return x;
}