mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[hir] Allow reorderable exprs in optional computed load
This commit is contained in:
@@ -1680,9 +1680,11 @@ function lowerExpression(
|
||||
}
|
||||
|
||||
/**
|
||||
* There are a few places where we do not preserve original evaluation ordering, such as switch case test values
|
||||
* and default values in destructuring (assignment patterns). In these cases we allow simple expressions whose
|
||||
* evaluation cannot be observed: primitives and arrays/objects whose values are also safely reorderable.
|
||||
* There are a few places where we do not preserve original evaluation ordering and/or control flow, such as
|
||||
* switch case test values and default values in destructuring (assignment patterns). In these cases we allow
|
||||
* simple expressions whose evaluation cannot be observed:
|
||||
* - primitives
|
||||
* - arrays/objects whose values are also safely reorderable.
|
||||
*/
|
||||
function lowerReorderableExpression(
|
||||
builder: HIRBuilder,
|
||||
@@ -1864,21 +1866,28 @@ function lowerMemberExpression(
|
||||
},
|
||||
};
|
||||
}
|
||||
if (t.isOptionalMemberExpression(expr)) {
|
||||
builder.errors.push({
|
||||
reason: `(BuildHIR::lowerMemberExpression) Handle computed OptionalMemberExpression`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
nodePath: expr,
|
||||
});
|
||||
let optional;
|
||||
let property: Place;
|
||||
|
||||
// See "PropertyLoad" for the difference between optionalMemberExpr()
|
||||
// and node.optional here
|
||||
if (expr.isOptionalMemberExpression()) {
|
||||
// if expr is in an optional chain, evaluation of `property` is
|
||||
// conditional on whether expr is nullish
|
||||
property = lowerReorderableExpression(builder, propertyNode);
|
||||
optional = expr.node.optional ?? false;
|
||||
} else {
|
||||
property = lowerExpressionToTemporary(builder, propertyNode);
|
||||
optional = false;
|
||||
}
|
||||
const propertyPlace = lowerExpressionToTemporary(builder, propertyNode);
|
||||
const value: InstructionValue = {
|
||||
kind: "ComputedLoad",
|
||||
object: { ...object },
|
||||
property: { ...propertyPlace },
|
||||
property: { ...property },
|
||||
loc: exprLoc,
|
||||
optional,
|
||||
};
|
||||
return { object, property: propertyPlace, value };
|
||||
return { object, property, value };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -609,6 +609,7 @@ export type InstructionValue =
|
||||
kind: "ComputedLoad";
|
||||
object: Place;
|
||||
property: Place;
|
||||
optional: boolean;
|
||||
loc: SourceLocation;
|
||||
}
|
||||
// `delete object[property]`
|
||||
|
||||
@@ -367,9 +367,9 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
|
||||
break;
|
||||
}
|
||||
case "ComputedLoad": {
|
||||
value = `ComputedLoad ${printPlace(instrValue.object)}[${printPlace(
|
||||
instrValue.property
|
||||
)}]`;
|
||||
value = `ComputedLoad ${printPlace(instrValue.object)}${
|
||||
instrValue.optional ? "?" : ""
|
||||
}[${printPlace(instrValue.property)}]`;
|
||||
break;
|
||||
}
|
||||
case "ComputedStore": {
|
||||
|
||||
@@ -160,7 +160,7 @@ function evaluateInstruction(
|
||||
loc: value.loc,
|
||||
property: property.value,
|
||||
object: value.object,
|
||||
optional: false,
|
||||
optional: value.optional,
|
||||
};
|
||||
// Future-proofing: when we add support for optional computed properties,
|
||||
// we'll need to copy the value here
|
||||
|
||||
@@ -795,11 +795,18 @@ function codegenInstructionValue(
|
||||
break;
|
||||
}
|
||||
case "ComputedLoad": {
|
||||
value = t.memberExpression(
|
||||
codegenPlace(cx, instrValue.object),
|
||||
codegenPlace(cx, instrValue.property),
|
||||
true
|
||||
);
|
||||
const object = codegenPlace(cx, instrValue.object);
|
||||
const property = codegenPlace(cx, instrValue.property);
|
||||
if (t.isOptionalMemberExpression(object) || instrValue.optional) {
|
||||
value = t.optionalMemberExpression(
|
||||
object,
|
||||
property,
|
||||
true,
|
||||
instrValue.optional
|
||||
);
|
||||
} else {
|
||||
value = t.memberExpression(object, property, true, instrValue.optional);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "ComputedDelete": {
|
||||
|
||||
+2
-2
@@ -13,11 +13,11 @@ function Component(props) {
|
||||
## Error
|
||||
|
||||
```
|
||||
[ReactForget] TodoError: (BuildHIR::lowerMemberExpression) Handle computed OptionalMemberExpression
|
||||
[ReactForget] TodoError: (BuildHIR::node.lowerReorderableExpression) Expression type 'MemberExpression' cannot be safely reordered
|
||||
1 | function Component(props) {
|
||||
2 | const object = makeObject(props);
|
||||
> 3 | return object?.[props.key];
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^
|
||||
4 | }
|
||||
5 |
|
||||
```
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
let x = a?.b.c[0];
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
const x = a?.b.c[0];
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
function Component(props) {
|
||||
let x = a?.b.c[0];
|
||||
return x;
|
||||
}
|
||||
Reference in New Issue
Block a user