mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Model other assignment variants as values
The previous PR only updated simple assignment expressions (where the lvalue is an identifier), this PR extends the same idea to all assignment variants. Note that there is one case that doesn't work yet, which is complex destructuring assignment as a value: ```javascript let x = makeObject(); x.foo(([[x]] = makeObject())); ``` What happens here is that we lower the destructuring to a series of steps: ``` tmp1: Destructure Const [ tmp0 ] = makeObject(); tmp2: Destructure Reassign [ x ] = tmp0; PropertyCall x, 'foo', [ tmp1 ] ``` Thankfully we can detect this case: if we have a const/let declaration with an lvalue, that's invalid. See the new error test case which shows we correctly detect & reject this case for now.
This commit is contained in:
@@ -2029,13 +2029,20 @@ function lowerAssignment(
|
||||
});
|
||||
return { kind: "UnsupportedNode", node: lvalueNode, loc };
|
||||
}
|
||||
return {
|
||||
kind: "PropertyStore",
|
||||
object,
|
||||
property: property.node.name,
|
||||
value,
|
||||
const temporary = buildTemporaryPlace(builder, loc);
|
||||
builder.push({
|
||||
id: makeInstructionId(0),
|
||||
lvalue: { ...temporary },
|
||||
value: {
|
||||
kind: "PropertyStore",
|
||||
object,
|
||||
property: property.node.name,
|
||||
value,
|
||||
loc,
|
||||
},
|
||||
loc,
|
||||
};
|
||||
});
|
||||
return { kind: "LoadLocal", place: temporary, loc: temporary.loc };
|
||||
} else {
|
||||
if (!property.isExpression()) {
|
||||
builder.errors.push({
|
||||
@@ -2047,13 +2054,20 @@ function lowerAssignment(
|
||||
return { kind: "UnsupportedNode", node: lvalueNode, loc };
|
||||
}
|
||||
const propertyPlace = lowerExpressionToTemporary(builder, property);
|
||||
return {
|
||||
kind: "ComputedStore",
|
||||
object,
|
||||
property: propertyPlace,
|
||||
value,
|
||||
const temporary = buildTemporaryPlace(builder, loc);
|
||||
builder.push({
|
||||
id: makeInstructionId(0),
|
||||
lvalue: { ...temporary },
|
||||
value: {
|
||||
kind: "ComputedStore",
|
||||
object,
|
||||
property: propertyPlace,
|
||||
value,
|
||||
loc,
|
||||
},
|
||||
loc,
|
||||
};
|
||||
});
|
||||
return { kind: "LoadLocal", place: temporary, loc: temporary.loc };
|
||||
}
|
||||
}
|
||||
case "ArrayPattern": {
|
||||
@@ -2093,10 +2107,10 @@ function lowerAssignment(
|
||||
followups.push({ place: temp, path: element as NodePath<t.LVal> }); // TODO remove type cast
|
||||
}
|
||||
}
|
||||
const temp = buildTemporaryPlace(builder, loc);
|
||||
const temporary = buildTemporaryPlace(builder, loc);
|
||||
builder.push({
|
||||
id: makeInstructionId(0),
|
||||
lvalue: { ...temp },
|
||||
lvalue: { ...temporary },
|
||||
value: {
|
||||
kind: "Destructure",
|
||||
lvalue: {
|
||||
@@ -2114,7 +2128,7 @@ function lowerAssignment(
|
||||
for (const { place, path } of followups) {
|
||||
lowerAssignment(builder, path.node.loc ?? loc, kind, path, place);
|
||||
}
|
||||
return { kind: "LoadLocal", place: value, loc: value.loc };
|
||||
return { kind: "LoadLocal", place: temporary, loc: value.loc };
|
||||
}
|
||||
case "ObjectPattern": {
|
||||
const lvalue = lvaluePath as NodePath<t.ObjectPattern>;
|
||||
@@ -2187,10 +2201,10 @@ function lowerAssignment(
|
||||
}
|
||||
}
|
||||
}
|
||||
const temp = buildTemporaryPlace(builder, loc);
|
||||
const temporary = buildTemporaryPlace(builder, loc);
|
||||
builder.push({
|
||||
id: makeInstructionId(0),
|
||||
lvalue: { ...temp },
|
||||
lvalue: { ...temporary },
|
||||
value: {
|
||||
kind: "Destructure",
|
||||
lvalue: {
|
||||
@@ -2208,7 +2222,7 @@ function lowerAssignment(
|
||||
for (const { place, path } of followups) {
|
||||
lowerAssignment(builder, path.node.loc ?? loc, kind, path, place);
|
||||
}
|
||||
return { kind: "LoadLocal", place: value, loc: value.loc };
|
||||
return { kind: "LoadLocal", place: temporary, loc: value.loc };
|
||||
}
|
||||
default: {
|
||||
builder.errors.push({
|
||||
|
||||
@@ -393,11 +393,23 @@ function codegenInstructionNullable(
|
||||
}
|
||||
switch (kind) {
|
||||
case InstructionKind.Const: {
|
||||
if (instr.lvalue !== null) {
|
||||
CompilerError.invariant(
|
||||
`Const declaration cannot be referenced as an expression`,
|
||||
instr.value.loc
|
||||
);
|
||||
}
|
||||
return createVariableDeclaration(instr.loc, "const", [
|
||||
t.variableDeclarator(codegenLValue(lvalue), value),
|
||||
]);
|
||||
}
|
||||
case InstructionKind.Let: {
|
||||
if (instr.lvalue !== null) {
|
||||
CompilerError.invariant(
|
||||
`Const declaration cannot be referenced as an expression`,
|
||||
instr.value.loc
|
||||
);
|
||||
}
|
||||
return createVariableDeclaration(instr.loc, "let", [
|
||||
t.variableDeclarator(codegenLValue(lvalue), value),
|
||||
]);
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
let x = makeObject();
|
||||
x.foo((x = makeObject()));
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
const $ = React.unstable_useMemoCache(1);
|
||||
let x;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
x = makeObject();
|
||||
x.foo((x = makeObject()));
|
||||
$[0] = x;
|
||||
} else {
|
||||
x = $[0];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
function Component(props) {
|
||||
let x = makeObject();
|
||||
x.foo((x = makeObject()));
|
||||
return x;
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
let x = makeObject();
|
||||
x.foo(([x] = makeObject()));
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
const $ = React.unstable_useMemoCache(1);
|
||||
let x;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
x = makeObject();
|
||||
x.foo(([x] = makeObject()));
|
||||
$[0] = x;
|
||||
} else {
|
||||
x = $[0];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
function Component(props) {
|
||||
let x = makeObject();
|
||||
x.foo(([x] = makeObject()));
|
||||
return x;
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
let x = makeObject();
|
||||
x.foo(([[x]] = makeObject()));
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
[ReactForget] Invariant: Const declaration cannot be referenced as an expression (3:3)
|
||||
```
|
||||
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
function Component(props) {
|
||||
let x = makeObject();
|
||||
x.foo(([[x]] = makeObject()));
|
||||
return x;
|
||||
}
|
||||
Reference in New Issue
Block a user