Support spread patterns in object literals
Support ObjectExpression with spread items, eg `const x = {...y}`.
Joe Savona committed
Mar 6, 2023 at 15:24 UTC
93cca54aba846663e24bfca2719796e7449765ad
6 files changed
+93
-39
compiler/forget/src/HIR/BuildHIR.ts
+42
-31
@@ -33,6 +33,7 @@ import {
33
Place,
34
ReturnTerminal,
35
SourceLocation,
36
+ SpreadPattern,
37
ThrowTerminal,
38
} from "./HIR";
39
import HIRBuilder from "./HIRBuilder";
@@ -799,45 +800,55 @@ function lowerExpression(
800
case "ObjectExpression": {
801
const expr = exprPath as NodePath<t.ObjectExpression>;
802
const propertyPaths = expr.get("properties");
802
- const properties: Array<ObjectProperty> = [];
803
+ const properties: Array<ObjectProperty | SpreadPattern> = [];
804
for (const propertyPath of propertyPaths) {
804
- if (!propertyPath.isObjectProperty()) {
805
- builder.errors.push({
806
- reason: `(BuildHIR::lowerExpression) Handle ${propertyPath.type} properties in ObjectExpression`,
807
- severity: ErrorSeverity.Todo,
808
- nodePath: propertyPath,
805
+ if (propertyPath.isObjectProperty()) {
806
+ const key = propertyPath.node.key;
807
+ let keyName: string;
808
+ if (key.type === "Identifier") {
809
+ keyName = key.name;
810
+ } else if (key.type === "StringLiteral") {
811
+ keyName = key.value;
812
+ } else {
813
+ builder.errors.push({
814
+ reason: `(BuildHIR::lowerExpression) Expected Identifier, got ${key.type} key in ObjectExpression`,
815
+ severity: ErrorSeverity.InvalidInput,
816
+ nodePath: propertyPath,
817
+ });
818
+ continue;
819
+ }
820
+ const valuePath = propertyPath.get("value");
821
+ if (!valuePath.isExpression()) {
822
+ builder.errors.push({
823
+ reason: `(BuildHIR::lowerExpression) Handle ${valuePath.type} values in ObjectExpression`,
824
+ severity: ErrorSeverity.Todo,
825
+ nodePath: valuePath,
826
+ });
827
+ continue;
828
+ }
829
+ const value = lowerExpressionToTemporary(builder, valuePath);
830
+ properties.push({
831
+ kind: "ObjectProperty",
832
+ name: keyName,
833
+ place: value,
834
});
810
- continue;
811
- }
812
- const key = propertyPath.node.key;
813
- let keyName: string;
814
- if (key.type === "Identifier") {
815
- keyName = key.name;
816
- } else if (key.type === "StringLiteral") {
817
- keyName = key.value;
818
- } else {
819
- builder.errors.push({
820
- reason: `(BuildHIR::lowerExpression) Expected Identifier, got ${key.type} key in ObjectExpression`,
821
- severity: ErrorSeverity.InvalidInput,
822
- nodePath: propertyPath,
835
+ } else if (propertyPath.isSpreadElement()) {
836
+ const place = lowerExpressionToTemporary(
837
+ builder,
838
+ propertyPath.get("argument")
839
+ );
840
+ properties.push({
841
+ kind: "Spread",
842
+ place,
843
});
824
- continue;
825
- }
826
- const valuePath = propertyPath.get("value");
827
- if (!valuePath.isExpression()) {
844
+ } else {
845
builder.errors.push({
829
- reason: `(BuildHIR::lowerExpression) Handle ${valuePath.type} values in ObjectExpression`,
846
+ reason: `(BuildHIR::lowerExpression) Handle ${propertyPath.type} properties in ObjectExpression`,
847
severity: ErrorSeverity.Todo,
831
- nodePath: valuePath,
848
+ nodePath: propertyPath,
849
});
850
continue;
851
}
835
- const value = lowerExpressionToTemporary(builder, valuePath);
836
- properties.push({
837
- kind: "ObjectProperty",
838
- name: keyName,
839
- place: value,
840
- });
852
}
853
return {
854
kind: "ObjectExpression",
compiler/forget/src/HIR/HIR.ts
+1
-1
@@ -518,7 +518,7 @@ export type InstructionValue =
518
}
519
| {
520
kind: "ObjectExpression";
521
- properties: Array<ObjectProperty>; // null === empty object
521
+ properties: Array<ObjectProperty | SpreadPattern>;
522
loc: SourceLocation;
523
}
524
| { kind: "ArrayExpression"; elements: Array<Place>; loc: SourceLocation }
compiler/forget/src/HIR/PrintHIR.ts
+5
-1
@@ -230,7 +230,11 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
230
const properties = [];
231
if (instrValue.properties !== null) {
232
for (const property of instrValue.properties) {
233
- properties.push(`${property.name}: ${printPlace(property.place)}`);
233
+ if (property.kind === "ObjectProperty") {
234
+ properties.push(`${property.name}: ${printPlace(property.place)}`);
235
+ } else {
236
+ properties.push(`...${printPlace(property.place)}`);
237
+ }
238
}
239
}
240
value = `Object { ${properties.join(", ")} }`;
compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+10
-6
@@ -586,12 +586,16 @@ function codegenInstructionValue(
586
case "ObjectExpression": {
587
const properties = [];
588
for (const property of instrValue.properties) {
589
- properties.push(
590
- t.objectProperty(
591
- t.stringLiteral(property.name),
592
- codegenPlace(cx, property.place)
593
- )
594
- );
589
+ if (property.kind === "ObjectProperty") {
590
+ properties.push(
591
+ t.objectProperty(
592
+ t.stringLiteral(property.name),
593
+ codegenPlace(cx, property.place)
594
+ )
595
+ );
596
+ } else {
597
+ properties.push(t.spreadElement(codegenPlace(cx, property.place)));
598
+ }
599
}
600
value = t.objectExpression(properties);
601
break;
compiler/forget/src/__tests__/fixtures/hir/object-literal-spread-element.expect.md
new
+31
@@ -0,0 +1,31 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component(props) {
6
+ const x = { ...props.foo };
7
+ return x;
8
+}
9
+
10
+```
11
+
12
+## Code
13
+
14
+```javascript
15
+function Component(props) {
16
+ const $ = React.unstable_useMemoCache(2);
17
+ const c_0 = $[0] !== props.foo;
18
+ let t0;
19
+ if (c_0) {
20
+ t0 = { ...props.foo };
21
+ $[0] = props.foo;
22
+ $[1] = t0;
23
+ } else {
24
+ t0 = $[1];
25
+ }
26
+ const x = t0;
27
+ return x;
28
+}
29
+
30
+```
31
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/object-literal-spread-element.js
new
+4
@@ -0,0 +1,4 @@
1
+function Component(props) {
2
+ const x = { ...props.foo };
3
+ return x;
4
+}