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.
This commit is contained in:
Joe Savona
2023-09-11 12:49:47 -07:00
parent de2b684bc5
commit b1f7ebfd47
13 changed files with 283 additions and 30 deletions
@@ -8,6 +8,7 @@
import { NodePath, Scope } from "@babel/traverse";
import * as t from "@babel/types";
import { Expression } from "@babel/types";
import invariant from "invariant";
import {
CompilerError,
CompilerSuggestionOperation,
@@ -2238,6 +2239,19 @@ function isReorderableExpression(
case "BigIntLiteral": {
return true;
}
case "UnaryExpression": {
const unary = expr as NodePath<t.UnaryExpression>;
switch (expr.node.operator) {
case "!":
case "+":
case "-": {
return isReorderableExpression(builder, unary.get("argument"));
}
default: {
return false;
}
}
}
case "TypeCastExpression": {
return isReorderableExpression(
builder,
@@ -2293,6 +2307,17 @@ function isReorderableExpression(
return false;
}
}
case "ArrowFunctionExpression": {
const fn = expr as NodePath<t.ArrowFunctionExpression>;
const body = fn.get("body");
if (body.node.type === "BlockStatement") {
return body.node.body.length === 0;
} else {
// For TypeScript
invariant(body.isExpression(), "Expected an expression");
return isReorderableExpression(builder, body);
}
}
default: {
return false;
}
@@ -0,0 +1,41 @@
## Input
```javascript
function Component(x = [-1, 1]) {
return x;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [],
};
```
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
function Component(t0) {
const $ = useMemoCache(2);
const c_0 = $[0] !== t0;
let t1;
if (c_0) {
t1 = t0 === undefined ? [-1, 1] : t0;
$[0] = t0;
$[1] = t1;
} else {
t1 = $[1];
}
const x = t1;
return x;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [],
};
```
@@ -0,0 +1,8 @@
function Component(x = [-1, 1]) {
return x;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [],
};
@@ -0,0 +1,41 @@
## Input
```javascript
function Component(x = () => {}) {
return x;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [],
};
```
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
function Component(t0) {
const $ = useMemoCache(2);
const c_0 = $[0] !== t0;
let t1;
if (c_0) {
t1 = t0 === undefined ? () => {} : t0;
$[0] = t0;
$[1] = t1;
} else {
t1 = $[1];
}
const x = t1;
return x;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [],
};
```
@@ -0,0 +1,8 @@
function Component(x = () => {}) {
return x;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [],
};
@@ -0,0 +1,41 @@
## Input
```javascript
function Component(x = () => [-1, true, 42.0, "hello"]) {
return x;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [],
};
```
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
function Component(t0) {
const $ = useMemoCache(2);
const c_0 = $[0] !== t0;
let t1;
if (c_0) {
t1 = t0 === undefined ? () => [-1, true, 42, "hello"] : t0;
$[0] = t0;
$[1] = t1;
} else {
t1 = $[1];
}
const x = t1;
return x;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [],
};
```
@@ -0,0 +1,8 @@
function Component(x = () => [-1, true, 42.0, "hello"]) {
return x;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [],
};
@@ -0,0 +1,43 @@
## Input
```javascript
function Component(props) {
const [x = [-1, 1]] = props.value;
return x;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ value: [] }],
};
```
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
function Component(props) {
const $ = useMemoCache(2);
const [t0] = props.value;
const c_0 = $[0] !== t0;
let t1;
if (c_0) {
t1 = t0 === undefined ? [-1, 1] : t0;
$[0] = t0;
$[1] = t1;
} else {
t1 = $[1];
}
const x = t1;
return x;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ value: [] }],
};
```
@@ -0,0 +1,9 @@
function Component(props) {
const [x = [-1, 1]] = props.value;
return x;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ value: [] }],
};
@@ -1,29 +0,0 @@
## Input
```javascript
function Foo() {
(function t() {
let x = {};
return function a(x = () => {}) {
return x;
};
})();
}
export const FIXTURE_ENTRYPOINT = {
fn: Foo,
params: [],
isComponent: false,
};
```
## Error
```
[ReactForget] Todo: (BuildHIR::node.lowerReorderableExpression) Expression type 'ArrowFunctionExpression' cannot be safely reordered (4:4)
```
@@ -0,0 +1,57 @@
## Input
```javascript
function Foo() {
return (function t() {
let x = {};
return function a(x = () => {}) {
return x;
};
})();
}
export const FIXTURE_ENTRYPOINT = {
fn: Foo,
params: [],
isComponent: false,
};
```
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
function Foo() {
const $ = useMemoCache(2);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = function t() {
return function a(t25) {
const x_0 = t25 === undefined ? () => {} : t25;
return x_0;
};
};
$[0] = t0;
} else {
t0 = $[0];
}
let t1;
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
t1 = t0();
$[1] = t1;
} else {
t1 = $[1];
}
return t1;
}
export const FIXTURE_ENTRYPOINT = {
fn: Foo,
params: [],
isComponent: false,
};
```
@@ -412,6 +412,7 @@ const skipFilter = new Set([
"fbt-call-complex-param-value",
"fbt-template-string-same-scope",
"component-declaration-basic.flow",
"nested-function-with-param-as-captured-dep",
// TODO: 🌲
"forest-basic",