[be] Extract computeUnconditionalBlocks() helper
Joe Savona committed
Feb 13, 2024 at 16:45 UTC
6dae958eab3b19a6d89ea13242b87ac8cad81e97
4 files changed
+43
-59
compiler/packages/babel-plugin-react-forget/src/Entrypoint/Pipeline.ts
+1
-6
@@ -184,12 +184,7 @@ function* runWithEnvironment(
184
}
185
186
if (env.config.validateNoSetStateInRender) {
187
- const noSetStateInRenderResult = validateNoSetStateInRender(hir).unwrap();
188
- yield log({
189
- kind: "debug",
190
- name: "ValidateNoSetStateInRender",
191
- value: noSetStateInRenderResult.debug(),
192
- });
187
+ validateNoSetStateInRender(hir).unwrap();
188
}
189
190
inferReactivePlaces(hir);
compiler/packages/babel-plugin-react-forget/src/HIR/ComputeUnconditionalBlocks.ts
new
+34
@@ -0,0 +1,34 @@
1
+/*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+import { BlockId, HIRFunction, computePostDominatorTree } from ".";
9
+import { CompilerError } from "..";
10
+
11
+export function computeUnconditionalBlocks(fn: HIRFunction): Set<BlockId> {
12
+ // Construct the set of blocks that is always reachable from the entry block.
13
+ const unconditionalBlocks = new Set<BlockId>();
14
+ const dominators = computePostDominatorTree(fn, {
15
+ /*
16
+ * Hooks must only be in a consistent order for executions that return normally,
17
+ * so we opt-in to viewing throw as a non-exit node.
18
+ */
19
+ includeThrowsAsExitNode: false,
20
+ });
21
+ const exit = dominators.exit;
22
+ let current: BlockId | null = fn.body.entry;
23
+ while (current !== null && current !== exit) {
24
+ CompilerError.invariant(!unconditionalBlocks.has(current), {
25
+ reason:
26
+ "Internal error: non-terminating loop in ValidateUnconditionalHooks",
27
+ loc: null,
28
+ suggestions: null,
29
+ });
30
+ unconditionalBlocks.add(current);
31
+ current = dominators.get(current);
32
+ }
33
+ return unconditionalBlocks;
34
+}
compiler/packages/babel-plugin-react-forget/src/Validation/ValidateHooksUsage.ts
+2
-23
@@ -10,10 +10,9 @@ import {
10
CompilerErrorDetail,
11
ErrorSeverity,
12
} from "../CompilerError";
13
-import { computePostDominatorTree } from "../HIR";
13
+import { computeUnconditionalBlocks } from "../HIR/ComputeUnconditionalBlocks";
14
import { isHookName } from "../HIR/Environment";
15
import {
16
- BlockId,
16
HIRFunction,
17
IdentifierId,
18
Place,
@@ -88,27 +87,7 @@ function joinKinds(a: Kind, b: Kind): Kind {
87
* See the note for Kind.PotentialHook for sources of potential hooks
88
*/
89
export function validateHooksUsage(fn: HIRFunction): void {
91
- // Construct the set of blocks that is always reachable from the entry block.
92
- const unconditionalBlocks = new Set<BlockId>();
93
- const dominators = computePostDominatorTree(fn, {
94
- /*
95
- * Hooks must only be in a consistent order for executions that return normally,
96
- * so we opt-in to viewing throw as a non-exit node.
97
- */
98
- includeThrowsAsExitNode: false,
99
- });
100
- const exit = dominators.exit;
101
- let current: BlockId | null = fn.body.entry;
102
- while (current !== null && current !== exit) {
103
- CompilerError.invariant(!unconditionalBlocks.has(current), {
104
- reason:
105
- "Internal error: non-terminating loop in ValidateUnconditionalHooks",
106
- loc: null,
107
- suggestions: null,
108
- });
109
- unconditionalBlocks.add(current);
110
- current = dominators.get(current);
111
- }
90
+ const unconditionalBlocks = computeUnconditionalBlocks(fn);
91
92
const errorsByPlace = new Map<SourceLocation, CompilerErrorDetail>();
93
function recordConditionalHookError(place: Place): void {
compiler/packages/babel-plugin-react-forget/src/Validation/ValidateNoSetStateInRender.ts
+6
-30
@@ -6,15 +6,8 @@
6
*/
7
8
import { CompilerError, ErrorSeverity } from "../CompilerError";
9
-import {
10
- BlockId,
11
- HIRFunction,
12
- IdentifierId,
13
- Place,
14
- computePostDominatorTree,
15
- isSetStateType,
16
-} from "../HIR";
17
-import { PostDominator } from "../HIR/Dominator";
9
+import { HIRFunction, IdentifierId, Place, isSetStateType } from "../HIR";
10
+import { computeUnconditionalBlocks } from "../HIR/ComputeUnconditionalBlocks";
11
import { eachInstructionValueOperand } from "../HIR/visitors";
12
import { Err, Ok, Result } from "../Utils/Result";
13
@@ -48,7 +41,7 @@ import { Err, Ok, Result } from "../Utils/Result";
41
*/
42
export function validateNoSetStateInRender(
43
fn: HIRFunction
51
-): Result<PostDominator<BlockId>, CompilerError> {
44
+): Result<void, CompilerError> {
45
const unconditionalSetStateFunctions: Set<IdentifierId> = new Set();
46
return validateNoSetStateInRenderImpl(fn, unconditionalSetStateFunctions);
47
}
@@ -56,25 +49,8 @@ export function validateNoSetStateInRender(
49
function validateNoSetStateInRenderImpl(
50
fn: HIRFunction,
51
unconditionalSetStateFunctions: Set<IdentifierId>
59
-): Result<PostDominator<BlockId>, CompilerError> {
60
- // Construct the set of blocks that is always reachable from the entry block.
61
- const unconditionalBlocks = new Set<BlockId>();
62
- const dominators = computePostDominatorTree(fn, {
63
- includeThrowsAsExitNode: false,
64
- });
65
- const exit = dominators.exit;
66
- let current: BlockId | null = fn.body.entry;
67
- while (current !== null && current !== exit) {
68
- CompilerError.invariant(!unconditionalBlocks.has(current), {
69
- reason:
70
- "Internal error: non-terminating loop in ValidateNoSetStateInRender",
71
- loc: null,
72
- suggestions: null,
73
- description: null,
74
- });
75
- unconditionalBlocks.add(current);
76
- current = dominators.get(current);
77
- }
52
+): Result<void, CompilerError> {
53
+ const unconditionalBlocks = computeUnconditionalBlocks(fn);
54
55
const errors = new CompilerError();
56
for (const [, block] of fn.body.blocks) {
@@ -140,7 +116,7 @@ function validateNoSetStateInRenderImpl(
116
if (errors.hasErrors()) {
117
return Err(errors);
118
} else {
143
- return Ok(dominators);
119
+ return Ok(undefined);
120
}
121
}
122