@samitouri / QOS-React / commits / 50d2197dd5

[compiler] Support for member expression inc/decrement

Test Plan: Builds support for a.x++ and friends. Similar to a.x += y, emits it as an assignment expression. ghstack-source-id: 8f3979913aad561cdba70464c3cc5f0ee95887b5 Pull Request resolved: https://github.com/facebook/react/pull/30697

Mike Vitousek committed Aug 15, 2024 at 15:00 UTC 50d2197dd580afc0f87f0acb79c5528bc0f5199c
4 files changed +135 -4
compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts
+51
@@ -2385,6 +2385,57 @@ function lowerExpression(
2385 case 'UpdateExpression': {
2386 let expr = exprPath as NodePath<t.UpdateExpression>;
2387 const argument = expr.get('argument');
2388 + if (argument.isMemberExpression()) {
2389 + const binaryOperator = expr.node.operator === '++' ? '+' : '-';
2390 + const leftExpr = argument as NodePath<t.MemberExpression>;
2391 + const {object, property, value} = lowerMemberExpression(
2392 + builder,
2393 + leftExpr,
2394 + );
2395 +
2396 + // Store the previous value to a temporary
2397 + const previousValuePlace = lowerValueToTemporary(builder, value);
2398 + // Store the new value to a temporary
2399 + const updatedValue = lowerValueToTemporary(builder, {
2400 + kind: 'BinaryExpression',
2401 + operator: binaryOperator,
2402 + left: {...previousValuePlace},
2403 + right: lowerValueToTemporary(builder, {
2404 + kind: 'Primitive',
2405 + value: 1,
2406 + loc: GeneratedSource,
2407 + }),
2408 + loc: leftExpr.node.loc ?? GeneratedSource,
2409 + });
2410 +
2411 + // Save the result back to the property
2412 + let newValuePlace;
2413 + if (typeof property === 'string') {
2414 + newValuePlace = lowerValueToTemporary(builder, {
2415 + kind: 'PropertyStore',
2416 + object: {...object},
2417 + property,
2418 + value: {...updatedValue},
2419 + loc: leftExpr.node.loc ?? GeneratedSource,
2420 + });
2421 + } else {
2422 + newValuePlace = lowerValueToTemporary(builder, {
2423 + kind: 'ComputedStore',
2424 + object: {...object},
2425 + property: {...property},
2426 + value: {...updatedValue},
2427 + loc: leftExpr.node.loc ?? GeneratedSource,
2428 + });
2429 + }
2430 +
2431 + return {
2432 + kind: 'LoadLocal',
2433 + place: expr.node.prefix
2434 + ? {...newValuePlace}
2435 + : {...previousValuePlace},
2436 + loc: exprLoc,
2437 + };
2438 + }
2439 if (!argument.isIdentifier()) {
2440 builder.errors.push({
2441 reason: `(BuildHIR::lowerExpression) Handle UpdateExpression with ${argument.type} argument`,
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-kitchensink.expect.md
-4
@@ -104,10 +104,6 @@ Todo: (BuildHIR::lowerStatement) Handle ArrayPattern inits in ForOfStatement (38
104
105 Todo: (BuildHIR::lowerStatement) Handle ObjectPattern inits in ForOfStatement (40:40)
106
107 -Todo: (BuildHIR::lowerExpression) Handle UpdateExpression with MemberExpression argument (49:49)
108 -
109 -Todo: (BuildHIR::lowerExpression) Handle UpdateExpression with MemberExpression argument (50:50)
110 -
107 Todo: (BuildHIR::node.lowerReorderableExpression) Expression type `MemberExpression` cannot be safely reordered (57:57)
108
109 Todo: (BuildHIR::node.lowerReorderableExpression) Expression type `BinaryExpression` cannot be safely reordered (53:53)
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/member-inc.expect.md new
+63
@@ -0,0 +1,63 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +//@flow
6 +
7 +component Foo() {
8 + let x = {a: 1};
9 + x.a++;
10 + x.a--;
11 + console.log(++x.a);
12 + console.log(x.a++);
13 +
14 + console.log(x.a);
15 + let y = x.a++;
16 + console.log(y);
17 + console.log(x.a);
18 +
19 + console.log((++x.a).toString(), (x.a++).toString(), x.a);
20 +}
21 +
22 +export const FIXTURE_ENTRYPOINT = {
23 + fn: Foo,
24 + params: [],
25 +};
26 +
27 +```
28 +
29 +## Code
30 +
31 +```javascript
32 +function Foo() {
33 + const x = { a: 1 };
34 + x.a = x.a + 1;
35 + x.a = x.a - 1;
36 + console.log((x.a = x.a + 1));
37 + const t0 = x.a;
38 + x.a = t0 + 1;
39 + console.log(t0);
40 +
41 + console.log(x.a);
42 + const t1 = x.a;
43 + x.a = t1 + 1;
44 + const y = t1;
45 + console.log(y);
46 + console.log(x.a);
47 +
48 + const t2 = (x.a = x.a + 1).toString();
49 + const t3 = x.a;
50 + x.a = t3 + 1;
51 + console.log(t2, t3.toString(), x.a);
52 +}
53 +
54 +export const FIXTURE_ENTRYPOINT = {
55 + fn: Foo,
56 + params: [],
57 +};
58 +
59 +```
60 +
61 +### Eval output
62 +(kind: ok)
63 +logs: [2,2,3,3,4,'5','5',6]
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/member-inc.js new
+21
@@ -0,0 +1,21 @@
1 +//@flow
2 +
3 +component Foo() {
4 + let x = {a: 1};
5 + x.a++;
6 + x.a--;
7 + console.log(++x.a);
8 + console.log(x.a++);
9 +
10 + console.log(x.a);
11 + let y = x.a++;
12 + console.log(y);
13 + console.log(x.a);
14 +
15 + console.log((++x.a).toString(), (x.a++).toString(), x.a);
16 +}
17 +
18 +export const FIXTURE_ENTRYPOINT = {
19 + fn: Foo,
20 + params: [],
21 +};