mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
TryStatement: partial lowering
Implements the core lowering logic for try/catch: * Inside of the `try` block, we use the new HIRBuilder mode to wrap every instruction in a separate basic block with a maybe-throw terminal * We emit a 'try' terminal for the try/catch itself For basic examples this already works correctly. But we don't handle catch clause params yet.
This commit is contained in:
@@ -913,6 +913,68 @@ function lowerStatement(
|
||||
case "EmptyStatement": {
|
||||
return;
|
||||
}
|
||||
case "TryStatement": {
|
||||
const stmt = stmtPath as NodePath<t.TryStatement>;
|
||||
const continuationBlock = builder.reserve("block");
|
||||
|
||||
const handlerPath = stmt.get("handler");
|
||||
// NOTE: null check is for older babel versions, the hasNode() check is for TS to refine the type
|
||||
if (handlerPath.node == null || !handlerPath.hasNode()) {
|
||||
builder.errors.push({
|
||||
reason: `(BuildHIR::lowerStatement) Handle TryStatement without a catch clause`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: stmt.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const handler = builder.enter("block", (_blockId) => {
|
||||
lowerStatement(builder, handlerPath.get("body"));
|
||||
return {
|
||||
kind: "goto",
|
||||
block: continuationBlock.id,
|
||||
variant: GotoVariant.Break,
|
||||
id: makeInstructionId(0),
|
||||
loc: handlerPath.node.loc ?? GeneratedSource,
|
||||
};
|
||||
});
|
||||
if (stmt.get("finalizer").node != null) {
|
||||
builder.errors.push({
|
||||
reason: `(BuildHIR::lowerStatement) Handle TryStatement with a finalizer ('finally') clause`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: stmt.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
|
||||
const block = builder.enter("block", (_blockId) => {
|
||||
const block = stmt.get("block");
|
||||
builder.enterTryCatch(handler, () => {
|
||||
lowerStatement(builder, block);
|
||||
});
|
||||
return {
|
||||
kind: "goto",
|
||||
block: continuationBlock.id,
|
||||
variant: GotoVariant.Break,
|
||||
id: makeInstructionId(0),
|
||||
loc: block.node.loc ?? GeneratedSource,
|
||||
};
|
||||
});
|
||||
|
||||
builder.terminateWithContinuation(
|
||||
{
|
||||
kind: "try",
|
||||
block,
|
||||
handler,
|
||||
fallthrough: continuationBlock.id,
|
||||
id: makeInstructionId(0),
|
||||
loc: stmt.node.loc ?? GeneratedSource,
|
||||
},
|
||||
continuationBlock
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
case "ForInStatement":
|
||||
case "ClassDeclaration":
|
||||
case "DeclareClass":
|
||||
@@ -932,7 +994,6 @@ function lowerStatement(
|
||||
case "ImportDeclaration":
|
||||
case "InterfaceDeclaration":
|
||||
case "OpaqueType":
|
||||
case "TryStatement":
|
||||
case "TypeAlias":
|
||||
case "TSDeclareFunction":
|
||||
case "TSEnumDeclaration":
|
||||
|
||||
@@ -79,7 +79,7 @@ export type Bindings = Map<
|
||||
|
||||
// Determines how instructions should be constructed in order to preserve
|
||||
// exception semantics
|
||||
type ExceptionsMode =
|
||||
export type ExceptionsMode =
|
||||
// Mode used for code not covered by explicit exception handling, any
|
||||
// errors are assumed to be thrown out of the function
|
||||
| { kind: "ThrowExceptions" }
|
||||
@@ -158,6 +158,13 @@ export default class HIRBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
enterTryCatch(handler: BlockId, fn: () => void): void {
|
||||
const prevMode = this.#mode;
|
||||
this.#mode = { kind: "CatchExceptions", handler };
|
||||
fn();
|
||||
this.#mode = prevMode;
|
||||
}
|
||||
|
||||
makeTemporary(): Identifier {
|
||||
const id = this.nextIdentifierId;
|
||||
return {
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
const x = [];
|
||||
try {
|
||||
x.push(foo());
|
||||
} catch {
|
||||
x.push(bar());
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { unstable_useMemoCache as useMemoCache } from "react";
|
||||
function Component(props) {
|
||||
const $ = useMemoCache(3);
|
||||
let x;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
x = [];
|
||||
try {
|
||||
let t0;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = foo();
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
x.push(t0);
|
||||
} catch {
|
||||
let t1;
|
||||
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = bar();
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
x.push(t1);
|
||||
}
|
||||
$[0] = x;
|
||||
} else {
|
||||
x = $[0];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
function Component(props) {
|
||||
const x = [];
|
||||
try {
|
||||
x.push(foo());
|
||||
} catch {
|
||||
x.push(bar());
|
||||
}
|
||||
return x;
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
const x = [];
|
||||
try {
|
||||
x.push(foo());
|
||||
} catch {
|
||||
x.push(bar());
|
||||
}
|
||||
x.push(props.value); // extend the mutable range to include the try/catch
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { unstable_useMemoCache as useMemoCache } from "react";
|
||||
function Component(props) {
|
||||
const $ = useMemoCache(4);
|
||||
const c_0 = $[0] !== props.value;
|
||||
let x;
|
||||
if (c_0) {
|
||||
x = [];
|
||||
try {
|
||||
let t0;
|
||||
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = foo();
|
||||
$[2] = t0;
|
||||
} else {
|
||||
t0 = $[2];
|
||||
}
|
||||
x.push(t0);
|
||||
} catch {
|
||||
let t1;
|
||||
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = bar();
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
x.push(t1);
|
||||
}
|
||||
|
||||
x.push(props.value);
|
||||
$[0] = props.value;
|
||||
$[1] = x;
|
||||
} else {
|
||||
x = $[1];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
function Component(props) {
|
||||
const x = [];
|
||||
try {
|
||||
x.push(foo());
|
||||
} catch {
|
||||
x.push(bar());
|
||||
}
|
||||
x.push(props.value); // extend the mutable range to include the try/catch
|
||||
return x;
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
let x;
|
||||
try {
|
||||
x = foo();
|
||||
} catch {
|
||||
x = null;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { unstable_useMemoCache as useMemoCache } from "react";
|
||||
function Component(props) {
|
||||
const $ = useMemoCache(1);
|
||||
let x = undefined;
|
||||
try {
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = foo();
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
x = t0;
|
||||
} catch {
|
||||
x = null;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
function Component(props) {
|
||||
let x;
|
||||
try {
|
||||
x = foo();
|
||||
} catch {
|
||||
x = null;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
@@ -412,6 +412,9 @@ const skipFilter = new Set([
|
||||
"fbt-call-complex-param-value",
|
||||
"fbt-template-string-same-scope",
|
||||
"component-declaration-basic.flow",
|
||||
"try-catch-mutate-outer-value",
|
||||
"try-catch-within-mutable-range",
|
||||
"try-catch",
|
||||
|
||||
// TODO: 🌲
|
||||
"forest-basic",
|
||||
|
||||
Reference in New Issue
Block a user