[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
+};