@samitouri / QOS-React-2 / commits / dcb6549dda

[hir] Bailout when reading from React namespace

Forget doesn't understand the React namespace object and generates incorrect code when compiling code that loads props from this namespace object. This PR makes Forget bailout when we see a property load from React namespace object.

Sathya Gunasekaran committed Jul 25, 2023 at 16:08 UTC dcb6549ddae8c10a826503222d6bd7c2dfb770a4
9 files changed +106 -155
compiler/forget/packages/babel-plugin-react-forget/src/HIR/BuildHIR.ts
+14
@@ -2215,6 +2215,20 @@ function lowerMemberExpression(
2215 const object =
2216 loweredObject ?? lowerExpressionToTemporary(builder, objectNode);
2217
2218 + if (objectNode.isIdentifier() && objectNode.node.name === "React") {
2219 + builder.errors.push({
2220 + reason: `(BuildHIR::lowerMemberExpression) Handle loading properties from React namespace`,
2221 + severity: ErrorSeverity.Todo,
2222 + loc: propertyNode.node.loc ?? null,
2223 + suggestions: null,
2224 + });
2225 + return {
2226 + object,
2227 + property: propertyNode.toString(),
2228 + value: { kind: "UnsupportedNode", node: exprNode, loc: exprLoc },
2229 + };
2230 + }
2231 +
2232 if (!expr.node.computed) {
2233 if (!propertyNode.isIdentifier()) {
2234 builder.errors.push({
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/_bug.hooks-with-React-namespace.expect.md deleted
-30
@@ -1,30 +0,0 @@
1 -
2 -## Input
3 -
4 -```javascript
5 -function Foo() {
6 - const [x, setX] = React.useState(1);
7 - return x;
8 -}
9 -
10 -```
11 -
12 -## Code
13 -
14 -```javascript
15 -import { unstable_useMemoCache as useMemoCache } from "react";
16 -function Foo() {
17 - const $ = useMemoCache(1);
18 - let t0;
19 - if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
20 - t0 = React.useState(1);
21 - $[0] = t0;
22 - } else {
23 - t0 = $[0];
24 - }
25 - const [x] = t0;
26 - return x;
27 -}
28 -
29 -```
30 -
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/babel-existing-react-kitchensink-import.expect.md
+38 -50
@@ -3,18 +3,18 @@
3
4 ```javascript
5 import * as React from "react";
6 -import { useState } from "react";
6 +import { useState, useMemo } from "react";
7
8 function Component(props) {
9 const [x] = useState(0);
10 - const expensiveNumber = React.useMemo(() => calculateExpensiveNumber(x), [x]);
10 + const expensiveNumber = useMemo(() => calculateExpensiveNumber(x), [x]);
11
12 return <div>{expensiveNumber}</div>;
13 }
14
15 function Component2(props) {
16 const [x] = useState(0);
17 - const expensiveNumber = React.useMemo(() => calculateExpensiveNumber(x), [x]);
17 + const expensiveNumber = useMemo(() => calculateExpensiveNumber(x), [x]);
18
19 return <div>{expensiveNumber}</div>;
20 }
@@ -25,74 +25,62 @@ function Component2(props) {
25
26 ```javascript
27 import * as React from "react";
28 -import { useState, unstable_useMemoCache as useMemoCache } from "react";
28 +import {
29 + useState,
30 + useMemo,
31 + unstable_useMemoCache as useMemoCache,
32 +} from "react";
33
34 function Component(props) {
31 - const $ = useMemoCache(6);
35 + const $ = useMemoCache(4);
36 const [x] = useState(0);
37 const c_0 = $[0] !== x;
34 - let t1;
38 + let t0;
39 if (c_0) {
36 - const c_2 = $[2] !== x;
37 - let t0;
38 - if (c_2) {
39 - t0 = () => calculateExpensiveNumber(x);
40 - $[2] = x;
41 - $[3] = t0;
42 - } else {
43 - t0 = $[3];
44 - }
45 - t1 = React.useMemo(t0, [x]);
40 + t0 = calculateExpensiveNumber(x);
41 $[0] = x;
47 - $[1] = t1;
42 + $[1] = t0;
43 } else {
49 - t1 = $[1];
44 + t0 = $[1];
45 }
51 - const expensiveNumber = t1;
52 - const c_4 = $[4] !== expensiveNumber;
53 - let t2;
54 - if (c_4) {
55 - t2 = <div>{expensiveNumber}</div>;
56 - $[4] = expensiveNumber;
57 - $[5] = t2;
46 + const t15 = t0;
47 + const expensiveNumber = t15;
48 + const c_2 = $[2] !== expensiveNumber;
49 + let t1;
50 + if (c_2) {
51 + t1 = <div>{expensiveNumber}</div>;
52 + $[2] = expensiveNumber;
53 + $[3] = t1;
54 } else {
59 - t2 = $[5];
55 + t1 = $[3];
56 }
61 - return t2;
57 + return t1;
58 }
59
60 function Component2(props) {
65 - const $ = useMemoCache(6);
61 + const $ = useMemoCache(4);
62 const [x] = useState(0);
63 const c_0 = $[0] !== x;
68 - let t1;
64 + let t0;
65 if (c_0) {
70 - const c_2 = $[2] !== x;
71 - let t0;
72 - if (c_2) {
73 - t0 = () => calculateExpensiveNumber(x);
74 - $[2] = x;
75 - $[3] = t0;
76 - } else {
77 - t0 = $[3];
78 - }
79 - t1 = React.useMemo(t0, [x]);
66 + t0 = calculateExpensiveNumber(x);
67 $[0] = x;
81 - $[1] = t1;
68 + $[1] = t0;
69 } else {
83 - t1 = $[1];
70 + t0 = $[1];
71 }
85 - const expensiveNumber = t1;
86 - const c_4 = $[4] !== expensiveNumber;
87 - let t2;
88 - if (c_4) {
89 - t2 = <div>{expensiveNumber}</div>;
90 - $[4] = expensiveNumber;
91 - $[5] = t2;
72 + const t15 = t0;
73 + const expensiveNumber = t15;
74 + const c_2 = $[2] !== expensiveNumber;
75 + let t1;
76 + if (c_2) {
77 + t1 = <div>{expensiveNumber}</div>;
78 + $[2] = expensiveNumber;
79 + $[3] = t1;
80 } else {
93 - t2 = $[5];
81 + t1 = $[3];
82 }
95 - return t2;
83 + return t1;
84 }
85
86 ```
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/babel-existing-react-kitchensink-import.js
+3 -3
@@ -1,16 +1,16 @@
1 import * as React from "react";
2 -import { useState } from "react";
2 +import { useState, useMemo } from "react";
3
4 function Component(props) {
5 const [x] = useState(0);
6 - const expensiveNumber = React.useMemo(() => calculateExpensiveNumber(x), [x]);
6 + const expensiveNumber = useMemo(() => calculateExpensiveNumber(x), [x]);
7
8 return <div>{expensiveNumber}</div>;
9 }
10
11 function Component2(props) {
12 const [x] = useState(0);
13 - const expensiveNumber = React.useMemo(() => calculateExpensiveNumber(x), [x]);
13 + const expensiveNumber = useMemo(() => calculateExpensiveNumber(x), [x]);
14
15 return <div>{expensiveNumber}</div>;
16 }
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/babel-existing-react-namespace-import.expect.md deleted
-72
@@ -1,72 +0,0 @@
1 -
2 -## Input
3 -
4 -```javascript
5 -import * as React from "react";
6 -
7 -function Component(props) {
8 - const [x] = React.useState(0);
9 - const expensiveNumber = React.useMemo(() => calculateExpensiveNumber(x), [x]);
10 -
11 - return <div>{expensiveNumber}</div>;
12 -}
13 -
14 -function Component2(props) {
15 - const [x] = React.useState(0);
16 - const expensiveNumber = React.useMemo(() => calculateExpensiveNumber(x), [x]);
17 -
18 - return <div>{expensiveNumber}</div>;
19 -}
20 -
21 -```
22 -
23 -## Code
24 -
25 -```javascript
26 -import { unstable_useMemoCache as useMemoCache } from "react";
27 -import * as React from "react";
28 -
29 -function Component(props) {
30 - const $ = useMemoCache(2);
31 - let t0;
32 - if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
33 - const [x] = React.useState(0);
34 - t0 = React.useMemo(() => calculateExpensiveNumber(x), [x]);
35 - $[0] = t0;
36 - } else {
37 - t0 = $[0];
38 - }
39 - const expensiveNumber = t0;
40 - let t1;
41 - if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
42 - t1 = <div>{expensiveNumber}</div>;
43 - $[1] = t1;
44 - } else {
45 - t1 = $[1];
46 - }
47 - return t1;
48 -}
49 -
50 -function Component2(props) {
51 - const $ = useMemoCache(2);
52 - let t0;
53 - if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
54 - const [x] = React.useState(0);
55 - t0 = React.useMemo(() => calculateExpensiveNumber(x), [x]);
56 - $[0] = t0;
57 - } else {
58 - t0 = $[0];
59 - }
60 - const expensiveNumber = t0;
61 - let t1;
62 - if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
63 - t1 = <div>{expensiveNumber}</div>;
64 - $[1] = t1;
65 - } else {
66 - t1 = $[1];
67 - }
68 - return t1;
69 -}
70 -
71 -```
72 -
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.babel-existing-react-namespace-import.expect.md new
+32
@@ -0,0 +1,32 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import * as React from "react";
6 +
7 +function Component(props) {
8 + const [x] = React.useState(0);
9 + const expensiveNumber = React.useMemo(() => calculateExpensiveNumber(x), [x]);
10 +
11 + return <div>{expensiveNumber}</div>;
12 +}
13 +
14 +function Component2(props) {
15 + const [x] = React.useState(0);
16 + const expensiveNumber = React.useMemo(() => calculateExpensiveNumber(x), [x]);
17 +
18 + return <div>{expensiveNumber}</div>;
19 +}
20 +
21 +```
22 +
23 +
24 +## Error
25 +
26 +```
27 +[ReactForget] Todo: (BuildHIR::lowerMemberExpression) Handle loading properties from React namespace (4:4)
28 +
29 +[ReactForget] Todo: (BuildHIR::lowerMemberExpression) Handle loading properties from React namespace (5:5)
30 +```
31 +
32 +
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.babel-existing-react-namespace-import.js renamed
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.hooks-with-React-namespace.expect.md new
+19
@@ -0,0 +1,19 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Foo() {
6 + const [x, setX] = React.useState(1);
7 + return x;
8 +}
9 +
10 +```
11 +
12 +
13 +## Error
14 +
15 +```
16 +[ReactForget] Todo: (BuildHIR::lowerMemberExpression) Handle loading properties from React namespace (2:2)
17 +```
18 +
19 +
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.hooks-with-React-namespace.js renamed