mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Object and array destructuring support in declaration/assignment
Implements support for array and object de-structuring in variable declarations and assignment expressions. Note that the code currently makes the overly optimistic assumption that the RHS is an array or object that can be safely indexed into. The correct representation would instead treat the RHS as possibly iterable, but we need to consider the appropriate representation. I think it's worth landing a first optimistic pass and we can iterate forward, this helps make it more clear what the ideal representation would have to be and should make a bunch of examples work. It also allows us to experiment with representations of, and handling for, scope dependencies that involve computed property access.
This commit is contained in:
@@ -623,10 +623,6 @@ function lowerStatement(
|
||||
nodeKind === "let" ? InstructionKind.Let : InstructionKind.Const;
|
||||
for (const declaration of stmt.get("declarations")) {
|
||||
const id = declaration.get("id");
|
||||
invariant(
|
||||
id.isIdentifier(),
|
||||
"Support non-identifier variable declarations"
|
||||
);
|
||||
const init = declaration.get("init");
|
||||
let value: InstructionValue;
|
||||
if (init.hasNode()) {
|
||||
@@ -942,45 +938,13 @@ function lowerExpression(
|
||||
|
||||
if (operator === "=") {
|
||||
const left = expr.get("left");
|
||||
const leftNode = left.node;
|
||||
switch (leftNode.type) {
|
||||
case "Identifier": {
|
||||
const lvalue = left as NodePath<t.Identifier>;
|
||||
return lowerAssignment(
|
||||
builder,
|
||||
leftNode.loc ?? GeneratedSource,
|
||||
InstructionKind.Reassign,
|
||||
lvalue,
|
||||
lowerExpression(builder, expr.get("right"))
|
||||
);
|
||||
}
|
||||
case "MemberExpression": {
|
||||
const leftExpr = left as NodePath<t.MemberExpression>;
|
||||
const property = leftExpr.get("property");
|
||||
invariant(
|
||||
property.isIdentifier(),
|
||||
"Assignment expression to dynamic properties is not yet supported"
|
||||
);
|
||||
const right = lowerExpressionToPlace(builder, expr.get("right"));
|
||||
const object = lowerExpressionToPlace(
|
||||
builder,
|
||||
leftExpr.get("object")
|
||||
);
|
||||
return {
|
||||
kind: "PropertyStore",
|
||||
object,
|
||||
property: property.node.name,
|
||||
value: right,
|
||||
loc: leftNode.loc ?? GeneratedSource,
|
||||
};
|
||||
}
|
||||
default: {
|
||||
todoInvariant(
|
||||
false,
|
||||
"Support lvalues other than identifier and member expression"
|
||||
);
|
||||
}
|
||||
}
|
||||
return lowerAssignment(
|
||||
builder,
|
||||
left.node.loc ?? GeneratedSource,
|
||||
InstructionKind.Reassign,
|
||||
left,
|
||||
lowerExpression(builder, expr.get("right"))
|
||||
);
|
||||
}
|
||||
|
||||
const operators: { [key: string]: t.BinaryExpression["operator"] } = {
|
||||
@@ -1399,15 +1363,131 @@ function lowerAssignment(
|
||||
builder: HIRBuilder,
|
||||
loc: SourceLocation,
|
||||
kind: InstructionKind,
|
||||
lvalue: NodePath<t.Identifier>,
|
||||
lvaluePath: NodePath<t.LVal>,
|
||||
value: InstructionValue
|
||||
): InstructionValue {
|
||||
const id = lowerIdentifier(builder, lvalue);
|
||||
builder.push({
|
||||
id: makeInstructionId(0),
|
||||
lvalue: { place: id, kind },
|
||||
value,
|
||||
loc,
|
||||
});
|
||||
return id;
|
||||
const lvalueNode = lvaluePath.node;
|
||||
switch (lvalueNode.type) {
|
||||
case "Identifier": {
|
||||
const lvalue = lvaluePath as NodePath<t.Identifier>;
|
||||
const place = lowerIdentifier(builder, lvalue);
|
||||
builder.push({
|
||||
id: makeInstructionId(0),
|
||||
lvalue: { place: { ...place }, kind },
|
||||
value,
|
||||
loc,
|
||||
});
|
||||
return place;
|
||||
}
|
||||
case "MemberExpression": {
|
||||
const leftExpr = lvaluePath as NodePath<t.MemberExpression>;
|
||||
const property = leftExpr.get("property");
|
||||
invariant(
|
||||
property.isIdentifier(),
|
||||
"Assignment expression to dynamic properties is not yet supported"
|
||||
);
|
||||
const object = lowerExpressionToPlace(builder, leftExpr.get("object"));
|
||||
let valuePlace: Place;
|
||||
if (value.kind === "Identifier") {
|
||||
valuePlace = value;
|
||||
} else {
|
||||
valuePlace = buildTemporaryPlace(builder, loc);
|
||||
builder.push({
|
||||
id: makeInstructionId(0),
|
||||
lvalue: { place: { ...valuePlace }, kind: InstructionKind.Const },
|
||||
value,
|
||||
loc,
|
||||
});
|
||||
}
|
||||
return {
|
||||
kind: "PropertyStore",
|
||||
object,
|
||||
property: property.node.name,
|
||||
value: valuePlace,
|
||||
loc,
|
||||
};
|
||||
}
|
||||
case "ArrayPattern": {
|
||||
const lvalue = lvaluePath as NodePath<t.ArrayPattern>;
|
||||
const arrayPlace = buildTemporaryPlace(builder, loc);
|
||||
builder.push({
|
||||
id: makeInstructionId(0),
|
||||
lvalue: { place: { ...arrayPlace }, kind: InstructionKind.Const },
|
||||
value,
|
||||
loc,
|
||||
});
|
||||
const elements = lvalue.get("elements");
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
const element = elements[i];
|
||||
if (!element.hasNode()) {
|
||||
continue;
|
||||
}
|
||||
todoInvariant(
|
||||
element.node.type !== "RestElement",
|
||||
"Rest elements are not supported yet"
|
||||
);
|
||||
const property = buildTemporaryPlace(
|
||||
builder,
|
||||
element.node.loc ?? GeneratedSource
|
||||
);
|
||||
builder.push({
|
||||
id: makeInstructionId(0),
|
||||
lvalue: { place: { ...property }, kind: InstructionKind.Const },
|
||||
value: {
|
||||
kind: "Primitive",
|
||||
value: i,
|
||||
loc: element.node.loc ?? GeneratedSource,
|
||||
},
|
||||
loc: element.node.loc ?? GeneratedSource,
|
||||
});
|
||||
const value: InstructionValue = {
|
||||
kind: "IndexLoad",
|
||||
loc,
|
||||
object: { ...arrayPlace },
|
||||
property,
|
||||
};
|
||||
lowerAssignment(builder, loc, kind, element, value);
|
||||
}
|
||||
return arrayPlace;
|
||||
}
|
||||
case "ObjectPattern": {
|
||||
const lvalue = lvaluePath as NodePath<t.ObjectPattern>;
|
||||
const objectPlace = buildTemporaryPlace(builder, loc);
|
||||
builder.push({
|
||||
id: makeInstructionId(0),
|
||||
lvalue: { place: { ...objectPlace }, kind },
|
||||
value,
|
||||
loc,
|
||||
});
|
||||
const properties = lvalue.get("properties");
|
||||
for (let i = 0; i < properties.length; i++) {
|
||||
const property = properties[i];
|
||||
invariant(
|
||||
property.isObjectProperty(),
|
||||
"Rest elements are not supported yet"
|
||||
);
|
||||
const key = property.get("key");
|
||||
invariant(
|
||||
key.isIdentifier(),
|
||||
"TODO: support non-identifier object property keys"
|
||||
);
|
||||
const element = property.get("value");
|
||||
invariant(
|
||||
element.isLVal(),
|
||||
"Expected object property value to be an lvalue"
|
||||
);
|
||||
const value: InstructionValue = {
|
||||
kind: "PropertyLoad",
|
||||
loc,
|
||||
object: { ...objectPlace },
|
||||
property: key.node.name,
|
||||
};
|
||||
lowerAssignment(builder, loc, kind, element, value);
|
||||
}
|
||||
return objectPlace;
|
||||
}
|
||||
default: {
|
||||
todo("Support other lvalue types beyond identifier");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function foo(a, b, c) {
|
||||
let d, g, n, o;
|
||||
[
|
||||
d,
|
||||
[
|
||||
{
|
||||
e: { f: g },
|
||||
},
|
||||
],
|
||||
] = a;
|
||||
({
|
||||
l: {
|
||||
m: [[n]],
|
||||
},
|
||||
o,
|
||||
} = b);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
function foo(a, b, c) {
|
||||
const d = undefined;
|
||||
const g = undefined;
|
||||
const n = undefined;
|
||||
const o = undefined;
|
||||
const d$0 = a[0];
|
||||
const g$1 = a[1][0].e.f;
|
||||
const n$2 = b.l.m[0][0];
|
||||
const o$3 = b.o;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
function foo(a, b, c) {
|
||||
let d, g, n, o;
|
||||
[
|
||||
d,
|
||||
[
|
||||
{
|
||||
e: { f: g },
|
||||
},
|
||||
],
|
||||
] = a;
|
||||
({
|
||||
l: {
|
||||
m: [[n]],
|
||||
},
|
||||
o,
|
||||
} = b);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function foo(a, b, c) {
|
||||
const [
|
||||
d,
|
||||
[
|
||||
{
|
||||
e: { f },
|
||||
},
|
||||
],
|
||||
] = a;
|
||||
const {
|
||||
l: {
|
||||
m: [[n]],
|
||||
},
|
||||
o,
|
||||
} = b;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
function foo(a, b, c) {
|
||||
const d = a[0];
|
||||
const f = a[1][0].e.f;
|
||||
const n = b.l.m[0][0];
|
||||
const o = b.o;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
function foo(a, b, c) {
|
||||
const [
|
||||
d,
|
||||
[
|
||||
{
|
||||
e: { f },
|
||||
},
|
||||
],
|
||||
] = a;
|
||||
const {
|
||||
l: {
|
||||
m: [[n]],
|
||||
},
|
||||
o,
|
||||
} = b;
|
||||
}
|
||||
Reference in New Issue
Block a user