Support complex computed properties in OptionalMemberExpression

Our previous lowering for OptionalMemberExpression reordered the evaluation of 
properties, such that we had to restrict the allowed properties to those that 
were safe for reordering. With the new representation we preserve order of 
evaluation, so we can relax the restriction. This unblocks a few cases in an 
internal product.
This commit is contained in:
Joe Savona
2023-05-03 17:10:28 -07:00
parent e192930cd4
commit 7ecee6a091
8 changed files with 134 additions and 35 deletions
+1 -10
View File
@@ -2176,16 +2176,7 @@ function lowerMemberExpression(
},
};
}
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);
} else {
property = lowerExpressionToTemporary(builder, propertyNode);
}
const property = lowerExpressionToTemporary(builder, propertyNode);
const value: InstructionValue = {
kind: "ComputedLoad",
object: { ...object },
@@ -1,25 +0,0 @@
## Input
```javascript
function Component(props) {
const object = makeObject(props);
return object?.[props.key];
}
```
## Error
```
[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 |
```
@@ -0,0 +1,43 @@
## Input
```javascript
function Component(props) {
const object = makeObject(props);
return object?.[props.key];
}
```
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
function Component(props) {
const $ = useMemoCache(5);
const c_0 = $[0] !== props;
let t0;
if (c_0) {
t0 = makeObject(props);
$[0] = props;
$[1] = t0;
} else {
t0 = $[1];
}
const object = t0;
const c_2 = $[2] !== object;
const c_3 = $[3] !== props;
let t1;
if (c_2 || c_3) {
t1 = object?.[props.key];
$[2] = object;
$[3] = props;
$[4] = t1;
} else {
t1 = $[4];
}
return t1;
}
```
@@ -0,0 +1,41 @@
## Input
```javascript
function Component(props) {
const x = makeObject();
return x?.[foo(props.value)];
}
```
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
function Component(props) {
const $ = useMemoCache(4);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = makeObject();
$[0] = t0;
} else {
t0 = $[0];
}
const x = t0;
const c_1 = $[1] !== props;
const c_2 = $[2] !== x;
let t1;
if (c_1 || c_2) {
t1 = x?.[foo(props.value)];
$[1] = props;
$[2] = x;
$[3] = t1;
} else {
t1 = $[3];
}
return t1;
}
```
@@ -0,0 +1,4 @@
function Component(props) {
const x = makeObject();
return x?.[foo(props.value)];
}
@@ -0,0 +1,41 @@
## Input
```javascript
function Component(props) {
const x = makeObject();
return x.y?.[props.a?.[props.b?.[props.c]]];
}
```
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
function Component(props) {
const $ = useMemoCache(4);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = makeObject();
$[0] = t0;
} else {
t0 = $[0];
}
const x = t0;
const c_1 = $[1] !== x.y;
const c_2 = $[2] !== props;
let t1;
if (c_1 || c_2) {
t1 = x.y?.[props.a?.[props.b?.[props.c]]];
$[1] = x.y;
$[2] = props;
$[3] = t1;
} else {
t1 = $[3];
}
return t1;
}
```
@@ -0,0 +1,4 @@
function Component(props) {
const x = makeObject();
return x.y?.[props.a?.[props.b?.[props.c]]];
}