mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 06:20:13 +00:00
[dart2wasm] Handle exceptions and returns in async* functions
New passing tests: - co19/Language/Expressions/Function_Invocation/async_cleanup_t07 - co19/Language/Expressions/Function_Invocation/async_cleanup_t08 - co19/Language/Expressions/Function_Invocation/async_generator_invokation_t05 - co19/Language/Expressions/Function_Invocation/async_generator_invokation_t09 - co19/Language/Statements/Rethrow/execution_t04 - co19/Language/Statements/Return/syntax_t10 - co19/Language/Statements/Return/syntax_t11 - co19/Language/Statements/Return/syntax_t12 - co19/Language/Statements/Return/syntax_t13 - co19/Language/Statements/Yield_and_Yield_Each/Yield_Each/execution_async_A01_t01 - language/async/return_types_runtime_test - language/async_star/basic_test - language/async_star/yield_statement_context_test - lib/async/stream_from_iterable_test Change-Id: Id03cc0abe150dadfcd753c4e74a282d46260a1f8 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/304501 Reviewed-by: Joshua Litt <joshualitt@google.com> Commit-Queue: Ömer Ağacan <omersa@google.com>
This commit is contained in:
parent
1e1258e595
commit
9d1dd89768
3 changed files with 125 additions and 63 deletions
|
@ -1068,7 +1068,7 @@ class AsyncCodeGenerator extends CodeGenerator {
|
|||
// directly, instead of via throw/catch. Would that be faster?
|
||||
exceptionHandlers.forEachFinalizer(
|
||||
(finalizer, _last) => finalizer.setContinuationRethrow(
|
||||
() => _getVariable(catch_.exception!),
|
||||
() => _getVariableBoxed(catch_.exception!),
|
||||
() => _getVariable(catch_.stackTrace!),
|
||||
));
|
||||
b.throw_(translator.exceptionTag);
|
||||
|
@ -1304,7 +1304,7 @@ class AsyncCodeGenerator extends CodeGenerator {
|
|||
|
||||
exceptionHandlers.forEachFinalizer((finalizer, _last) {
|
||||
finalizer.setContinuationRethrow(
|
||||
() => _getVariable(catchVars.exception),
|
||||
() => _getVariableBoxed(catchVars.exception),
|
||||
() => _getVariable(catchVars.stackTrace),
|
||||
);
|
||||
});
|
||||
|
@ -1413,24 +1413,33 @@ class AsyncCodeGenerator extends CodeGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
void _getVariable(VariableDeclaration variable) {
|
||||
w.ValueType _getVariable(VariableDeclaration variable) {
|
||||
final w.Local? local = locals[variable];
|
||||
final Capture? capture = closures.captures[variable];
|
||||
if (capture != null) {
|
||||
if (!capture.written && local != null) {
|
||||
b.local_get(local);
|
||||
return local.type;
|
||||
} else {
|
||||
b.local_get(capture.context.currentLocal);
|
||||
b.struct_get(capture.context.struct, capture.fieldIndex);
|
||||
return capture.context.struct.fields[capture.fieldIndex].type.unpacked;
|
||||
}
|
||||
} else {
|
||||
if (local == null) {
|
||||
throw "Write of undefined variable ${variable}";
|
||||
}
|
||||
b.local_get(local);
|
||||
return local.type;
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as [_getVariable], but boxes the value if it's not already boxed.
|
||||
void _getVariableBoxed(VariableDeclaration variable) {
|
||||
final varType = _getVariable(variable);
|
||||
translator.convertType(function, varType, translator.topInfo.nullableType);
|
||||
}
|
||||
|
||||
void _getCurrentException() {
|
||||
b.local_get(suspendStateLocal);
|
||||
b.struct_get(asyncSuspendStateInfo.struct,
|
||||
|
|
|
@ -330,21 +330,26 @@ class _WasmTransformer extends Transformer {
|
|||
// Future<void> Function() #body = () async {
|
||||
// Completer<bool> #completer = Completer<bool>();
|
||||
// #controller.add(#completer);
|
||||
// await #completer.future;
|
||||
// ...
|
||||
// #controller.add(i);
|
||||
// #completer = Completer<bool>();
|
||||
// #controller.add(#completer)
|
||||
// await #completer.future;
|
||||
// ...
|
||||
// await for (var i in bar) {
|
||||
// try {
|
||||
// await #completer.future;
|
||||
// ...
|
||||
// #controller.add(i);
|
||||
// #completer = Completer<bool>();
|
||||
// #controller.add(#completer)
|
||||
// await #completer.future;
|
||||
// ...
|
||||
// await for (var i in bar) {
|
||||
// #controller.add(i);
|
||||
// #completer = Completer<bool>();
|
||||
// #controller.add(#completer)
|
||||
// await #completer.future;
|
||||
// }
|
||||
// ...
|
||||
// } catch (e) {
|
||||
// #controller.addError(e);
|
||||
// } finally {
|
||||
// #controller.close();
|
||||
// }
|
||||
// ...
|
||||
// #controller.close();
|
||||
// };
|
||||
// bool isEven = false;
|
||||
// bool isFirst = true;
|
||||
|
@ -411,15 +416,45 @@ class _WasmTransformer extends Transformer {
|
|||
functionNode.body?.accept<TreeNode>(this) as Statement?;
|
||||
_asyncStarFrames.removeLast();
|
||||
|
||||
// Try-catch-finally around the body to call `controller.addError` and
|
||||
// `controller.close`.
|
||||
final exceptionVar = VariableDeclaration(null, isSynthesized: true);
|
||||
final Procedure controllerAddErrorProc = coreTypes.index
|
||||
.getProcedure('dart:async', 'StreamController', 'addError');
|
||||
final FunctionType controllerAddErrorType =
|
||||
Substitution.fromInterfaceType(controllerNullableObjectType)
|
||||
.substituteType(controllerAddErrorProc.function
|
||||
.computeThisFunctionType(Nullability.nonNullable))
|
||||
as FunctionType;
|
||||
final tryCatch = TryCatch(
|
||||
Block([
|
||||
ExpressionStatement(_awaitCompleterFuture(completer, fileOffset)),
|
||||
if (transformedBody != null) transformedBody,
|
||||
]),
|
||||
[
|
||||
Catch(
|
||||
exceptionVar,
|
||||
ExpressionStatement(InstanceInvocation(
|
||||
InstanceAccessKind.Instance,
|
||||
VariableGet(controller),
|
||||
Name('addError'),
|
||||
Arguments([VariableGet(exceptionVar)]),
|
||||
interfaceTarget: controllerAddErrorProc,
|
||||
functionType: controllerAddErrorType,
|
||||
)),
|
||||
)
|
||||
],
|
||||
);
|
||||
final tryFinally =
|
||||
TryFinally(tryCatch, ExpressionStatement(callControllerClose));
|
||||
|
||||
// Locally declare body function.
|
||||
final bodyFunction = FunctionNode(
|
||||
Block([
|
||||
completer,
|
||||
ExpressionStatement(
|
||||
_addCompleterToController(controller, completer, fileOffset)),
|
||||
ExpressionStatement(_awaitCompleterFuture(completer, fileOffset)),
|
||||
if (transformedBody != null) transformedBody,
|
||||
ExpressionStatement(callControllerClose),
|
||||
tryFinally,
|
||||
]),
|
||||
futureValueType: const VoidType(),
|
||||
returnType: InterfaceType(
|
||||
|
|
|
@ -19,61 +19,79 @@ static method asyncMethod(asy::Stream<core::int> stream) → asy::Stream<core::i
|
|||
dynamic :async_temporary_5;
|
||||
synthesized asy::Completer<core::bool> #completer = asy::Completer::•<core::bool>();
|
||||
#controller.{asy::StreamController::add}(#completer){(core::Object?) → void};
|
||||
:async_temporary_0 = await #completer.{asy::Completer::future}{asy::Future<core::bool>};
|
||||
:async_temporary_0 as dynamic;
|
||||
{
|
||||
{
|
||||
#controller.{asy::StreamController::add}(1){(core::Object?) → void};
|
||||
#completer = asy::Completer::•<core::bool>();
|
||||
#controller.{asy::StreamController::add}(#completer){(core::Object?) → void};
|
||||
:async_temporary_1 = await #completer.{asy::Completer::future}{asy::Future<core::bool>};
|
||||
:async_temporary_1 as dynamic;
|
||||
}
|
||||
{
|
||||
#controller.{asy::StreamController::add}(2){(core::Object?) → void};
|
||||
#completer = asy::Completer::•<core::bool>();
|
||||
#controller.{asy::StreamController::add}(#completer){(core::Object?) → void};
|
||||
:async_temporary_2 = await #completer.{asy::Completer::future}{asy::Future<core::bool>};
|
||||
:async_temporary_2 as dynamic;
|
||||
}
|
||||
{
|
||||
synthesized asy::_StreamIterator<core::int> #forIterator = new asy::_StreamIterator::•<core::int>(stream);
|
||||
synthesized core::bool #jumpSentinel = #C1;
|
||||
{
|
||||
core::int #t1 = 0;
|
||||
core::Object #t2;
|
||||
core::StackTrace #t3;
|
||||
try {
|
||||
#L1:
|
||||
for (; ; ) {
|
||||
:async_temporary_4 = await #forIterator.{asy::_StreamIterator::moveNext}(){() → asy::Future<core::bool>};
|
||||
if(#jumpSentinel = :async_temporary_4 as dynamic) {
|
||||
synthesized core::int #awaitForVar = #forIterator.{asy::_StreamIterator::current}{core::int};
|
||||
{
|
||||
#controller.{asy::StreamController::add}(#awaitForVar){(core::Object?) → void};
|
||||
#completer = asy::Completer::•<core::bool>();
|
||||
#controller.{asy::StreamController::add}(#completer){(core::Object?) → void};
|
||||
:async_temporary_3 = await #completer.{asy::Completer::future}{asy::Future<core::bool>};
|
||||
:async_temporary_3 as dynamic;
|
||||
core::int #t1 = 0;
|
||||
core::Object #t2;
|
||||
core::StackTrace #t3;
|
||||
try
|
||||
try {
|
||||
:async_temporary_0 = await #completer.{asy::Completer::future}{asy::Future<core::bool>};
|
||||
:async_temporary_0 as dynamic;
|
||||
{
|
||||
{
|
||||
#controller.{asy::StreamController::add}(1){(core::Object?) → void};
|
||||
#completer = asy::Completer::•<core::bool>();
|
||||
#controller.{asy::StreamController::add}(#completer){(core::Object?) → void};
|
||||
:async_temporary_1 = await #completer.{asy::Completer::future}{asy::Future<core::bool>};
|
||||
:async_temporary_1 as dynamic;
|
||||
}
|
||||
{
|
||||
#controller.{asy::StreamController::add}(2){(core::Object?) → void};
|
||||
#completer = asy::Completer::•<core::bool>();
|
||||
#controller.{asy::StreamController::add}(#completer){(core::Object?) → void};
|
||||
:async_temporary_2 = await #completer.{asy::Completer::future}{asy::Future<core::bool>};
|
||||
:async_temporary_2 as dynamic;
|
||||
}
|
||||
{
|
||||
synthesized asy::_StreamIterator<core::int> #forIterator = new asy::_StreamIterator::•<core::int>(stream);
|
||||
synthesized core::bool #jumpSentinel = #C1;
|
||||
{
|
||||
core::int #t4 = 0;
|
||||
core::Object #t5;
|
||||
core::StackTrace #t6;
|
||||
try {
|
||||
#L1:
|
||||
for (; ; ) {
|
||||
:async_temporary_4 = await #forIterator.{asy::_StreamIterator::moveNext}(){() → asy::Future<core::bool>};
|
||||
if(#jumpSentinel = :async_temporary_4 as dynamic) {
|
||||
synthesized core::int #awaitForVar = #forIterator.{asy::_StreamIterator::current}{core::int};
|
||||
{
|
||||
#controller.{asy::StreamController::add}(#awaitForVar){(core::Object?) → void};
|
||||
#completer = asy::Completer::•<core::bool>();
|
||||
#controller.{asy::StreamController::add}(#completer){(core::Object?) → void};
|
||||
:async_temporary_3 = await #completer.{asy::Completer::future}{asy::Future<core::bool>};
|
||||
:async_temporary_3 as dynamic;
|
||||
}
|
||||
}
|
||||
else
|
||||
break #L1;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if(#jumpSentinel) {
|
||||
:async_temporary_5 = await #forIterator.{asy::_StreamIterator::cancel}(){() → asy::Future<dynamic>};
|
||||
:async_temporary_5;
|
||||
}
|
||||
#t4;
|
||||
#t5;
|
||||
#t6;
|
||||
}
|
||||
}
|
||||
else
|
||||
break #L1;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if(#jumpSentinel) {
|
||||
:async_temporary_5 = await #forIterator.{asy::_StreamIterator::cancel}(){() → asy::Future<dynamic>};
|
||||
:async_temporary_5;
|
||||
}
|
||||
#t1;
|
||||
#t2;
|
||||
#t3;
|
||||
}
|
||||
}
|
||||
on dynamic catch(dynamic #t7, core::StackTrace #t8) {
|
||||
#controller.{asy::StreamController::addError}(#t7){(core::Object, [core::StackTrace?]) → void};
|
||||
#t7;
|
||||
#t8;
|
||||
}
|
||||
finally {
|
||||
#controller.{asy::StreamController::close}(){() → asy::Future<dynamic>};
|
||||
#t1;
|
||||
#t2;
|
||||
#t3;
|
||||
}
|
||||
}
|
||||
#controller.{asy::StreamController::close}(){() → asy::Future<dynamic>};
|
||||
};
|
||||
synthesized core::bool #isFirst = #C2;
|
||||
synthesized core::bool #isEven = #C1;
|
||||
|
|
Loading…
Reference in a new issue