[HIR] Nonnullable block fallthroughs
ghstack-source-id: 6f24b60056c741e5d4e9836f91bc4fce0f9e8fdd Pull Request resolved: https://github.com/facebook/react-forget/pull/2865
Mofei Zhang committed
Apr 23, 2024 at 10:18 UTC
555100ca83049ce94cac210098ed7c367fd8751c
11 files changed
+172
-295
compiler/packages/babel-plugin-react-forget/src/HIR/HIR.ts
+20
-4
@@ -362,6 +362,8 @@ export type Terminal =
362
| TryTerminal
363
| ReactiveScopeTerminal;
364
365
+export type TerminalWithFallthrough = Terminal & { fallthrough: BlockId };
366
+
367
function _staticInvariantTerminalHasLocation(
368
terminal: Terminal
369
): SourceLocation {
@@ -376,6 +378,13 @@ function _staticInvariantTerminalHasInstructionId(
378
return terminal.id;
379
}
380
381
+function _staticInvariantTerminalHasFallthrough(
382
+ terminal: Terminal
383
+): BlockId | never | undefined {
384
+ // If this fails, it is because a variant of Terminal is missing a fallthrough annotation
385
+ return terminal.fallthrough;
386
+}
387
+
388
/*
389
* Terminal nodes allowed for a value block
390
* A terminal that couldn't be lowered correctly.
@@ -384,6 +393,7 @@ export type UnsupportedTerminal = {
393
kind: "unsupported";
394
id: InstructionId;
395
loc: SourceLocation;
396
+ fallthrough?: never;
397
};
398
399
/**
@@ -395,6 +405,7 @@ export type UnreachableTerminal = {
405
kind: "unreachable";
406
id: InstructionId;
407
loc: SourceLocation;
408
+ fallthrough?: never;
409
};
410
411
export type ThrowTerminal = {
@@ -402,6 +413,7 @@ export type ThrowTerminal = {
413
value: Place;
414
id: InstructionId;
415
loc: SourceLocation;
416
+ fallthrough?: never;
417
};
418
export type Case = { test: Place | null; block: BlockId };
419
@@ -410,6 +422,7 @@ export type ReturnTerminal = {
422
loc: SourceLocation;
423
value: Place;
424
id: InstructionId;
425
+ fallthrough?: never;
426
};
427
428
export type GotoTerminal = {
@@ -418,6 +431,7 @@ export type GotoTerminal = {
431
variant: GotoVariant;
432
id: InstructionId;
433
loc: SourceLocation;
434
+ fallthrough?: never;
435
};
436
437
export enum GotoVariant {
@@ -431,7 +445,7 @@ export type IfTerminal = {
445
test: Place;
446
consequent: BlockId;
447
alternate: BlockId;
434
- fallthrough: BlockId | null;
448
+ fallthrough: BlockId;
449
id: InstructionId;
450
loc: SourceLocation;
451
};
@@ -443,13 +457,14 @@ export type BranchTerminal = {
457
alternate: BlockId;
458
id: InstructionId;
459
loc: SourceLocation;
460
+ fallthrough?: never;
461
};
462
463
export type SwitchTerminal = {
464
kind: "switch";
465
test: Place;
466
cases: Array<Case>;
452
- fallthrough: BlockId | null;
467
+ fallthrough: BlockId;
468
id: InstructionId;
469
loc: SourceLocation;
470
};
@@ -521,7 +536,7 @@ export type TernaryTerminal = {
536
export type LabelTerminal = {
537
kind: "label";
538
block: BlockId;
524
- fallthrough: BlockId | null;
539
+ fallthrough: BlockId;
540
id: InstructionId;
541
loc: SourceLocation;
542
};
@@ -555,7 +570,7 @@ export type TryTerminal = {
570
handlerBinding: Place | null;
571
handler: BlockId;
572
// TODO: support `finally`
558
- fallthrough: BlockId | null;
573
+ fallthrough: BlockId;
574
id: InstructionId;
575
loc: SourceLocation;
576
};
@@ -566,6 +581,7 @@ export type MaybeThrowTerminal = {
581
handler: BlockId;
582
id: InstructionId;
583
loc: SourceLocation;
584
+ fallthrough?: never;
585
};
586
587
export type ReactiveScopeTerminal = {
compiler/packages/babel-plugin-react-forget/src/HIR/HIRBuilder.ts
+71
-120
@@ -8,7 +8,6 @@
8
import { Binding, NodePath } from "@babel/traverse";
9
import * as t from "@babel/types";
10
import { CompilerError } from "../CompilerError";
11
-import { assertExhaustive } from "../Utils/utils";
11
import { Environment } from "./Environment";
12
import { Global } from "./Globals";
13
import {
@@ -32,8 +31,8 @@ import {
31
import { printInstruction } from "./PrintHIR";
32
import {
33
eachTerminalSuccessor,
35
- mapOptionalFallthroughs,
34
mapTerminalSuccessors,
35
+ terminalFallthrough,
36
} from "./visitors";
37
38
/*
@@ -329,7 +328,6 @@ export default class HIRBuilder {
328
ir.blocks = rpoBlocks;
329
330
removeUnreachableForUpdates(ir);
332
- removeUnreachableFallthroughs(ir);
331
removeDeadDoWhileStatements(ir);
332
removeUnnecessaryTryCatch(ir);
333
markInstructionIds(ir);
@@ -629,24 +627,6 @@ export function removeUnreachableForUpdates(fn: HIR): void {
627
}
628
}
629
632
-export function removeUnreachableFallthroughs(func: HIR): void {
633
- const visited: Set<BlockId> = new Set();
634
- for (const [_, block] of func.blocks) {
635
- visited.add(block.id);
636
- }
637
-
638
- // Cleanup any fallthrough blocks that weren't visited
639
- for (const [_, block] of func.blocks) {
640
- mapOptionalFallthroughs(block.terminal, (fallthrough) => {
641
- if (visited.has(fallthrough)) {
642
- return fallthrough;
643
- } else {
644
- return null;
645
- }
646
- });
647
- }
648
-}
649
-
630
export function removeDeadDoWhileStatements(func: HIR): void {
631
const visited: Set<BlockId> = new Set();
632
for (const [_, block] of func.blocks) {
@@ -691,14 +671,19 @@ export function reversePostorderBlocks(func: HIR): void {
671
*/
672
function getReversePostorderedBlocks(func: HIR): HIR["blocks"] {
673
const visited: Set<BlockId> = new Set();
674
+ const used: Set<BlockId> = new Set();
675
+ const usedFallthroughs: Set<BlockId> = new Set();
676
const postorder: Array<BlockId> = [];
695
- function visit(blockId: BlockId): void {
696
- if (visited.has(blockId)) {
677
+ function visit(blockId: BlockId, isUsed: boolean): void {
678
+ const wasUsed = used.has(blockId);
679
+ const wasVisited = visited.has(blockId);
680
+ visited.add(blockId);
681
+ if (isUsed) {
682
+ used.add(blockId);
683
+ }
684
+ if (wasVisited && (wasUsed || !isUsed)) {
685
return;
686
}
699
- visited.add(blockId);
700
- const block = func.blocks.get(blockId)!;
701
- const { terminal } = block;
687
688
/*
689
* Note that we visit successors in reverse order. This ensures that when we
@@ -710,7 +695,7 @@ function getReversePostorderedBlocks(func: HIR): HIR["blocks"] {
695
* // bb1
696
* x = 1;
697
* } else {
713
- * // b2
698
+ * // bb2
699
* x = 2;
700
* }
701
* // bb3
@@ -721,104 +706,50 @@ function getReversePostorderedBlocks(func: HIR): HIR["blocks"] {
706
* program order for visual debugging. By visiting the successors in reverse order
707
* (eg bb2 then bb1), we ensure that they get reversed back to the correct order.
708
*/
724
- switch (terminal.kind) {
725
- case "return":
726
- case "throw": {
727
- // no-op, no successors
728
- break;
729
- }
730
- case "goto": {
731
- visit(terminal.block);
732
- break;
733
- }
734
- case "if": {
735
- /*
736
- * can ignore fallthrough, if its reachable it will be reached through
737
- * consequent/alternate
738
- */
739
- const { consequent, alternate } = terminal;
740
- visit(alternate);
741
- visit(consequent);
742
- break;
743
- }
744
- case "branch": {
745
- const { consequent, alternate } = terminal;
746
- visit(alternate);
747
- visit(consequent);
748
- break;
749
- }
750
- case "switch": {
751
- /*
752
- * can ignore fallthrough, if its reachable it will be reached through
753
- * a case
754
- */
755
- const { cases } = terminal;
756
- for (const case_ of [...cases].reverse()) {
757
- visit(case_.block);
758
- }
759
- break;
760
- }
761
- case "optional":
762
- case "ternary":
763
- case "logical": {
764
- visit(terminal.test);
765
- break;
766
- }
767
- case "do-while": {
768
- visit(terminal.loop);
769
- break;
770
- }
771
- case "while": {
772
- visit(terminal.test);
773
- break;
774
- }
775
- case "for":
776
- case "for-in":
777
- case "for-of": {
778
- visit(terminal.init);
779
- break;
780
- }
781
- case "label": {
782
- visit(terminal.block);
783
- break;
784
- }
785
- case "sequence": {
786
- visit(terminal.block);
787
- break;
788
- }
789
- case "maybe-throw": {
790
- visit(terminal.handler);
791
- visit(terminal.continuation);
792
- break;
793
- }
794
- case "try": {
795
- visit(terminal.block);
796
- break;
797
- }
798
- case "scope": {
799
- visit(terminal.block);
800
- break;
801
- }
802
- case "unreachable":
803
- case "unsupported": {
804
- break;
805
- }
806
- default: {
807
- assertExhaustive(
808
- terminal,
809
- `Unexpected terminal kind \`${(terminal as any).kind}\``
810
- );
709
+ const block = func.blocks.get(blockId)!;
710
+ const successors = [...eachTerminalSuccessor(block.terminal)].reverse();
711
+ const fallthrough = terminalFallthrough(block.terminal);
712
+
713
+ /**
714
+ * Fallthrough blocks are only used to record original program block structure. If the
715
+ * fallthrough is actually reachable, it will be reached through terminal successors.
716
+ * To retain program structure, we visit fallthrough blocks first (marking them as not
717
+ * actually used yet) to ensure their block IDs emitted in the correct order.
718
+ */
719
+ if (fallthrough != null) {
720
+ if (isUsed) {
721
+ usedFallthroughs.add(fallthrough);
722
}
723
+ visit(fallthrough, false);
724
+ }
725
+ for (const successor of successors) {
726
+ visit(successor, isUsed);
727
}
728
814
- postorder.push(blockId);
729
+ if (!wasVisited) {
730
+ postorder.push(blockId);
731
+ }
732
}
816
- visit(func.entry);
817
-
818
- const blocks = new Map();
733
+ visit(func.entry, true);
734
+ const blocks = new Map<BlockId, BasicBlock>();
735
for (const blockId of postorder.reverse()) {
820
- blocks.set(blockId, func.blocks.get(blockId)!);
736
+ const block = func.blocks.get(blockId)!;
737
+ if (used.has(blockId)) {
738
+ blocks.set(blockId, func.blocks.get(blockId)!);
739
+ } else if (usedFallthroughs.has(blockId)) {
740
+ blocks.set(blockId, {
741
+ ...block,
742
+ instructions: [],
743
+ terminal: {
744
+ kind: "unreachable",
745
+ id: block.terminal.id,
746
+ loc: block.terminal.loc,
747
+ },
748
+ });
749
+ }
750
+ // otherwise this block is unreachable
751
}
752
+
753
return blocks;
754
}
755
@@ -847,6 +778,14 @@ export function markPredecessors(func: HIR): void {
778
const visited: Set<BlockId> = new Set();
779
function visit(blockId: BlockId, prevBlock: BasicBlock | null): void {
780
const block = func.blocks.get(blockId)!;
781
+ if (block == null) {
782
+ return;
783
+ }
784
+ CompilerError.invariant(block != null, {
785
+ reason: "unexpected missing block",
786
+ description: `block ${blockId}`,
787
+ loc: GeneratedSource,
788
+ });
789
if (prevBlock) {
790
block.preds.add(prevBlock.id);
791
}
@@ -879,7 +818,7 @@ function getTargetIfIndirection(block: BasicBlock): number | null {
818
819
/*
820
* Finds try terminals where the handler is unreachable, and converts the try
882
- * to a goto(terminal.fallthrough)
821
+ * to a goto(terminal.block)
822
*/
823
export function removeUnnecessaryTryCatch(fn: HIR): void {
824
for (const [, block] of fn.blocks) {
@@ -887,6 +826,9 @@ export function removeUnnecessaryTryCatch(fn: HIR): void {
826
block.terminal.kind === "try" &&
827
!fn.blocks.has(block.terminal.handler)
828
) {
829
+ const handlerId = block.terminal.handler;
830
+ const fallthroughId = block.terminal.fallthrough;
831
+ const fallthrough = fn.blocks.get(fallthroughId);
832
block.terminal = {
833
kind: "goto",
834
block: block.terminal.block,
@@ -894,6 +836,15 @@ export function removeUnnecessaryTryCatch(fn: HIR): void {
836
loc: block.terminal.loc,
837
variant: GotoVariant.Break,
838
};
839
+
840
+ if (fallthrough != null) {
841
+ if (fallthrough.preds.size === 1 && fallthrough.preds.has(handlerId)) {
842
+ // delete fallthrough
843
+ fn.blocks.delete(fallthroughId);
844
+ } else {
845
+ fallthrough.preds.delete(handlerId);
846
+ }
847
+ }
848
}
849
}
850
}
compiler/packages/babel-plugin-react-forget/src/HIR/MergeConsecutiveBlocks.ts
+6
-5
@@ -13,8 +13,8 @@ import {
13
HIRFunction,
14
Instruction,
15
} from "./HIR";
16
-import { markPredecessors, removeUnreachableFallthroughs } from "./HIRBuilder";
17
-import { mapOptionalFallthroughs, terminalFallthrough } from "./visitors";
16
+import { markPredecessors } from "./HIRBuilder";
17
+import { terminalFallthrough, terminalHasFallthrough } from "./visitors";
18
19
/*
20
* Merges sequences of blocks that will always execute consecutively —
@@ -113,10 +113,11 @@ export function mergeConsecutiveBlocks(fn: HIRFunction): void {
113
fn.body.blocks.delete(block.id);
114
}
115
markPredecessors(fn.body);
116
- for (const [, block] of fn.body.blocks) {
117
- mapOptionalFallthroughs(block.terminal, (blockId) => merged.get(blockId));
116
+ for (const [, { terminal }] of fn.body.blocks) {
117
+ if (terminalHasFallthrough(terminal)) {
118
+ terminal.fallthrough = merged.get(terminal.fallthrough);
119
+ }
120
}
119
- removeUnreachableFallthroughs(fn.body);
121
}
122
123
class MergedBlocks {
compiler/packages/babel-plugin-react-forget/src/HIR/index.ts
-1
@@ -23,7 +23,6 @@ export {
23
markInstructionIds,
24
markPredecessors,
25
removeUnnecessaryTryCatch,
26
- removeUnreachableFallthroughs,
26
reversePostorderBlocks,
27
} from "./HIRBuilder";
28
export { mergeConsecutiveBlocks } from "./MergeConsecutiveBlocks";
compiler/packages/babel-plugin-react-forget/src/HIR/visitors.ts
+22
-114
@@ -634,8 +634,7 @@ export function mapTerminalSuccessors(
634
case "if": {
635
const consequent = fn(terminal.consequent);
636
const alternate = fn(terminal.alternate);
637
- const fallthrough =
638
- terminal.fallthrough !== null ? fn(terminal.fallthrough) : null;
637
+ const fallthrough = fn(terminal.fallthrough);
638
return {
639
kind: "if",
640
test: terminal.test,
@@ -666,8 +665,7 @@ export function mapTerminalSuccessors(
665
block: target,
666
};
667
});
669
- const fallthrough =
670
- terminal.fallthrough !== null ? fn(terminal.fallthrough) : null;
668
+ const fallthrough = fn(terminal.fallthrough);
669
return {
670
kind: "switch",
671
test: terminal.test,
@@ -794,8 +792,7 @@ export function mapTerminalSuccessors(
792
}
793
case "label": {
794
const block = fn(terminal.block);
797
- const fallthrough =
798
- terminal.fallthrough !== null ? fn(terminal.fallthrough) : null;
795
+ const fallthrough = fn(terminal.fallthrough);
796
return {
797
kind: "label",
798
block,
@@ -829,8 +826,7 @@ export function mapTerminalSuccessors(
826
case "try": {
827
const block = fn(terminal.block);
828
const handler = fn(terminal.handler);
832
- const fallthrough =
833
- terminal.fallthrough !== null ? fn(terminal.fallthrough) : null;
829
+ const fallthrough = fn(terminal.fallthrough);
830
return {
831
kind: "try",
832
block,
@@ -866,12 +862,10 @@ export function mapTerminalSuccessors(
862
}
863
}
864
869
-/*
870
- * Helper to get a terminal's fallthrough. The main reason to extract this as a helper
871
- * function is to ensure that we use an exhaustive switch to ensure that we add new terminal
872
- * variants as appropriate.
873
- */
874
-export function terminalFallthrough(terminal: Terminal): BlockId | null {
865
+export function terminalHasFallthrough<
866
+ T extends Terminal,
867
+ U extends T & { fallthrough: BlockId }
868
+>(terminal: T): terminal is U {
869
switch (terminal.kind) {
870
case "maybe-throw":
871
case "branch":
@@ -880,7 +874,8 @@ export function terminalFallthrough(terminal: Terminal): BlockId | null {
874
case "throw":
875
case "unreachable":
876
case "unsupported": {
883
- return null;
877
+ const _: undefined = terminal.fallthrough;
878
+ return false;
879
}
880
case "try":
881
case "do-while":
@@ -896,7 +891,8 @@ export function terminalFallthrough(terminal: Terminal): BlockId | null {
891
case "ternary":
892
case "while":
893
case "scope": {
899
- return terminal.fallthrough;
894
+ const _: BlockId = terminal.fallthrough;
895
+ return true;
896
}
897
default: {
898
assertExhaustive(
@@ -907,104 +903,16 @@ export function terminalFallthrough(terminal: Terminal): BlockId | null {
903
}
904
}
905
910
-export function mapOptionalFallthroughs(
911
- terminal: Terminal,
912
- fn: (block: BlockId) => BlockId | null
913
-): void {
914
- switch (terminal.kind) {
915
- case "maybe-throw":
916
- case "branch":
917
- case "goto":
918
- case "return":
919
- case "throw":
920
- case "unreachable":
921
- case "unsupported": {
922
- return;
923
- }
924
- /*
925
- * NOTE: TypeScript has a bug where it does not correctly model properties whose values are
926
- * non-null in some cases and nullable in other cases, if those cases are joined together.
927
- * Thus we use one block per case here to ensure that any changes to the types will cause
928
- * a compiler error.
929
- */
930
- case "do-while": {
931
- const _: BlockId = terminal.fallthrough;
932
- break;
933
- }
934
- case "for-of": {
935
- const _: BlockId = terminal.fallthrough;
936
- break;
937
- }
938
- case "for-in": {
939
- const _: BlockId = terminal.fallthrough;
940
- break;
941
- }
942
- case "for": {
943
- const _: BlockId = terminal.fallthrough;
944
- break;
945
- }
946
- case "logical": {
947
- const _: BlockId = terminal.fallthrough;
948
- break;
949
- }
950
- case "optional": {
951
- const _: BlockId = terminal.fallthrough;
952
- break;
953
- }
954
- case "ternary": {
955
- const _: BlockId = terminal.fallthrough;
956
- break;
957
- }
958
- case "while": {
959
- const _: BlockId = terminal.fallthrough;
960
- break;
961
- }
962
- case "scope": {
963
- const _: BlockId = terminal.fallthrough;
964
- break;
965
- }
966
- case "switch": {
967
- if (terminal.fallthrough !== null) {
968
- terminal.fallthrough = fn(terminal.fallthrough);
969
- } else {
970
- terminal.fallthrough = null;
971
- }
972
- break;
973
- }
974
- case "if": {
975
- if (terminal.fallthrough !== null) {
976
- terminal.fallthrough = fn(terminal.fallthrough);
977
- } else {
978
- terminal.fallthrough = null;
979
- }
980
- break;
981
- }
982
- case "label": {
983
- if (terminal.fallthrough !== null) {
984
- terminal.fallthrough = fn(terminal.fallthrough);
985
- } else {
986
- terminal.fallthrough = null;
987
- }
988
- break;
989
- }
990
- case "sequence": {
991
- const _: BlockId = terminal.fallthrough;
992
- break;
993
- }
994
- case "try": {
995
- if (terminal.fallthrough !== null) {
996
- terminal.fallthrough = fn(terminal.fallthrough);
997
- } else {
998
- terminal.fallthrough = null;
999
- }
1000
- break;
1001
- }
1002
- default: {
1003
- assertExhaustive(
1004
- terminal,
1005
- `Unexpected terminal kind \`${(terminal as any).kind}\``
1006
- );
1007
- }
906
+/*
907
+ * Helper to get a terminal's fallthrough. The main reason to extract this as a helper
908
+ * function is to ensure that we use an exhaustive switch to ensure that we add new terminal
909
+ * variants as appropriate.
910
+ */
911
+export function terminalFallthrough(terminal: Terminal): BlockId | null {
912
+ if (terminalHasFallthrough(terminal)) {
913
+ return terminal.fallthrough;
914
+ } else {
915
+ return null;
916
}
917
}
918
compiler/packages/babel-plugin-react-forget/src/Optimization/ConstantPropagation.ts
-2
@@ -23,7 +23,6 @@ import {
23
markInstructionIds,
24
markPredecessors,
25
mergeConsecutiveBlocks,
26
- removeUnreachableFallthroughs,
26
reversePostorderBlocks,
27
} from "../HIR";
28
import {
@@ -72,7 +71,6 @@ function constantPropagationImpl(fn: HIRFunction, constants: Constants): void {
71
* Re-run minification of the graph (incl reordering instruction ids)
72
*/
73
reversePostorderBlocks(fn.body);
75
- removeUnreachableFallthroughs(fn.body);
74
removeUnreachableForUpdates(fn.body);
75
removeDeadDoWhileStatements(fn.body);
76
removeUnnecessaryTryCatch(fn.body);
compiler/packages/babel-plugin-react-forget/src/Optimization/PruneMaybeThrows.ts
-2
@@ -15,7 +15,6 @@ import {
15
assertConsistentIdentifiers,
16
assertTerminalSuccessorsExist,
17
mergeConsecutiveBlocks,
18
- removeUnreachableFallthroughs,
18
reversePostorderBlocks,
19
} from "../HIR";
20
import {
@@ -39,7 +38,6 @@ export function pruneMaybeThrows(fn: HIRFunction): void {
38
* Re-run minification of the graph (incl reordering instruction ids)
39
*/
40
reversePostorderBlocks(fn.body);
42
- removeUnreachableFallthroughs(fn.body);
41
removeUnreachableForUpdates(fn.body);
42
removeDeadDoWhileStatements(fn.body);
43
removeUnnecessaryTryCatch(fn.body);
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/BuildReactiveFunction.ts
+19
-20
@@ -112,7 +112,7 @@ class Driver {
112
}
113
case "if": {
114
const fallthroughId =
115
- terminal.fallthrough !== null &&
115
+ this.cx.reachable(terminal.fallthrough) &&
116
!this.cx.isScheduled(terminal.fallthrough)
117
? terminal.fallthrough
118
: null;
@@ -176,7 +176,7 @@ class Driver {
176
}
177
case "switch": {
178
const fallthroughId =
179
- terminal.fallthrough !== null &&
179
+ this.cx.reachable(terminal.fallthrough) &&
180
!this.cx.isScheduled(terminal.fallthrough)
181
? terminal.fallthrough
182
: null;
@@ -289,7 +289,7 @@ class Driver {
289
}
290
case "while": {
291
const fallthroughId =
292
- terminal.fallthrough !== null &&
292
+ this.cx.reachable(terminal.fallthrough) &&
293
!this.cx.isScheduled(terminal.fallthrough)
294
? terminal.fallthrough
295
: null;
@@ -350,11 +350,9 @@ class Driver {
350
? terminal.loop
351
: null;
352
353
- const fallthroughId =
354
- terminal.fallthrough !== null &&
355
- !this.cx.isScheduled(terminal.fallthrough)
356
- ? terminal.fallthrough
357
- : null;
353
+ const fallthroughId = !this.cx.isScheduled(terminal.fallthrough)
354
+ ? terminal.fallthrough
355
+ : null;
356
357
const scheduleId = this.cx.scheduleLoop(
358
terminal.fallthrough,
@@ -437,11 +435,9 @@ class Driver {
435
? terminal.loop
436
: null;
437
440
- const fallthroughId =
441
- terminal.fallthrough !== null &&
442
- !this.cx.isScheduled(terminal.fallthrough)
443
- ? terminal.fallthrough
444
- : null;
438
+ const fallthroughId = !this.cx.isScheduled(terminal.fallthrough)
439
+ ? terminal.fallthrough
440
+ : null;
441
442
const scheduleId = this.cx.scheduleLoop(
443
terminal.fallthrough,
@@ -512,11 +508,9 @@ class Driver {
508
? terminal.loop
509
: null;
510
515
- const fallthroughId =
516
- terminal.fallthrough !== null &&
517
- !this.cx.isScheduled(terminal.fallthrough)
518
- ? terminal.fallthrough
519
- : null;
511
+ const fallthroughId = !this.cx.isScheduled(terminal.fallthrough)
512
+ ? terminal.fallthrough
513
+ : null;
514
515
const scheduleId = this.cx.scheduleLoop(
516
terminal.fallthrough,
@@ -626,7 +620,7 @@ class Driver {
620
}
621
case "label": {
622
const fallthroughId =
629
- terminal.fallthrough !== null &&
623
+ this.cx.reachable(terminal.fallthrough) &&
624
!this.cx.isScheduled(terminal.fallthrough)
625
? terminal.fallthrough
626
: null;
@@ -747,7 +741,7 @@ class Driver {
741
}
742
case "try": {
743
const fallthroughId =
750
- terminal.fallthrough !== null &&
744
+ this.cx.reachable(terminal.fallthrough) &&
745
!this.cx.isScheduled(terminal.fallthrough)
746
? terminal.fallthrough
747
: null;
@@ -1241,6 +1235,11 @@ class Context {
1235
this.#catchHandlers.add(block);
1236
}
1237
1238
+ reachable(id: BlockId): boolean {
1239
+ const block = this.ir.blocks.get(id)!;
1240
+ return block.terminal.kind !== "unreachable";
1241
+ }
1242
+
1243
/*
1244
* Record that the given block will be emitted (eg by the codegen of a parent node)
1245
* so that child nodes can avoid re-emitting it.
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-in-nested-scope.expect.md
+25
-17
@@ -40,38 +40,46 @@ import { unstable_useMemoCache as useMemoCache } from "react";
40
import { mutate, setProperty, throwErrorWithMessageIf } from "shared-runtime";
41
42
function useFoo(t0) {
43
- const $ = useMemoCache(3);
43
+ const $ = useMemoCache(6);
44
const { value, cond } = t0;
45
+ let y;
46
let t1;
47
if ($[0] !== value || $[1] !== cond) {
48
t1 = Symbol.for("react.early_return_sentinel");
49
bb0: {
49
- const y = [value];
50
- const x = { cond };
51
- try {
52
- mutate(x);
53
- throwErrorWithMessageIf(x.cond, "error");
54
- } catch {
55
- setProperty(x, "henderson");
56
- t1 = x;
57
- break bb0;
58
- }
50
+ y = [value];
51
+ let x;
52
+ if ($[4] !== cond) {
53
+ x = { cond };
54
+ try {
55
+ mutate(x);
56
+ throwErrorWithMessageIf(x.cond, "error");
57
+ } catch {
58
+ setProperty(x, "henderson");
59
+ t1 = x;
60
+ break bb0;
61
+ }
62
60
- setProperty(x, "nevada");
63
+ setProperty(x, "nevada");
64
+ $[4] = cond;
65
+ $[5] = x;
66
+ } else {
67
+ x = $[5];
68
+ }
69
y.push(x);
62
-
63
- t1 = y;
64
- break bb0;
70
}
71
$[0] = value;
72
$[1] = cond;
68
- $[2] = t1;
73
+ $[2] = y;
74
+ $[3] = t1;
75
} else {
70
- t1 = $[2];
76
+ y = $[2];
77
+ t1 = $[3];
78
}
79
if (t1 !== Symbol.for("react.early_return_sentinel")) {
80
return t1;
81
}
82
+ return y;
83
}
84
85
export const FIXTURE_ENTRYPOINT = {
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-try-value-modified-in-catch.expect.md
+1
-3
@@ -45,9 +45,6 @@ function Component(props) {
45
t0 = e;
46
break bb0;
47
}
48
-
49
- t0 = null;
50
- break bb0;
48
}
49
$[0] = props.y;
50
$[1] = props.e;
@@ -58,6 +55,7 @@ function Component(props) {
55
if (t0 !== Symbol.for("react.early_return_sentinel")) {
56
return t0;
57
}
58
+ return null;
59
}
60
61
export const FIXTURE_ENTRYPOINT = {
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-with-catch-param.expect.md
+8
-7
@@ -31,12 +31,13 @@ import { unstable_useMemoCache as useMemoCache } from "react";
31
const { throwInput } = require("shared-runtime");
32
33
function Component(props) {
34
- const $ = useMemoCache(1);
34
+ const $ = useMemoCache(2);
35
+ let x;
36
let t0;
37
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
38
t0 = Symbol.for("react.early_return_sentinel");
39
bb0: {
39
- const x = [];
40
+ x = [];
41
try {
42
throwInput(x);
43
} catch (t1) {
@@ -45,17 +46,17 @@ function Component(props) {
46
t0 = e;
47
break bb0;
48
}
48
-
49
- t0 = x;
50
- break bb0;
49
}
52
- $[0] = t0;
50
+ $[0] = x;
51
+ $[1] = t0;
52
} else {
54
- t0 = $[0];
53
+ x = $[0];
54
+ t0 = $[1];
55
}
56
if (t0 !== Symbol.for("react.early_return_sentinel")) {
57
return t0;
58
}
59
+ return x;
60
}
61
62
export const FIXTURE_ENTRYPOINT = {