@samitouri / QOS-React / commits / aded0ef831

[compiler] Handle member expr as computed property (#31344)

This PR loosens the restriction on the types of computed properties we can handle. Previously, we would disallow anything that is not an identifier because non-identifiers could be mutating. But member expressions are not mutating so we can treat them similar to identifiers.

Sathya Gunasekaran committed Oct 28, 2024 at 13:10 UTC aded0ef831e100a5ee9d9e674cb0482326279118
5 files changed +122 -1
compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts
+1 -1
@@ -1418,7 +1418,7 @@ function lowerObjectPropertyKey(
1418 name: key.node.value,
1419 };
1420 } else if (property.node.computed && key.isExpression()) {
1421 - if (!key.isIdentifier()) {
1421 + if (!key.isIdentifier() && !key.isMemberExpression()) {
1422 /*
1423 * NOTE: allowing complex key expressions can trigger a bug where a mutation is made conditional
1424 * see fixture
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-object-expression-member-expr-call.expect.md new
+37
@@ -0,0 +1,37 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import {identity, mutate, mutateAndReturn} from 'shared-runtime';
6 +
7 +function Component(props) {
8 + const obj = {mutateAndReturn};
9 + const key = {};
10 + const context = {
11 + [obj.mutateAndReturn(key)]: identity([props.value]),
12 + };
13 + mutate(key);
14 + return context;
15 +}
16 +
17 +export const FIXTURE_ENTRYPOINT = {
18 + fn: Component,
19 + params: [{value: 42}],
20 +};
21 +
22 +```
23 +
24 +
25 +## Error
26 +
27 +```
28 + 5 | const key = {};
29 + 6 | const context = {
30 +> 7 | [obj.mutateAndReturn(key)]: identity([props.value]),
31 + | ^^^^^^^^^^^^^^^^^^^^^^^^ Todo: (BuildHIR::lowerExpression) Expected Identifier, got CallExpression key in ObjectExpression (7:7)
32 + 8 | };
33 + 9 | mutate(key);
34 + 10 | return context;
35 +```
36 +
37 +
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-object-expression-member-expr-call.js new
+16
@@ -0,0 +1,16 @@
1 +import {identity, mutate, mutateAndReturn} from 'shared-runtime';
2 +
3 +function Component(props) {
4 + const obj = {mutateAndReturn};
5 + const key = {};
6 + const context = {
7 + [obj.mutateAndReturn(key)]: identity([props.value]),
8 + };
9 + mutate(key);
10 + return context;
11 +}
12 +
13 +export const FIXTURE_ENTRYPOINT = {
14 + fn: Component,
15 + params: [{value: 42}],
16 +};
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-expression-computed-member.expect.md new
+53
@@ -0,0 +1,53 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import {identity, mutate, mutateAndReturn} from 'shared-runtime';
6 +
7 +function Component(props) {
8 + const key = {a: 'key'};
9 + const context = {
10 + [key.a]: identity([props.value]),
11 + };
12 + mutate(key);
13 + return context;
14 +}
15 +
16 +export const FIXTURE_ENTRYPOINT = {
17 + fn: Component,
18 + params: [{value: 42}],
19 +};
20 +
21 +```
22 +
23 +## Code
24 +
25 +```javascript
26 +import { c as _c } from "react/compiler-runtime";
27 +import { identity, mutate, mutateAndReturn } from "shared-runtime";
28 +
29 +function Component(props) {
30 + const $ = _c(2);
31 + let context;
32 + if ($[0] !== props.value) {
33 + const key = { a: "key" };
34 + context = { [key.a]: identity([props.value]) };
35 +
36 + mutate(key);
37 + $[0] = props.value;
38 + $[1] = context;
39 + } else {
40 + context = $[1];
41 + }
42 + return context;
43 +}
44 +
45 +export const FIXTURE_ENTRYPOINT = {
46 + fn: Component,
47 + params: [{ value: 42 }],
48 +};
49 +
50 +```
51 +
52 +### Eval output
53 +(kind: ok) {"key":[42]}
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-expression-computed-member.js new
+15
@@ -0,0 +1,15 @@
1 +import {identity, mutate, mutateAndReturn} from 'shared-runtime';
2 +
3 +function Component(props) {
4 + const key = {a: 'key'};
5 + const context = {
6 + [key.a]: identity([props.value]),
7 + };
8 + mutate(key);
9 + return context;
10 +}
11 +
12 +export const FIXTURE_ENTRYPOINT = {
13 + fn: Component,
14 + params: [{value: 42}],
15 +};