mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 05:10:25 +00:00
[dart2wasm] Run finally
blocks on break
and continue
Fixes https://github.com/dart-lang/sdk/issues/50026 No new tests added: existing tests cover this. These tests now pass: co19/Language/Statements/Break/execution_t01 co19/Language/Statements/Break/execution_t02 co19/Language/Statements/Break/execution_t04 co19/Language/Statements/Break/execution_t06 co19/Language/Statements/Break/execution_t07 co19/Language/Statements/Break/execution_t08 co19/Language/Statements/Break/execution_t09 co19/Language/Statements/Continue/control_transfer_t01 co19/Language/Statements/Continue/control_transfer_t02 co19/Language/Statements/Continue/control_transfer_t03 co19/Language/Statements/Continue/control_transfer_t04 co19/Language/Statements/Continue/control_transfer_t05 co19/Language/Statements/Continue/control_transfer_t07 co19/Language/Statements/Continue/control_transfer_t08 co19/Language/Statements/Continue/control_transfer_t09 language/exception/finally12_test language/exception/finally3_test language/exception/finally9_test language/exception/try_catch4_test language/exception/try_finally_regress_25654_test Change-Id: I7172d7dac9e58d37c2ecfbc1bde495d8d045fc74 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/262240 Reviewed-by: Joshua Litt <joshualitt@google.com> Commit-Queue: Ömer Ağacan <omersa@google.com> Reviewed-by: Aske Simon Christensen <askesc@google.com>
This commit is contained in:
parent
88539e58ea
commit
d968be9021
|
@ -53,9 +53,17 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
w.Local? preciseThisLocal;
|
||||
w.Local? returnValueLocal;
|
||||
final Map<TypeParameter, w.Local> typeLocals = {};
|
||||
final List<TryBlockFinalizer> finalizers = [];
|
||||
|
||||
/// Finalizers to run on `return`.
|
||||
final List<TryBlockFinalizer> returnFinalizers = [];
|
||||
|
||||
/// Finalizers to run on a `break`. `breakFinalizers[L].last` (which should
|
||||
/// always be present) is the `br` target for the label `L` that will run the
|
||||
/// finalizers, or break out of the loop.
|
||||
final Map<LabeledStatement, List<w.Label>> breakFinalizers = {};
|
||||
|
||||
final List<w.Label> tryLabels = [];
|
||||
final Map<LabeledStatement, w.Label> labels = {};
|
||||
|
||||
final Map<SwitchCase, w.Label> switchLabels = {};
|
||||
|
||||
/// Maps a switch statement to the information used when doing a backward
|
||||
|
@ -726,15 +734,15 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
@override
|
||||
void visitLabeledStatement(LabeledStatement node) {
|
||||
w.Label label = b.block();
|
||||
labels[node] = label;
|
||||
breakFinalizers[node] = <w.Label>[label];
|
||||
visitStatement(node.body);
|
||||
labels.remove(node);
|
||||
breakFinalizers.remove(node);
|
||||
b.end();
|
||||
}
|
||||
|
||||
@override
|
||||
void visitBreakStatement(BreakStatement node) {
|
||||
b.br(labels[node.target]!);
|
||||
b.br(breakFinalizers[node.target]!.last);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -867,30 +875,64 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
|
||||
@override
|
||||
void visitTryFinally(TryFinally node) {
|
||||
// We lower a [TryFinally] to three nested blocks, and we emit the finalizer
|
||||
// up to three times. Once in a catch, to handle the case where the try
|
||||
// We lower a [TryFinally] to a number of nested blocks, depending on how
|
||||
// many different code paths we have that run the finally block.
|
||||
//
|
||||
// We emit the finalizer once in a catch, to handle the case where the try
|
||||
// throws. Once outside of the catch, to handle the case where the try does
|
||||
// not throw. Finally, if there is a return within the try block, then we
|
||||
// emit the finalizer one more time along with logic to continue walking up
|
||||
// the stack.
|
||||
// not throw. If there is a return within the try block, then we emit the
|
||||
// finalizer one more time along with logic to continue walking up the
|
||||
// stack.
|
||||
//
|
||||
// A `break L` can run more than one finalizer, and each of those
|
||||
// finalizers will need to be run in a different `try` block. So for each
|
||||
// wrapping label we generate a block to run the finalizer on `break` and
|
||||
// then branch to the right Wasm block to either run the next finalizer or
|
||||
// break.
|
||||
|
||||
// The block for the try-finally statement. Used as `br` target in normal
|
||||
// execution after the finalizer (no throws, returns, or breaks).
|
||||
w.Label tryFinallyBlock = b.block();
|
||||
w.Label finalizerBlock = b.block();
|
||||
finalizers.add(TryBlockFinalizer(finalizerBlock));
|
||||
|
||||
// Create one block for each wrapping label
|
||||
for (final labelBlocks in breakFinalizers.values) {
|
||||
labelBlocks.add(b.block());
|
||||
}
|
||||
|
||||
// Continuation of this block runs the finalizer and returns (or jumps to
|
||||
// the next finalizer block). Used as `br` target on `return`.
|
||||
w.Label returnFinalizerBlock = b.block();
|
||||
returnFinalizers.add(TryBlockFinalizer(returnFinalizerBlock));
|
||||
|
||||
w.Label tryBlock = b.try_();
|
||||
visitStatement(node.body);
|
||||
bool mustHandleReturn = finalizers.removeLast().mustHandleReturn;
|
||||
b.br(tryBlock);
|
||||
final bool mustHandleReturn =
|
||||
returnFinalizers.removeLast().mustHandleReturn;
|
||||
b.catch_(translator.exceptionTag);
|
||||
|
||||
// `break` statements in the current finalizer and the rest will not run
|
||||
// the current finalizer, update the `break` targets
|
||||
final removedBreakTargets = <LabeledStatement, w.Label>{};
|
||||
for (final breakFinalizerEntry in breakFinalizers.entries) {
|
||||
removedBreakTargets[breakFinalizerEntry.key] =
|
||||
breakFinalizerEntry.value.removeLast();
|
||||
}
|
||||
|
||||
// Run finalizer on exception
|
||||
visitStatement(node.finalizer);
|
||||
b.rethrow_(tryBlock);
|
||||
b.end(); // end tryBlock.
|
||||
|
||||
// Run finalizer on normal execution (no breaks, throws, or returns)
|
||||
visitStatement(node.finalizer);
|
||||
b.br(tryFinallyBlock);
|
||||
b.end(); // end finalizerBlock.
|
||||
b.end(); // end returnFinalizerBlock.
|
||||
|
||||
// Run finalizer on `return`
|
||||
if (mustHandleReturn) {
|
||||
visitStatement(node.finalizer);
|
||||
if (finalizers.isNotEmpty) {
|
||||
b.br(finalizers.last.label);
|
||||
if (returnFinalizers.isNotEmpty) {
|
||||
b.br(returnFinalizers.last.label);
|
||||
} else {
|
||||
if (returnValueLocal != null) {
|
||||
b.local_get(returnValueLocal!);
|
||||
|
@ -899,7 +941,16 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
_returnFromFunction();
|
||||
}
|
||||
}
|
||||
b.end(); // end tryFinallyBlock.
|
||||
|
||||
// Generate finalizers for `break`s in the `try` block
|
||||
for (final removedBreakTargetEntry in removedBreakTargets.entries) {
|
||||
b.end();
|
||||
visitStatement(node.finalizer);
|
||||
b.br(breakFinalizers[removedBreakTargetEntry.key]!.last);
|
||||
}
|
||||
|
||||
// Terminate `tryFinallyBlock`
|
||||
b.end();
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -1077,8 +1128,8 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
// as the stack unwinds. When we get to the top of the finalizer stack, we
|
||||
// will handle the return using [returnValueLocal] if this function returns
|
||||
// a value.
|
||||
if (finalizers.isNotEmpty) {
|
||||
for (TryBlockFinalizer finalizer in finalizers) {
|
||||
if (returnFinalizers.isNotEmpty) {
|
||||
for (TryBlockFinalizer finalizer in returnFinalizers) {
|
||||
finalizer.mustHandleReturn = true;
|
||||
}
|
||||
if (returnType != voidMarker) {
|
||||
|
@ -1088,7 +1139,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
returnValueLocal ??= addLocal(returnType.withNullability(true));
|
||||
b.local_set(returnValueLocal!);
|
||||
}
|
||||
b.br(finalizers.last.label);
|
||||
b.br(returnFinalizers.last.label);
|
||||
} else {
|
||||
_returnFromFunction();
|
||||
}
|
||||
|
@ -2411,7 +2462,12 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
}
|
||||
|
||||
class TryBlockFinalizer {
|
||||
/// `br` target to run the finalizer
|
||||
final w.Label label;
|
||||
|
||||
/// Whether the last finalizer in the chain should return. When this is
|
||||
/// `false` the block won't be used, as the block is for running finalizers
|
||||
/// when returning.
|
||||
bool mustHandleReturn = false;
|
||||
|
||||
TryBlockFinalizer(this.label);
|
||||
|
|
Loading…
Reference in a new issue