@samitouri / QOS-React-2 / commits / 93cca54aba

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