@samitouri / QOS-React-1 / commits / b1f7ebfd47

Reodering of unary and arrow function expressions

Supports reordering of unary and arrow function expressions: * Supports a trivially safe subset of unary operators, rejects things like `void` just because we don't need it yet. * Supports arrow function expressions that are either an empty block statement or a single expression which is itself reorderable.

Joe Savona committed Sep 11, 2023 at 12:49 UTC b1f7ebfd47d3ecc1c9561a43c3c68d129b240484
13 files changed +283 -30
compiler/packages/babel-plugin-react-forget/src/HIR/BuildHIR.ts
+25
@@ -8,6 +8,7 @@
8 import { NodePath, Scope } from "@babel/traverse";
9 import * as t from "@babel/types";
10 import { Expression } from "@babel/types";
11 +import invariant from "invariant";
12 import {
13 CompilerError,
14 CompilerSuggestionOperation,
@@ -2238,6 +2239,19 @@ function isReorderableExpression(
2239 case "BigIntLiteral": {
2240 return true;
2241 }
2242 + case "UnaryExpression": {
2243 + const unary = expr as NodePath<t.UnaryExpression>;
2244 + switch (expr.node.operator) {
2245 + case "!":
2246 + case "+":
2247 + case "-": {
2248 + return isReorderableExpression(builder, unary.get("argument"));
2249 + }
2250 + default: {
2251 + return false;
2252 + }
2253 + }
2254 + }
2255 case "TypeCastExpression": {
2256 return isReorderableExpression(
2257 builder,
@@ -2293,6 +2307,17 @@ function isReorderableExpression(
2307 return false;
2308 }
2309 }
2310 + case "ArrowFunctionExpression": {
2311 + const fn = expr as NodePath<t.ArrowFunctionExpression>;
2312 + const body = fn.get("body");
2313 + if (body.node.type === "BlockStatement") {
2314 + return body.node.body.length === 0;
2315 + } else {
2316 + // For TypeScript
2317 + invariant(body.isExpression(), "Expected an expression");
2318 + return isReorderableExpression(builder, body);
2319 + }
2320 + }
2321 default: {
2322 return false;
2323 }
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/default-param-array-with-unary.expect.md new
+41
@@ -0,0 +1,41 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(x = [-1, 1]) {
6 + return x;
7 +}
8 +
9 +export const FIXTURE_ENTRYPOINT = {
10 + fn: Component,
11 + params: [],
12 +};
13 +
14 +```
15 +
16 +## Code
17 +
18 +```javascript
19 +import { unstable_useMemoCache as useMemoCache } from "react";
20 +function Component(t0) {
21 + const $ = useMemoCache(2);
22 + const c_0 = $[0] !== t0;
23 + let t1;
24 + if (c_0) {
25 + t1 = t0 === undefined ? [-1, 1] : t0;
26 + $[0] = t0;
27 + $[1] = t1;
28 + } else {
29 + t1 = $[1];
30 + }
31 + const x = t1;
32 + return x;
33 +}
34 +
35 +export const FIXTURE_ENTRYPOINT = {
36 + fn: Component,
37 + params: [],
38 +};
39 +
40 +```
41 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/default-param-array-with-unary.js new
+8
@@ -0,0 +1,8 @@
1 +function Component(x = [-1, 1]) {
2 + return x;
3 +}
4 +
5 +export const FIXTURE_ENTRYPOINT = {
6 + fn: Component,
7 + params: [],
8 +};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/default-param-with-empty-callback.expect.md new
+41
@@ -0,0 +1,41 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(x = () => {}) {
6 + return x;
7 +}
8 +
9 +export const FIXTURE_ENTRYPOINT = {
10 + fn: Component,
11 + params: [],
12 +};
13 +
14 +```
15 +
16 +## Code
17 +
18 +```javascript
19 +import { unstable_useMemoCache as useMemoCache } from "react";
20 +function Component(t0) {
21 + const $ = useMemoCache(2);
22 + const c_0 = $[0] !== t0;
23 + let t1;
24 + if (c_0) {
25 + t1 = t0 === undefined ? () => {} : t0;
26 + $[0] = t0;
27 + $[1] = t1;
28 + } else {
29 + t1 = $[1];
30 + }
31 + const x = t1;
32 + return x;
33 +}
34 +
35 +export const FIXTURE_ENTRYPOINT = {
36 + fn: Component,
37 + params: [],
38 +};
39 +
40 +```
41 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/default-param-with-empty-callback.js new
+8
@@ -0,0 +1,8 @@
1 +function Component(x = () => {}) {
2 + return x;
3 +}
4 +
5 +export const FIXTURE_ENTRYPOINT = {
6 + fn: Component,
7 + params: [],
8 +};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/default-param-with-reorderable-callback.expect.md new
+41
@@ -0,0 +1,41 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(x = () => [-1, true, 42.0, "hello"]) {
6 + return x;
7 +}
8 +
9 +export const FIXTURE_ENTRYPOINT = {
10 + fn: Component,
11 + params: [],
12 +};
13 +
14 +```
15 +
16 +## Code
17 +
18 +```javascript
19 +import { unstable_useMemoCache as useMemoCache } from "react";
20 +function Component(t0) {
21 + const $ = useMemoCache(2);
22 + const c_0 = $[0] !== t0;
23 + let t1;
24 + if (c_0) {
25 + t1 = t0 === undefined ? () => [-1, true, 42, "hello"] : t0;
26 + $[0] = t0;
27 + $[1] = t1;
28 + } else {
29 + t1 = $[1];
30 + }
31 + const x = t1;
32 + return x;
33 +}
34 +
35 +export const FIXTURE_ENTRYPOINT = {
36 + fn: Component,
37 + params: [],
38 +};
39 +
40 +```
41 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/default-param-with-reorderable-callback.js new
+8
@@ -0,0 +1,8 @@
1 +function Component(x = () => [-1, true, 42.0, "hello"]) {
2 + return x;
3 +}
4 +
5 +export const FIXTURE_ENTRYPOINT = {
6 + fn: Component,
7 + params: [],
8 +};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-default-array-with-unary.expect.md new
+43
@@ -0,0 +1,43 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + const [x = [-1, 1]] = props.value;
7 + return x;
8 +}
9 +
10 +export const FIXTURE_ENTRYPOINT = {
11 + fn: Component,
12 + params: [{ value: [] }],
13 +};
14 +
15 +```
16 +
17 +## Code
18 +
19 +```javascript
20 +import { unstable_useMemoCache as useMemoCache } from "react";
21 +function Component(props) {
22 + const $ = useMemoCache(2);
23 + const [t0] = props.value;
24 + const c_0 = $[0] !== t0;
25 + let t1;
26 + if (c_0) {
27 + t1 = t0 === undefined ? [-1, 1] : t0;
28 + $[0] = t0;
29 + $[1] = t1;
30 + } else {
31 + t1 = $[1];
32 + }
33 + const x = t1;
34 + return x;
35 +}
36 +
37 +export const FIXTURE_ENTRYPOINT = {
38 + fn: Component,
39 + params: [{ value: [] }],
40 +};
41 +
42 +```
43 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-default-array-with-unary.js new
+9
@@ -0,0 +1,9 @@
1 +function Component(props) {
2 + const [x = [-1, 1]] = props.value;
3 + return x;
4 +}
5 +
6 +export const FIXTURE_ENTRYPOINT = {
7 + fn: Component,
8 + params: [{ value: [] }],
9 +};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.nested-function-with-param-as-captured-dep.expect.md deleted
-29
@@ -1,29 +0,0 @@
1 -
2 -## Input
3 -
4 -```javascript
5 -function Foo() {
6 - (function t() {
7 - let x = {};
8 - return function a(x = () => {}) {
9 - return x;
10 - };
11 - })();
12 -}
13 -
14 -export const FIXTURE_ENTRYPOINT = {
15 - fn: Foo,
16 - params: [],
17 - isComponent: false,
18 -};
19 -
20 -```
21 -
22 -
23 -## Error
24 -
25 -```
26 -[ReactForget] Todo: (BuildHIR::node.lowerReorderableExpression) Expression type 'ArrowFunctionExpression' cannot be safely reordered (4:4)
27 -```
28 -
29 -
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/nested-function-with-param-as-captured-dep.expect.md new
+57
@@ -0,0 +1,57 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Foo() {
6 + return (function t() {
7 + let x = {};
8 + return function a(x = () => {}) {
9 + return x;
10 + };
11 + })();
12 +}
13 +
14 +export const FIXTURE_ENTRYPOINT = {
15 + fn: Foo,
16 + params: [],
17 + isComponent: false,
18 +};
19 +
20 +```
21 +
22 +## Code
23 +
24 +```javascript
25 +import { unstable_useMemoCache as useMemoCache } from "react";
26 +function Foo() {
27 + const $ = useMemoCache(2);
28 + let t0;
29 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
30 + t0 = function t() {
31 + return function a(t25) {
32 + const x_0 = t25 === undefined ? () => {} : t25;
33 + return x_0;
34 + };
35 + };
36 + $[0] = t0;
37 + } else {
38 + t0 = $[0];
39 + }
40 + let t1;
41 + if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
42 + t1 = t0();
43 + $[1] = t1;
44 + } else {
45 + t1 = $[1];
46 + }
47 + return t1;
48 +}
49 +
50 +export const FIXTURE_ENTRYPOINT = {
51 + fn: Foo,
52 + params: [],
53 + isComponent: false,
54 +};
55 +
56 +```
57 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/nested-function-with-param-as-captured-dep.ts renamed
+1 -1
@@ -1,5 +1,5 @@
1 function Foo() {
2 - (function t() {
2 + return (function t() {
3 let x = {};
4 return function a(x = () => {}) {
5 return x;
compiler/packages/sprout/src/SproutTodoFilter.ts
+1
@@ -412,6 +412,7 @@ const skipFilter = new Set([
412 "fbt-call-complex-param-value",
413 "fbt-template-string-same-scope",
414 "component-declaration-basic.flow",
415 + "nested-function-with-param-as-captured-dep",
416
417 // TODO: 🌲
418 "forest-basic",