@samitouri / QOS-React-2 / commits / 7ecee6a091

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.

Joe Savona committed May 3, 2023 at 17:10 UTC 7ecee6a09148e3117c487438e4ef575bc6b569bc
8 files changed +134 -35
compiler/forget/src/HIR/BuildHIR.ts
+1 -10
@@ -2176,16 +2176,7 @@ function lowerMemberExpression(
2176 },
2177 };
2178 }
2179 - let property: Place;
2180 - // See "PropertyLoad" for the difference between optionalMemberExpr()
2181 - // and node.optional here
2182 - if (expr.isOptionalMemberExpression()) {
2183 - // if expr is in an optional chain, evaluation of `property` is
2184 - // conditional on whether expr is nullish
2185 - property = lowerReorderableExpression(builder, propertyNode);
2186 - } else {
2187 - property = lowerExpressionToTemporary(builder, propertyNode);
2188 - }
2179 + const property = lowerExpressionToTemporary(builder, propertyNode);
2180 const value: InstructionValue = {
2181 kind: "ComputedLoad",
2182 object: { ...object },
compiler/forget/src/__tests__/fixtures/compiler/error.optional-computed-member-expression.expect.md deleted
-25
@@ -1,25 +0,0 @@
1 -
2 -## Input
3 -
4 -```javascript
5 -function Component(props) {
6 - const object = makeObject(props);
7 - return object?.[props.key];
8 -}
9 -
10 -```
11 -
12 -
13 -## Error
14 -
15 -```
16 -[ReactForget] TodoError: (BuildHIR::node.lowerReorderableExpression) Expression type 'MemberExpression' cannot be safely reordered
17 - 1 | function Component(props) {
18 - 2 | const object = makeObject(props);
19 -> 3 | return object?.[props.key];
20 - | ^^^^^^^^^
21 - 4 | }
22 - 5 |
23 -```
24 -
25 -
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/optional-computed-member-expression.expect.md new
+43
@@ -0,0 +1,43 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + const object = makeObject(props);
7 + return object?.[props.key];
8 +}
9 +
10 +```
11 +
12 +## Code
13 +
14 +```javascript
15 +import { unstable_useMemoCache as useMemoCache } from "react";
16 +function Component(props) {
17 + const $ = useMemoCache(5);
18 + const c_0 = $[0] !== props;
19 + let t0;
20 + if (c_0) {
21 + t0 = makeObject(props);
22 + $[0] = props;
23 + $[1] = t0;
24 + } else {
25 + t0 = $[1];
26 + }
27 + const object = t0;
28 + const c_2 = $[2] !== object;
29 + const c_3 = $[3] !== props;
30 + let t1;
31 + if (c_2 || c_3) {
32 + t1 = object?.[props.key];
33 + $[2] = object;
34 + $[3] = props;
35 + $[4] = t1;
36 + } else {
37 + t1 = $[4];
38 + }
39 + return t1;
40 +}
41 +
42 +```
43 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/optional-computed-member-expression.js renamed
compiler/forget/src/__tests__/fixtures/compiler/optional-member-expression-call-as-property.expect.md new
+41
@@ -0,0 +1,41 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + const x = makeObject();
7 + return x?.[foo(props.value)];
8 +}
9 +
10 +```
11 +
12 +## Code
13 +
14 +```javascript
15 +import { unstable_useMemoCache as useMemoCache } from "react";
16 +function Component(props) {
17 + const $ = useMemoCache(4);
18 + let t0;
19 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
20 + t0 = makeObject();
21 + $[0] = t0;
22 + } else {
23 + t0 = $[0];
24 + }
25 + const x = t0;
26 + const c_1 = $[1] !== props;
27 + const c_2 = $[2] !== x;
28 + let t1;
29 + if (c_1 || c_2) {
30 + t1 = x?.[foo(props.value)];
31 + $[1] = props;
32 + $[2] = x;
33 + $[3] = t1;
34 + } else {
35 + t1 = $[3];
36 + }
37 + return t1;
38 +}
39 +
40 +```
41 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/optional-member-expression-call-as-property.js new
+4
@@ -0,0 +1,4 @@
1 +function Component(props) {
2 + const x = makeObject();
3 + return x?.[foo(props.value)];
4 +}
compiler/forget/src/__tests__/fixtures/compiler/optional-member-expression-with-optional-member-expr-as-property.expect.md new
+41
@@ -0,0 +1,41 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + const x = makeObject();
7 + return x.y?.[props.a?.[props.b?.[props.c]]];
8 +}
9 +
10 +```
11 +
12 +## Code
13 +
14 +```javascript
15 +import { unstable_useMemoCache as useMemoCache } from "react";
16 +function Component(props) {
17 + const $ = useMemoCache(4);
18 + let t0;
19 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
20 + t0 = makeObject();
21 + $[0] = t0;
22 + } else {
23 + t0 = $[0];
24 + }
25 + const x = t0;
26 + const c_1 = $[1] !== x.y;
27 + const c_2 = $[2] !== props;
28 + let t1;
29 + if (c_1 || c_2) {
30 + t1 = x.y?.[props.a?.[props.b?.[props.c]]];
31 + $[1] = x.y;
32 + $[2] = props;
33 + $[3] = t1;
34 + } else {
35 + t1 = $[3];
36 + }
37 + return t1;
38 +}
39 +
40 +```
41 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/optional-member-expression-with-optional-member-expr-as-property.js new
+4
@@ -0,0 +1,4 @@
1 +function Component(props) {
2 + const x = makeObject();
3 + return x.y?.[props.a?.[props.b?.[props.c]]];
4 +}