@samitouri / QOS-React-2 / commits / 62b7526929

[hir] Handle OptionalMemberExpression syntax

(Implemented as per discussions with @gsathya ) Handle OptionalMemberExpression by adding an 'optional' flag to `PropertyLoad` instruction, which is set during `BuildHIR` and read during `CodegenReactiveFunction`.

mofeiZ committed Jan 25, 2023 at 10:16 UTC 62b75269294f9c1a39d6f6ea8c64363696efde40
5 files changed +72 -7
compiler/forget/src/HIR/BuildHIR.ts
+8 -2
@@ -1193,6 +1193,7 @@ function lowerExpression(
1193 object: { ...object },
1194 property: property.node.name,
1195 loc: leftExpr.node.loc ?? GeneratedSource,
1196 + optional: false, // LVal cannot be optional
1197 },
1198 loc: leftExpr.node.loc ?? GeneratedSource,
1199 });
@@ -1233,8 +1234,11 @@ function lowerExpression(
1234 }
1235 }
1236 }
1237 + case "OptionalMemberExpression":
1238 case "MemberExpression": {
1237 - const expr = exprPath as NodePath<t.MemberExpression>;
1239 + const expr = exprPath as NodePath<
1240 + t.MemberExpression | t.OptionalMemberExpression
1241 + >;
1242 const { value } = lowerMemberExpression(builder, expr);
1243 const place: Place = buildTemporaryPlace(builder, exprLoc);
1244 builder.push({
@@ -1424,7 +1428,7 @@ function lowerExpression(
1428
1429 function lowerMemberExpression(
1430 builder: HIRBuilder,
1427 - expr: NodePath<t.MemberExpression>
1431 + expr: NodePath<t.MemberExpression | t.OptionalMemberExpression>
1432 ): { object: Place; property: Place | string; value: InstructionValue } {
1433 const exprNode = expr.node;
1434 const exprLoc = exprNode.loc ?? GeneratedSource;
@@ -1448,6 +1452,7 @@ function lowerMemberExpression(
1452 object: { ...object },
1453 property: property.node.name,
1454 loc: exprLoc,
1455 + optional: t.isOptionalMemberExpression(expr),
1456 };
1457 return { object, property: property.node.name, value };
1458 } else {
@@ -1895,6 +1900,7 @@ function lowerAssignment(
1900 loc,
1901 object: { ...objectPlace },
1902 property: key.node.name,
1903 + optional: false, // Key of ObjectPattern (evaluation of LVal) cannot be optional.
1904 };
1905 lowerAssignment(builder, loc, kind, element, value);
1906 }
compiler/forget/src/HIR/HIR.ts
+1 -1
@@ -392,7 +392,7 @@ export type InstructionData =
392 // store `object.property = value`
393 | { kind: "PropertyStore"; object: Place; property: string; value: Place }
394 // load `object.property`
395 - | { kind: "PropertyLoad"; object: Place; property: string }
395 + | { kind: "PropertyLoad"; object: Place; property: string; optional: boolean }
396
397 // store `object[index] = value` - like PropertyStore but with a dynamic property
398 | { kind: "ComputedStore"; object: Place; property: Place; value: Place }
compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+13 -4
@@ -631,10 +631,19 @@ function codegenInstructionValue(
631 break;
632 }
633 case "PropertyLoad": {
634 - value = t.memberExpression(
635 - codegenPlace(temp, instrValue.object),
636 - t.identifier(instrValue.property)
637 - );
634 + if (instrValue.optional) {
635 + value = t.optionalMemberExpression(
636 + codegenPlace(temp, instrValue.object),
637 + t.identifier(instrValue.property),
638 + undefined,
639 + true
640 + );
641 + } else {
642 + value = t.memberExpression(
643 + codegenPlace(temp, instrValue.object),
644 + t.identifier(instrValue.property)
645 + );
646 + }
647 break;
648 }
649 case "ComputedStore": {
compiler/forget/src/__tests__/fixtures/hir/optional-member-expression.expect.md new
+43
@@ -0,0 +1,43 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Foo(props) {
6 + let x = bar(props.a);
7 + let y = x?.b;
8 +
9 + let z = useBar(y);
10 + return z;
11 +}
12 +
13 +```
14 +
15 +## Code
16 +
17 +```javascript
18 +function Foo(props) {
19 + const $ = React.useMemoCache();
20 + const c_0 = $[0] !== props.a;
21 + let x;
22 + if (c_0) {
23 + x = bar(props.a);
24 + $[0] = props.a;
25 + $[1] = x;
26 + } else {
27 + x = $[1];
28 + }
29 + const y = x?.b;
30 + const c_2 = $[2] !== y;
31 + let z;
32 + if (c_2) {
33 + z = useBar(y);
34 + $[2] = y;
35 + $[3] = z;
36 + } else {
37 + z = $[3];
38 + }
39 + return z;
40 +}
41 +
42 +```
43 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/optional-member-expression.js new
+7
@@ -0,0 +1,7 @@
1 +function Foo(props) {
2 + let x = bar(props.a);
3 + let y = x?.b;
4 +
5 + let z = useBar(y);
6 + return z;
7 +}