@@ -90,7 +90,8 @@ abstract class AstCodeGenerator
9090 final LinkedHashMap <LabeledStatement , List <w.Label >> breakFinalizers =
9191 LinkedHashMap ();
9292
93- final List <w.Label > tryLabels = [];
93+ final List <({w.Local exceptionLocal, w.Local stackTraceLocal})>
94+ tryBlockLocals = [];
9495
9596 final Map <SwitchCase , w.Label > switchLabels = {};
9697
@@ -903,33 +904,48 @@ abstract class AstCodeGenerator
903904
904905 @override
905906 void visitTryCatch (TryCatch node) {
906- // It is not valid dart to have a try without a catch.
907+ // It is not valid Dart to have a try without a catch.
907908 assert (node.catches.isNotEmpty);
908909
909- // We lower a [TryCatch] to a wasm try block.
910- w.Label try_ = b.try_ ();
911- translateStatement (node.body);
912- b.br (try_);
910+ final w.RefType exceptionType = translator.topInfo.nonNullableType;
911+ final w.RefType stackTraceType =
912+ translator.stackTraceInfo.repr.nonNullableType;
913+
914+ final w.Label wrapperBlock = b.block ();
915+
916+ // Create a block target for each Dart `catch` block, to be able to share
917+ // code when generating a `catch` and `catch_all` for the same Dart `catch`
918+ // block, when the block can catch both Dart and JS exceptions.
919+ // The `end` for the Wasm `try` block works as the first exception handler
920+ // target.
921+ List <w.Label > catchBlockLabels = List .generate (node.catches.length - 1 ,
922+ (i) => b.block ([], [exceptionType, stackTraceType]),
923+ growable: true );
924+
925+ w.Label try_ = b.try_ ([], [exceptionType, stackTraceType]);
926+ catchBlockLabels.add (try_);
913927
914- // Note: We must wait to add the try block to the [tryLabels] stack until
915- // after we have visited the body of the try. This is to handle the case of
916- // a rethrow nested within a try nested within a catch, that is we need the
917- // rethrow to target the last try block with a catch.
918- tryLabels.add (try_);
928+ catchBlockLabels = catchBlockLabels.reversed.toList ();
929+
930+ translateStatement (node.body);
931+ b.br (wrapperBlock);
919932
920933 // Stash the original exception in a local so we can push it back onto the
921934 // stack after each type test. Also, store the stack trace in a local.
922- w.Local thrownException = addLocal (translator.topInfo.nonNullableType);
923- w.Local thrownStackTrace =
924- addLocal (translator.stackTraceInfo.repr.nonNullableType);
935+ w.Local thrownException = addLocal (exceptionType);
936+ w.Local thrownStackTrace = addLocal (stackTraceType);
937+
938+ tryBlockLocals.add (
939+ (exceptionLocal: thrownException, stackTraceLocal: thrownStackTrace));
925940
926- void emitCatchBlock (Catch catch_, bool emitGuard) {
941+ void emitCatchBlock (
942+ w.Label catchBlockTarget, Catch catch_, bool emitGuard) {
927943 // For each catch node:
928944 // 1) Create a block for the catch.
929945 // 2) Push the caught exception onto the stack.
930946 // 3) Add a type test based on the guard of the catch.
931947 // 4) If the test fails, we jump to the next catch. Otherwise, we
932- // execute the body of the catch.
948+ // jump to the block for the body of the catch.
933949 w.Label catchBlock = b.block ();
934950 DartType guard = catch_.guard;
935951
@@ -942,29 +958,10 @@ abstract class AstCodeGenerator
942958 b.br_if (catchBlock);
943959 }
944960
945- final VariableDeclaration ? exceptionDeclaration = catch_.exception;
946- if (exceptionDeclaration != null ) {
947- initializeVariable (exceptionDeclaration, () {
948- b.local_get (thrownException);
949- // Type test passed, downcast the exception to the expected type.
950- translator.convertType (
951- b,
952- thrownException.type,
953- translator.translateType (exceptionDeclaration.type),
954- );
955- });
956- }
957-
958- final VariableDeclaration ? stackTraceDeclaration = catch_.stackTrace;
959- if (stackTraceDeclaration != null ) {
960- initializeVariable (
961- stackTraceDeclaration, () => b.local_get (thrownStackTrace));
962- }
963-
964- translateStatement (catch_.body);
961+ b.local_get (thrownException);
962+ b.local_get (thrownStackTrace);
963+ b.br (catchBlockTarget);
965964
966- // Jump out of the try entirely if we enter any catch block.
967- b.br (try_);
968965 b.end (); // end catchBlock.
969966 }
970967
@@ -974,26 +971,29 @@ abstract class AstCodeGenerator
974971
975972 b.local_set (thrownStackTrace);
976973 b.local_set (thrownException);
977- for (final Catch catch_ in node.catches) {
974+ for (int catchBlockIndex = 0 ;
975+ catchBlockIndex < node.catches.length;
976+ catchBlockIndex += 1 ) {
977+ final catch_ = node.catches[catchBlockIndex];
978978 // Only insert type checks if the guard is not `Object`
979979 final bool shouldEmitGuard =
980980 catch_.guard != translator.coreTypes.objectNonNullableRawType;
981- emitCatchBlock (catch_, shouldEmitGuard);
981+ emitCatchBlock (
982+ catchBlockLabels[catchBlockIndex], catch_, shouldEmitGuard);
982983 if (! shouldEmitGuard) {
983984 // If we didn't emit a guard, we won't ever fall through to the
984985 // following catch blocks.
985986 break ;
986987 }
987988 }
989+
988990 // Rethrow if all the catch blocks fall through
989991 b.rethrow_ (try_);
990992
991993 // If we have a catches that are generic enough to catch a JavaScript
992994 // error, we need to put that into a catch_all block.
993- final Iterable <Catch > catchAllCatches = node.catches
994- .where ((c) => guardCanMatchJSException (translator, c.guard));
995-
996- if (catchAllCatches.isNotEmpty) {
995+ if (node.catches
996+ .any ((c) => guardCanMatchJSException (translator, c.guard))) {
997997 // This catches any objects that aren't dart exceptions, such as
998998 // JavaScript exceptions or objects.
999999 b.catch_all ();
@@ -1007,14 +1007,21 @@ abstract class AstCodeGenerator
10071007 call (translator.javaScriptErrorFactory.reference);
10081008 b.local_set (thrownException);
10091009
1010- for (final c in catchAllCatches) {
1010+ for (int catchBlockIndex = 0 ;
1011+ catchBlockIndex < node.catches.length;
1012+ catchBlockIndex += 1 ) {
1013+ final catch_ = node.catches[catchBlockIndex];
1014+ if (! guardCanMatchJSException (translator, catch_.guard)) {
1015+ continue ;
1016+ }
10111017 // Type guards based on a type parameter are special, in that we cannot
10121018 // statically determine whether a JavaScript error will always satisfy
10131019 // the guard, so we should emit the type checking code for it. All
10141020 // other guards will always match a JavaScript error, however, so no
10151021 // need to emit type checks for those.
1016- final bool shouldEmitGuard = c.guard is TypeParameterType ;
1017- emitCatchBlock (c, shouldEmitGuard);
1022+ final bool shouldEmitGuard = catch_.guard is TypeParameterType ;
1023+ emitCatchBlock (
1024+ catchBlockLabels[catchBlockIndex], catch_, shouldEmitGuard);
10181025 if (! shouldEmitGuard) {
10191026 // If we didn't emit a guard, we won't ever fall through to the
10201027 // following catch blocks.
@@ -1026,8 +1033,36 @@ abstract class AstCodeGenerator
10261033 b.rethrow_ (try_);
10271034 }
10281035
1029- tryLabels.removeLast ();
1030- b.end (); // end try_.
1036+ for (Catch catch_ in node.catches) {
1037+ b.end ();
1038+ b.local_set (thrownStackTrace);
1039+ b.local_set (thrownException);
1040+
1041+ final VariableDeclaration ? exceptionDeclaration = catch_.exception;
1042+ if (exceptionDeclaration != null ) {
1043+ initializeVariable (exceptionDeclaration, () {
1044+ b.local_get (thrownException);
1045+ // Type test passed, downcast the exception to the expected type.
1046+ translator.convertType (
1047+ b,
1048+ thrownException.type,
1049+ translator.translateType (exceptionDeclaration.type),
1050+ );
1051+ });
1052+ }
1053+
1054+ final VariableDeclaration ? stackTraceDeclaration = catch_.stackTrace;
1055+ if (stackTraceDeclaration != null ) {
1056+ initializeVariable (
1057+ stackTraceDeclaration, () => b.local_get (thrownStackTrace));
1058+ }
1059+
1060+ translateStatement (catch_.body);
1061+ b.br (wrapperBlock);
1062+ }
1063+
1064+ tryBlockLocals.removeLast ();
1065+ b.end (); // end tryWrapper
10311066 }
10321067
10331068 @override
@@ -2721,7 +2756,10 @@ abstract class AstCodeGenerator
27212756
27222757 @override
27232758 w.ValueType visitRethrow (Rethrow node, w.ValueType expectedType) {
2724- b.rethrow_ (tryLabels.last);
2759+ final exceptionLocals = tryBlockLocals.last;
2760+ b.local_get (exceptionLocals.exceptionLocal);
2761+ b.local_get (exceptionLocals.stackTraceLocal);
2762+ b.throw_ (translator.getExceptionTag (b.module));
27252763 return expectedType;
27262764 }
27272765
0 commit comments