@samitouri / QOS-React / commits / 85e6e9b469

[hir] Mark MethodCalls as escaping

Treat MethodCalls similar to general CallExpressions and mark them as escaping in PruneNonEscapingScopes pass. ghstack-source-id: 3c81bdb17f58fbeef8be24e7cb363172d1867217 Pull Request resolved: https://github.com/facebook/react-forget/pull/2925

Sathya Gunsasekaran committed May 1, 2024 at 15:58 UTC 85e6e9b469d6b1c437764d7662341c2f532bb3c4
7 files changed +232 -9
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/PruneNonEscapingScopes.ts
+9 -3
@@ -872,12 +872,18 @@ class CollectDependenciesVisitor extends ReactiveFunctionVisitor<State> {
872 instruction.lvalue.identifier.id,
873 instruction.value.place.identifier.id
874 );
875 - } else if (instruction.value.kind === "CallExpression") {
876 - const callee = instruction.value.callee;
875 + } else if (
876 + instruction.value.kind === "CallExpression" ||
877 + instruction.value.kind === "MethodCall"
878 + ) {
879 + let callee =
880 + instruction.value.kind === "CallExpression"
881 + ? instruction.value.callee
882 + : instruction.value.property;
883 if (getHookKind(state.env, callee.identifier) != null) {
884 const signature = getFunctionCallSignature(
885 this.env,
880 - instruction.value.callee.identifier.type
886 + callee.identifier.type
887 );
888 /*
889 * Hook values are assumed to escape by default since they can be inputs
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/rules-of-hooks-93dc5d5e538a.expect.md
+10 -3
@@ -17,14 +17,21 @@ function RegressionTest() {
17 ## Code
18
19 ```javascript
20 -// Valid because the loop doesn't change the order of hooks calls.
20 +import { unstable_useMemoCache as useMemoCache } from "react"; // Valid because the loop doesn't change the order of hooks calls.
21 function RegressionTest() {
22 + const $ = useMemoCache(1);
23 const res = [];
24 for (let i = 0; i !== 10 && true; ++i) {
25 res.push(i);
26 }
26 -
27 - React.useLayoutEffect(() => {});
27 + let t0;
28 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
29 + t0 = () => {};
30 + $[0] = t0;
31 + } else {
32 + t0 = $[0];
33 + }
34 + React.useLayoutEffect(t0);
35 }
36
37 ```
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/useEffect-global-pruned.expect.md new
+77
@@ -0,0 +1,77 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import { useEffect } from "react";
6 +
7 +function someGlobal() {}
8 +function useFoo() {
9 + const fn = React.useMemo(
10 + () =>
11 + function () {
12 + someGlobal();
13 + },
14 + []
15 + );
16 + useEffect(() => {
17 + fn();
18 + }, [fn]);
19 +
20 + return null;
21 +}
22 +
23 +export const FIXTURE_ENTRYPOINT = {
24 + fn: useFoo,
25 + params: [],
26 + isComponent: false,
27 +};
28 +
29 +```
30 +
31 +## Code
32 +
33 +```javascript
34 +import { useEffect, unstable_useMemoCache as useMemoCache } from "react";
35 +
36 +function someGlobal() {}
37 +function useFoo() {
38 + const $ = useMemoCache(3);
39 + let t0;
40 + let t1;
41 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
42 + t1 = function () {
43 + someGlobal();
44 + };
45 + $[0] = t1;
46 + } else {
47 + t1 = $[0];
48 + }
49 + t0 = t1;
50 + const fn = t0;
51 + let t2;
52 + let t3;
53 + if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
54 + t2 = () => {
55 + fn();
56 + };
57 + t3 = [fn];
58 + $[1] = t2;
59 + $[2] = t3;
60 + } else {
61 + t2 = $[1];
62 + t3 = $[2];
63 + }
64 + useEffect(t2, t3);
65 + return null;
66 +}
67 +
68 +export const FIXTURE_ENTRYPOINT = {
69 + fn: useFoo,
70 + params: [],
71 + isComponent: false,
72 +};
73 +
74 +```
75 +
76 +### Eval output
77 +(kind: ok) null
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/useEffect-global-pruned.js new
+23
@@ -0,0 +1,23 @@
1 +import { useEffect } from "react";
2 +
3 +function someGlobal() {}
4 +function useFoo() {
5 + const fn = React.useMemo(
6 + () =>
7 + function () {
8 + someGlobal();
9 + },
10 + []
11 + );
12 + useEffect(() => {
13 + fn();
14 + }, [fn]);
15 +
16 + return null;
17 +}
18 +
19 +export const FIXTURE_ENTRYPOINT = {
20 + fn: useFoo,
21 + params: [],
22 + isComponent: false,
23 +};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/useEffect-method-call.expect.md
+12 -3
@@ -19,11 +19,20 @@ export const FIXTURE_ENTRYPOINT = {
19 ## Code
20
21 ```javascript
22 +import { unstable_useMemoCache as useMemoCache } from "react";
23 let x = {};
24 function Component() {
24 - React.useEffect(() => {
25 - x.foo = 1;
26 - });
25 + const $ = useMemoCache(1);
26 + let t0;
27 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
28 + t0 = () => {
29 + x.foo = 1;
30 + };
31 + $[0] = t0;
32 + } else {
33 + t0 = $[0];
34 + }
35 + React.useEffect(t0);
36 }
37
38 export const FIXTURE_ENTRYPOINT = {
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/useEffect-namespace-pruned.expect.md new
+78
@@ -0,0 +1,78 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import * as React from "react";
6 +
7 +function someGlobal() {}
8 +function useFoo() {
9 + const fn = React.useMemo(
10 + () =>
11 + function () {
12 + someGlobal();
13 + },
14 + []
15 + );
16 + React.useEffect(() => {
17 + fn();
18 + }, [fn]);
19 +
20 + return null;
21 +}
22 +
23 +export const FIXTURE_ENTRYPOINT = {
24 + fn: useFoo,
25 + params: [],
26 + isComponent: false,
27 +};
28 +
29 +```
30 +
31 +## Code
32 +
33 +```javascript
34 +import { unstable_useMemoCache as useMemoCache } from "react";
35 +import * as React from "react";
36 +
37 +function someGlobal() {}
38 +function useFoo() {
39 + const $ = useMemoCache(3);
40 + let t0;
41 + let t1;
42 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
43 + t1 = function () {
44 + someGlobal();
45 + };
46 + $[0] = t1;
47 + } else {
48 + t1 = $[0];
49 + }
50 + t0 = t1;
51 + const fn = t0;
52 + let t2;
53 + let t3;
54 + if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
55 + t2 = () => {
56 + fn();
57 + };
58 + t3 = [fn];
59 + $[1] = t2;
60 + $[2] = t3;
61 + } else {
62 + t2 = $[1];
63 + t3 = $[2];
64 + }
65 + React.useEffect(t2, t3);
66 + return null;
67 +}
68 +
69 +export const FIXTURE_ENTRYPOINT = {
70 + fn: useFoo,
71 + params: [],
72 + isComponent: false,
73 +};
74 +
75 +```
76 +
77 +### Eval output
78 +(kind: ok) null
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/useEffect-namespace-pruned.js new
+23
@@ -0,0 +1,23 @@
1 +import * as React from "react";
2 +
3 +function someGlobal() {}
4 +function useFoo() {
5 + const fn = React.useMemo(
6 + () =>
7 + function () {
8 + someGlobal();
9 + },
10 + []
11 + );
12 + React.useEffect(() => {
13 + fn();
14 + }, [fn]);
15 +
16 + return null;
17 +}
18 +
19 +export const FIXTURE_ENTRYPOINT = {
20 + fn: useFoo,
21 + params: [],
22 + isComponent: false,
23 +};