[hir] Add support for string literal property keys

Mostly reuses existing analysis of an identifier property key. 

Adds a new type field to ObjectProperty to propogate the type of the key. This 
is used in codegen to correctly emit a string literal or an identifier.
This commit is contained in:
Sathya Gunasekaran
2023-09-07 13:56:56 +01:00
parent 59504e1cb4
commit f2027b9a4d
16 changed files with 353 additions and 64 deletions
@@ -967,6 +967,33 @@ function lowerStatement(
}
}
function lowerObjectPropertyKey(
builder: HIRBuilder,
key: t.PrivateName | t.Expression
): { name: string; type: "string" | "identifier" } | null {
if (key.type === "Identifier") {
return {
name: key.name,
type: "identifier",
};
}
if (key.type === "StringLiteral") {
return {
name: key.value,
type: "string",
};
}
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Expected Identifier, got ${key.type} key in ObjectExpression`,
severity: ErrorSeverity.Todo,
loc: key.loc ?? null,
suggestions: null,
});
return null;
}
function lowerExpression(
builder: HIRBuilder,
exprPath: NodePath<t.Expression>
@@ -1009,19 +1036,11 @@ function lowerExpression(
const properties: Array<ObjectProperty | SpreadPattern> = [];
for (const propertyPath of propertyPaths) {
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.Todo,
loc: propertyPath.node.loc ?? null,
suggestions: null,
});
const loweredKey = lowerObjectPropertyKey(
builder,
propertyPath.node.key
);
if (!loweredKey) {
continue;
}
const valuePath = propertyPath.get("value");
@@ -1037,8 +1056,8 @@ function lowerExpression(
const value = lowerExpressionToTemporary(builder, valuePath);
properties.push({
kind: "ObjectProperty",
name: keyName,
place: value,
...loweredKey,
});
} else if (propertyPath.isSpreadElement()) {
const place = lowerExpressionToTemporary(
@@ -2800,14 +2819,8 @@ function lowerAssignment(
});
continue;
}
const key = property.get("key");
if (!key.isIdentifier()) {
builder.errors.push({
reason: `(BuildHIR::lowerAssignment) Handle ${key.type} keys in ObjectPattern`,
severity: ErrorSeverity.Todo,
loc: key.node.loc ?? null,
suggestions: null,
});
const loweredKey = lowerObjectPropertyKey(builder, property.node.key);
if (!loweredKey) {
continue;
}
const element = property.get("value");
@@ -2832,8 +2845,8 @@ function lowerAssignment(
}
properties.push({
kind: "ObjectProperty",
name: key.node.name,
place: identifier,
...loweredKey,
});
} else {
const temp = buildTemporaryPlace(
@@ -2842,8 +2855,8 @@ function lowerAssignment(
);
properties.push({
kind: "ObjectProperty",
name: key.node.name,
place: { ...temp },
...loweredKey,
});
followups.push({ place: temp, path: element as NodePath<t.LVal> }); // TODO remove type cast
}
@@ -501,6 +501,7 @@ export type ObjectPattern = {
export type ObjectProperty = {
kind: "ObjectProperty";
type: "string" | "identifier";
name: string; // TODO: make a Place
place: Place;
};
@@ -16,6 +16,7 @@ import {
IdentifierId,
InstructionKind,
JsxAttribute,
ObjectProperty,
Pattern,
Place,
ReactiveBlock,
@@ -898,14 +899,14 @@ function codegenInstructionValue(
const properties = [];
for (const property of instrValue.properties) {
if (property.kind === "ObjectProperty") {
const key = t.identifier(property.name);
const key = codegenObjectPropertyKey(property);
const value = codegenPlace(cx, property.place);
properties.push(
t.objectProperty(
key,
value,
false,
value.type === "Identifier" && value.name === key.name
value.type === "Identifier" && value.name === property.name
)
);
} else {
@@ -1310,6 +1311,17 @@ function convertMemberExpressionToJsx(
}
}
function codegenObjectPropertyKey(
property: ObjectProperty
): t.StringLiteral | t.Identifier {
switch (property.type) {
case "identifier":
return t.identifier(property.name);
case "string":
return t.stringLiteral(property.name);
}
}
function codegenLValue(
pattern: Pattern | Place | SpreadPattern
): t.ArrayPattern | t.ObjectPattern | t.RestElement | t.Identifier {
@@ -1328,13 +1340,13 @@ function codegenLValue(
return t.objectPattern(
pattern.properties.map((property) => {
if (property.kind === "ObjectProperty") {
const key = t.identifier(property.name);
const key = codegenObjectPropertyKey(property);
const value = codegenLValue(property.place);
return t.objectProperty(
key,
value,
false,
value.type === "Identifier" && value.name === key.name
value.type === "Identifier" && value.name === property.name
);
} else {
return t.restElement(codegenLValue(property.place));
@@ -0,0 +1,53 @@
## Input
```javascript
function foo() {
const {
"data-foo-bar": x,
a: y,
data: z,
} = { "data-foo-bar": 1, a: 2, data: 3 };
return [x, y, z];
}
export const FIXTURE_ENTRYPOINT = {
fn: foo,
params: [],
isComponent: false,
};
```
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
function foo() {
const $ = useMemoCache(2);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = { "data-foo-bar": 1, a: 2, data: 3 };
$[0] = t0;
} else {
t0 = $[0];
}
const { "data-foo-bar": x, a: y, data: z } = t0;
let t1;
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
t1 = [x, y, z];
$[1] = t1;
} else {
t1 = $[1];
}
return t1;
}
export const FIXTURE_ENTRYPOINT = {
fn: foo,
params: [],
isComponent: false,
};
```
@@ -0,0 +1,14 @@
function foo() {
const {
"data-foo-bar": x,
a: y,
data: z,
} = { "data-foo-bar": 1, a: 2, data: 3 };
return [x, y, z];
}
export const FIXTURE_ENTRYPOINT = {
fn: foo,
params: [],
isComponent: false,
};
@@ -0,0 +1,32 @@
## Input
```javascript
function foo({ "data-foo-bar": dataTestID }) {
return dataTestID;
}
export const FIXTURE_ENTRYPOINT = {
fn: foo,
params: [{ "data-foo-bar": {} }],
isComponent: false,
};
```
## Code
```javascript
function foo(t5) {
const { "data-foo-bar": dataTestID } = t5;
return dataTestID;
}
export const FIXTURE_ENTRYPOINT = {
fn: foo,
params: [{ "data-foo-bar": {} }],
isComponent: false,
};
```
@@ -0,0 +1,9 @@
function foo({ "data-foo-bar": dataTestID }) {
return dataTestID;
}
export const FIXTURE_ENTRYPOINT = {
fn: foo,
params: [{ "data-foo-bar": {} }],
isComponent: false,
};
@@ -0,0 +1,32 @@
## Input
```javascript
function foo({ data: dataTestID }) {
return dataTestID;
}
export const FIXTURE_ENTRYPOINT = {
fn: foo,
params: [{ data: {} }],
isComponent: false,
};
```
## Code
```javascript
function foo(t5) {
const { data: dataTestID } = t5;
return dataTestID;
}
export const FIXTURE_ENTRYPOINT = {
fn: foo,
params: [{ data: {} }],
isComponent: false,
};
```
@@ -0,0 +1,9 @@
function foo({ data: dataTestID }) {
return dataTestID;
}
export const FIXTURE_ENTRYPOINT = {
fn: foo,
params: [{ data: {} }],
isComponent: false,
};
@@ -0,0 +1,42 @@
## Input
```javascript
function foo() {
const { "data-foo-bar": t } = { "data-foo-bar": 1 };
return t;
}
export const FIXTURE_ENTRYPOINT = {
fn: foo,
params: [],
isComponent: false,
};
```
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
function foo() {
const $ = useMemoCache(1);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = { "data-foo-bar": 1 };
$[0] = t0;
} else {
t0 = $[0];
}
const { "data-foo-bar": t } = t0;
return t;
}
export const FIXTURE_ENTRYPOINT = {
fn: foo,
params: [],
isComponent: false,
};
```
@@ -0,0 +1,10 @@
function foo() {
const { "data-foo-bar": t } = { "data-foo-bar": 1 };
return t;
}
export const FIXTURE_ENTRYPOINT = {
fn: foo,
params: [],
isComponent: false,
};
@@ -0,0 +1,42 @@
## Input
```javascript
function foo() {
const { data: t } = { data: 1 };
return t;
}
export const FIXTURE_ENTRYPOINT = {
fn: foo,
params: [],
isComponent: false,
};
```
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
function foo() {
const $ = useMemoCache(1);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = { data: 1 };
$[0] = t0;
} else {
t0 = $[0];
}
const { data: t } = t0;
return t;
}
export const FIXTURE_ENTRYPOINT = {
fn: foo,
params: [],
isComponent: false,
};
```
@@ -0,0 +1,10 @@
function foo() {
const { data: t } = { data: 1 };
return t;
}
export const FIXTURE_ENTRYPOINT = {
fn: foo,
params: [],
isComponent: false,
};
@@ -1,36 +0,0 @@
## Input
```javascript
function Foo() {
return {
"a.b": 1,
"a\b": 2,
"a/b": 3,
"a+b": 4,
"a b": 5,
};
}
export const FIXTURE_ENTRYPOINT = {
fn: Foo,
params: [],
isComponent: false,
};
```
## Error
```
Unexpected token, expected "," (2:13)
1 | import { unstable_useMemoCache as useMemoCache } from "react";function Foo() {const $ = useMemoCache(1);let t0;if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
> 2 | t0 = { a.b: 1, a: 2, a/b: 3, a+b: 4, a b: 5 };$[0] = t0;} else {t0 = $[0];}return t0;}
| ^
3 |
4 |
5 |
```
@@ -0,0 +1,46 @@
## Input
```javascript
function Foo() {
return {
"a.b": 1,
"a\b": 2,
"a/b": 3,
"a+b": 4,
"a b": 5,
};
}
export const FIXTURE_ENTRYPOINT = {
fn: Foo,
params: [],
isComponent: false,
};
```
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
function Foo() {
const $ = useMemoCache(1);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = { "a.b": 1, "a\b": 2, "a/b": 3, "a+b": 4, "a b": 5 };
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}
export const FIXTURE_ENTRYPOINT = {
fn: Foo,
params: [],
isComponent: false,
};
```