diff --git a/compiler/forget/src/HIR/BuildHIR.ts b/compiler/forget/src/HIR/BuildHIR.ts index bb727db3b9..9aa8b70542 100644 --- a/compiler/forget/src/HIR/BuildHIR.ts +++ b/compiler/forget/src/HIR/BuildHIR.ts @@ -33,6 +33,7 @@ import { Place, ReturnTerminal, SourceLocation, + SpreadPattern, ThrowTerminal, } from "./HIR"; import HIRBuilder from "./HIRBuilder"; @@ -799,9 +800,48 @@ function lowerExpression( case "ObjectExpression": { const expr = exprPath as NodePath; const propertyPaths = expr.get("properties"); - const properties: Array = []; + const properties: Array = []; for (const propertyPath of propertyPaths) { - if (!propertyPath.isObjectProperty()) { + if (propertyPath.isObjectProperty()) { + const key = propertyPath.node.key; + let keyName: string; + if (key.type === "Identifier") { + keyName = key.name; + } else if (key.type === "StringLiteral") { + keyName = key.value; + } else { + builder.errors.push({ + reason: `(BuildHIR::lowerExpression) Expected Identifier, got ${key.type} key in ObjectExpression`, + severity: ErrorSeverity.InvalidInput, + nodePath: propertyPath, + }); + continue; + } + const valuePath = propertyPath.get("value"); + if (!valuePath.isExpression()) { + builder.errors.push({ + reason: `(BuildHIR::lowerExpression) Handle ${valuePath.type} values in ObjectExpression`, + severity: ErrorSeverity.Todo, + nodePath: valuePath, + }); + continue; + } + const value = lowerExpressionToTemporary(builder, valuePath); + properties.push({ + kind: "ObjectProperty", + name: keyName, + place: value, + }); + } else if (propertyPath.isSpreadElement()) { + const place = lowerExpressionToTemporary( + builder, + propertyPath.get("argument") + ); + properties.push({ + kind: "Spread", + place, + }); + } else { builder.errors.push({ reason: `(BuildHIR::lowerExpression) Handle ${propertyPath.type} properties in ObjectExpression`, severity: ErrorSeverity.Todo, @@ -809,35 +849,6 @@ function lowerExpression( }); continue; } - const key = propertyPath.node.key; - let keyName: string; - if (key.type === "Identifier") { - keyName = key.name; - } else if (key.type === "StringLiteral") { - keyName = key.value; - } else { - builder.errors.push({ - reason: `(BuildHIR::lowerExpression) Expected Identifier, got ${key.type} key in ObjectExpression`, - severity: ErrorSeverity.InvalidInput, - nodePath: propertyPath, - }); - continue; - } - const valuePath = propertyPath.get("value"); - if (!valuePath.isExpression()) { - builder.errors.push({ - reason: `(BuildHIR::lowerExpression) Handle ${valuePath.type} values in ObjectExpression`, - severity: ErrorSeverity.Todo, - nodePath: valuePath, - }); - continue; - } - const value = lowerExpressionToTemporary(builder, valuePath); - properties.push({ - kind: "ObjectProperty", - name: keyName, - place: value, - }); } return { kind: "ObjectExpression", diff --git a/compiler/forget/src/HIR/HIR.ts b/compiler/forget/src/HIR/HIR.ts index 6fb6a4d83b..2b3a56fa66 100644 --- a/compiler/forget/src/HIR/HIR.ts +++ b/compiler/forget/src/HIR/HIR.ts @@ -518,7 +518,7 @@ export type InstructionValue = } | { kind: "ObjectExpression"; - properties: Array; // null === empty object + properties: Array; loc: SourceLocation; } | { kind: "ArrayExpression"; elements: Array; loc: SourceLocation } diff --git a/compiler/forget/src/HIR/PrintHIR.ts b/compiler/forget/src/HIR/PrintHIR.ts index 2a356cdbe5..07f0d29d48 100644 --- a/compiler/forget/src/HIR/PrintHIR.ts +++ b/compiler/forget/src/HIR/PrintHIR.ts @@ -230,7 +230,11 @@ export function printInstructionValue(instrValue: ReactiveValue): string { const properties = []; if (instrValue.properties !== null) { for (const property of instrValue.properties) { - properties.push(`${property.name}: ${printPlace(property.place)}`); + if (property.kind === "ObjectProperty") { + properties.push(`${property.name}: ${printPlace(property.place)}`); + } else { + properties.push(`...${printPlace(property.place)}`); + } } } value = `Object { ${properties.join(", ")} }`; diff --git a/compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts b/compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts index 51412c663f..1db1c89ac1 100644 --- a/compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -586,12 +586,16 @@ function codegenInstructionValue( case "ObjectExpression": { const properties = []; for (const property of instrValue.properties) { - properties.push( - t.objectProperty( - t.stringLiteral(property.name), - codegenPlace(cx, property.place) - ) - ); + if (property.kind === "ObjectProperty") { + properties.push( + t.objectProperty( + t.stringLiteral(property.name), + codegenPlace(cx, property.place) + ) + ); + } else { + properties.push(t.spreadElement(codegenPlace(cx, property.place))); + } } value = t.objectExpression(properties); break; diff --git a/compiler/forget/src/__tests__/fixtures/hir/object-literal-spread-element.expect.md b/compiler/forget/src/__tests__/fixtures/hir/object-literal-spread-element.expect.md new file mode 100644 index 0000000000..fa0dbf5cb6 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/object-literal-spread-element.expect.md @@ -0,0 +1,31 @@ + +## Input + +```javascript +function Component(props) { + const x = { ...props.foo }; + return x; +} + +``` + +## Code + +```javascript +function Component(props) { + const $ = React.unstable_useMemoCache(2); + const c_0 = $[0] !== props.foo; + let t0; + if (c_0) { + t0 = { ...props.foo }; + $[0] = props.foo; + $[1] = t0; + } else { + t0 = $[1]; + } + const x = t0; + return x; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/object-literal-spread-element.js b/compiler/forget/src/__tests__/fixtures/hir/object-literal-spread-element.js new file mode 100644 index 0000000000..b9ad7b65c2 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/object-literal-spread-element.js @@ -0,0 +1,4 @@ +function Component(props) { + const x = { ...props.foo }; + return x; +}