Skip to content

Commit f9327d3

Browse files
Clement Skaucommit-bot@chromium.org
authored andcommitted
[SDK] Adds --lazy-async-stack support for async*.
Bug: #39525 Change-Id: I53cd334243649901ea8e0f9799d9f41c126e3627 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/126729 Commit-Queue: Clement Skau <cskau@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
1 parent 42a4442 commit f9327d3

File tree

14 files changed

+359
-24
lines changed

14 files changed

+359
-24
lines changed

pkg/kernel/lib/transformations/continuation.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class ContinuationVariables {
1818
static const asyncCompleter = ':async_completer';
1919
static const awaitContextVar = ':await_ctx_var';
2020
static const asyncStackTraceVar = ':async_stack_trace';
21+
static const controller = ':controller';
2122
static const controllerStreamVar = ':controller_stream';
2223
static const exceptionParam = ':exception';
2324
static const stackTraceParam = ':stack_trace';
@@ -965,7 +966,8 @@ class AsyncStarFunctionRewriter extends AsyncRewriterBase {
965966
final elementType = elementTypeFromReturnType(helper.streamClass);
966967

967968
// _AsyncStarStreamController<T> :controller;
968-
controllerVariable = new VariableDeclaration(":controller",
969+
controllerVariable = new VariableDeclaration(
970+
ContinuationVariables.controller,
969971
type: new InterfaceType(helper.asyncStarStreamControllerClass,
970972
staticTypeContext.nullable, [elementType]));
971973
statements.add(controllerVariable);

pkg/vm/lib/bytecode/local_vars.dart

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import '../metadata/direct_call.dart' show DirectCallMetadata;
1919
// Keep in sync with runtime/vm/object.h:Context::kAwaitJumpVarIndex.
2020
const int awaitJumpVarContextIndex = 0;
2121
const int asyncCompleterContextIndex = 1;
22+
const int controllerContextIndex = 1;
2223

2324
class LocalVariables {
2425
final _scopes = new Map<TreeNode, Scope>();
@@ -443,12 +444,17 @@ class _ScopeBuilder extends RecursiveVisitor<Null> {
443444
._getVarDesc(_currentFrame
444445
.getSyntheticVar(ContinuationVariables.awaitJumpVar))
445446
.moveToScope(_currentScope);
446-
}
447-
if (_currentFrame.dartAsyncMarker == AsyncMarker.Async) {
448-
locals
449-
._getVarDesc(_currentFrame
450-
.getSyntheticVar(ContinuationVariables.asyncCompleter))
451-
.moveToScope(_currentScope);
447+
if (_currentFrame.dartAsyncMarker == AsyncMarker.Async) {
448+
locals
449+
._getVarDesc(_currentFrame
450+
.getSyntheticVar(ContinuationVariables.asyncCompleter))
451+
.moveToScope(_currentScope);
452+
} else if (_currentFrame.dartAsyncMarker == AsyncMarker.AsyncStar) {
453+
locals
454+
._getVarDesc(_currentFrame
455+
.getSyntheticVar(ContinuationVariables.controller))
456+
.moveToScope(_currentScope);
457+
}
452458
}
453459
}
454460

@@ -1110,6 +1116,12 @@ class _Allocator extends RecursiveVisitor<Null> {
11101116
assert(locals._getVarDesc(asyncCompleter).index ==
11111117
asyncCompleterContextIndex);
11121118
}
1119+
if (_currentFrame.dartAsyncMarker == AsyncMarker.AsyncStar) {
1120+
final controller =
1121+
_currentFrame.getSyntheticVar(ContinuationVariables.controller);
1122+
_allocateVariable(controller);
1123+
assert(locals._getVarDesc(controller).index == controllerContextIndex);
1124+
}
11131125
_allocateParameters(node, function);
11141126
_allocateSpecialVariables();
11151127

@@ -1172,6 +1184,8 @@ class _Allocator extends RecursiveVisitor<Null> {
11721184
assert(locals._getVarDesc(node).index == awaitJumpVarContextIndex);
11731185
} else if (node.name == ContinuationVariables.asyncCompleter) {
11741186
assert(locals._getVarDesc(node).index == asyncCompleterContextIndex);
1187+
} else if (node.name == ContinuationVariables.controller) {
1188+
assert(locals._getVarDesc(node).index == controllerContextIndex);
11751189
} else {
11761190
_allocateVariable(node);
11771191
}

runtime/tests/vm/dart/causal_stacks/utils.dart

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,37 @@ Future nonAsyncNoStack1() async => await nonAsyncNoStack2();
9696

9797
Future nonAsyncNoStack2() async => Future.value(0).then((_) => throwAsync());
9898

99+
// ----
100+
// Scenario: async*:
101+
// ----
102+
103+
Future awaitEveryAsyncStarThrowSync() async {
104+
await for (Future v in asyncStarThrowSync()) {
105+
await v;
106+
}
107+
}
108+
109+
Stream<Future> asyncStarThrowSync() async* {
110+
for (int i = 0; i < 2; i++) {
111+
await i;
112+
yield throwSync();
113+
}
114+
}
115+
116+
Future awaitEveryAsyncStarThrowAsync() async {
117+
await for (Future v in asyncStarThrowAsync()) {
118+
await v;
119+
}
120+
}
121+
122+
Stream<Future> asyncStarThrowAsync() async* {
123+
for (int i = 0; i < 2; i++) {
124+
await i;
125+
yield Future.value(i);
126+
await throwAsync();
127+
}
128+
}
129+
99130
// Helpers:
100131

101132
void assertStack(List<String> expects, StackTrace stackTrace) {
@@ -354,6 +385,93 @@ Future<void> doTestsCausal() async {
354385
await doTestAwait(nonAsyncNoStack, nonAsyncNoStackExpected);
355386
await doTestAwaitThen(nonAsyncNoStack, nonAsyncNoStackExpected);
356387
await doTestAwaitCatchError(nonAsyncNoStack, nonAsyncNoStackExpected);
388+
389+
final asyncStarThrowSyncExpected = const <String>[
390+
r'^#0 throwSync \(.*/utils.dart:(16|16:3)\)$',
391+
r'^#1 asyncStarThrowSync \(.*/utils.dart:(112|112:11)\)$',
392+
r'^<asynchronous suspension>$',
393+
r'^#2 awaitEveryAsyncStarThrowSync \(.+\)$',
394+
];
395+
await doTestAwait(
396+
awaitEveryAsyncStarThrowSync,
397+
asyncStarThrowSyncExpected +
398+
const <String>[
399+
r'^#3 doTestAwait \(.+\)$',
400+
r'^#4 doTestsCausal \(.+\)$',
401+
r'^<asynchronous suspension>$',
402+
r'^#5 main \(.+\)$',
403+
r'^#6 _startIsolate.<anonymous closure> \(.+\)$',
404+
r'^#7 _RawReceivePortImpl._handleMessage \(.+\)$',
405+
r'^$',
406+
]);
407+
await doTestAwaitThen(
408+
awaitEveryAsyncStarThrowSync,
409+
asyncStarThrowSyncExpected +
410+
const <String>[
411+
r'^#3 doTestAwaitThen \(.+\)$',
412+
r'^#4 doTestsCausal \(.+\)$',
413+
r'^<asynchronous suspension>$',
414+
r'^#5 main \(.+\)$',
415+
r'^#6 _startIsolate.<anonymous closure> \(.+\)$',
416+
r'^#7 _RawReceivePortImpl._handleMessage \(.+\)$',
417+
r'^$',
418+
]);
419+
await doTestAwaitCatchError(
420+
awaitEveryAsyncStarThrowSync,
421+
asyncStarThrowSyncExpected +
422+
const <String>[
423+
r'^#3 doTestAwaitCatchError \(.+\)$',
424+
r'^#4 doTestsCausal \(.+\)$',
425+
r'^<asynchronous suspension>$',
426+
r'^#5 main \(.+\)$',
427+
r'^#6 _startIsolate.<anonymous closure> \(.+\)$',
428+
r'^#7 _RawReceivePortImpl._handleMessage \(.+\)$',
429+
r'^$',
430+
]);
431+
432+
final asyncStarThrowAsyncExpected = const <String>[
433+
r'^#0 throwAsync \(.*/utils.dart:(21|21:3)\)$',
434+
r'^<asynchronous suspension>$',
435+
r'^#1 asyncStarThrowAsync \(.*/utils.dart:(126|126:11)\)$',
436+
r'^<asynchronous suspension>$',
437+
r'^#2 awaitEveryAsyncStarThrowAsync \(.+\)$',
438+
];
439+
await doTestAwait(
440+
awaitEveryAsyncStarThrowAsync,
441+
asyncStarThrowAsyncExpected +
442+
const <String>[
443+
r'^#3 doTestAwait \(.+\)$',
444+
r'^#4 doTestsCausal \(.+\)$',
445+
r'^<asynchronous suspension>$',
446+
r'^#5 main \(.+\)$',
447+
r'^#6 _startIsolate.<anonymous closure> \(.+\)$',
448+
r'^#7 _RawReceivePortImpl._handleMessage \(.+\)$',
449+
r'^$',
450+
]);
451+
await doTestAwaitThen(
452+
awaitEveryAsyncStarThrowAsync,
453+
asyncStarThrowAsyncExpected +
454+
const <String>[
455+
r'^#3 doTestAwaitThen \(.+\)$',
456+
r'^#4 doTestsCausal \(.+\)$',
457+
r'^<asynchronous suspension>$',
458+
r'^#5 main \(.+\)$',
459+
r'^#6 _startIsolate.<anonymous closure> \(.+\)$',
460+
r'^#7 _RawReceivePortImpl._handleMessage \(.+\)$',
461+
r'^$',
462+
]);
463+
await doTestAwaitCatchError(
464+
awaitEveryAsyncStarThrowAsync,
465+
asyncStarThrowAsyncExpected +
466+
const <String>[
467+
r'^#3 doTestAwaitCatchError \(.+\)$',
468+
r'^#4 doTestsCausal \(.+\)$',
469+
r'^<asynchronous suspension>$',
470+
r'^#5 main \(.+\)$',
471+
r'^#6 _startIsolate.<anonymous closure> \(.+\)$',
472+
r'^#7 _RawReceivePortImpl._handleMessage \(.+\)$',
473+
r'^$',
474+
]);
357475
}
358476

359477
// For: --no-causal-async-stacks
@@ -586,6 +704,47 @@ Future<void> doTestsNoCausal() async {
586704
await doTestAwait(nonAsyncNoStack, nonAsyncNoStackExpected);
587705
await doTestAwaitThen(nonAsyncNoStack, nonAsyncNoStackExpected);
588706
await doTestAwaitCatchError(nonAsyncNoStack, nonAsyncNoStackExpected);
707+
708+
final asyncStarThrowSyncExpected = const <String>[
709+
r'^#0 throwSync \(.+/utils.dart:(16|16:3)\)$',
710+
r'^#1 asyncStarThrowSync \(.+/utils.dart:(112|112:11)\)$',
711+
r'^#2 _RootZone.runUnary \(.+\)$',
712+
r'^#3 _FutureListener.handleValue \(.+\)$',
713+
r'^#4 Future._propagateToListeners.handleValueCallback \(.+\)$',
714+
r'^#5 Future._propagateToListeners \(.+\)$',
715+
// TODO(dart-vm): Figure out why this is inconsistent:
716+
r'^#6 Future.(_addListener|_prependListeners).<anonymous closure> \(.+\)$',
717+
r'^#7 _microtaskLoop \(.+\)$',
718+
r'^#8 _startMicrotaskLoop \(.+\)$',
719+
r'^#9 _runPendingImmediateCallback \(.+\)$',
720+
r'^#10 _RawReceivePortImpl._handleMessage \(.+\)$',
721+
r'^$',
722+
];
723+
await doTestAwait(awaitEveryAsyncStarThrowSync, asyncStarThrowSyncExpected);
724+
await doTestAwaitThen(
725+
awaitEveryAsyncStarThrowSync, asyncStarThrowSyncExpected);
726+
await doTestAwaitCatchError(
727+
awaitEveryAsyncStarThrowSync, asyncStarThrowSyncExpected);
728+
729+
final asyncStarThrowAsyncExpected = const <String>[
730+
r'^#0 throwAsync \(.*/utils.dart:(21|21:3)\)$',
731+
r'^#1 _RootZone.runUnary ',
732+
r'^#2 _FutureListener.handleValue ',
733+
r'^#3 Future._propagateToListeners.handleValueCallback ',
734+
r'^#4 Future._propagateToListeners ',
735+
// TODO(dart-vm): Figure out why this is inconsistent:
736+
r'^#5 Future.(_addListener|_prependListeners).<anonymous closure> ',
737+
r'^#6 _microtaskLoop ',
738+
r'^#7 _startMicrotaskLoop ',
739+
r'^#8 _runPendingImmediateCallback ',
740+
r'^#9 _RawReceivePortImpl._handleMessage ',
741+
r'^$',
742+
];
743+
await doTestAwait(awaitEveryAsyncStarThrowAsync, asyncStarThrowAsyncExpected);
744+
await doTestAwaitThen(
745+
awaitEveryAsyncStarThrowAsync, asyncStarThrowAsyncExpected);
746+
await doTestAwaitCatchError(
747+
awaitEveryAsyncStarThrowAsync, asyncStarThrowAsyncExpected);
589748
}
590749

591750
// For: --lazy-async-stacks
@@ -744,4 +903,32 @@ Future<void> doTestsLazy() async {
744903
const <String>[
745904
r'^$',
746905
]);
906+
907+
final asyncStarThrowSyncExpected = const <String>[
908+
r'^#0 throwSync \(.+/utils.dart:(16|16:3)\)$',
909+
r'^#1 asyncStarThrowSync \(.+/utils.dart:(112|112:11)\)$',
910+
r'^<asynchronous suspension>$',
911+
// Non-visible _onData frame.
912+
r'^<asynchronous suspension>$',
913+
r'^$',
914+
];
915+
await doTestAwait(awaitEveryAsyncStarThrowSync, asyncStarThrowSyncExpected);
916+
await doTestAwaitThen(
917+
awaitEveryAsyncStarThrowSync, asyncStarThrowSyncExpected);
918+
await doTestAwaitCatchError(
919+
awaitEveryAsyncStarThrowSync, asyncStarThrowSyncExpected);
920+
921+
final asyncStarThrowAsyncExpected = const <String>[
922+
r'^#0 throwAsync \(.*/utils.dart:(21|21:3)\)$',
923+
r'^<asynchronous suspension>$',
924+
r'^#1 asyncStarThrowAsync \(.*/utils.dart:(0|126|126:5)\)$',
925+
r'^<asynchronous suspension>$',
926+
// Non-visible _onData frame.
927+
r'^<asynchronous suspension>$',
928+
];
929+
await doTestAwait(awaitEveryAsyncStarThrowAsync, asyncStarThrowAsyncExpected);
930+
await doTestAwaitThen(
931+
awaitEveryAsyncStarThrowAsync, asyncStarThrowAsyncExpected);
932+
await doTestAwaitCatchError(
933+
awaitEveryAsyncStarThrowAsync, asyncStarThrowAsyncExpected);
747934
}

runtime/vm/compiler/frontend/scope_builder.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1280,7 +1280,8 @@ void ScopeBuilder::VisitVariableDeclaration() {
12801280
// This way we can allocate them in the outermost context at fixed indices,
12811281
// allowing support for --lazy-async-stacks implementation to find awaiters.
12821282
if (name.Equals(Symbols::AwaitJumpVar()) ||
1283-
name.Equals(Symbols::AsyncCompleter())) {
1283+
name.Equals(Symbols::AsyncCompleter()) ||
1284+
name.Equals(Symbols::Controller())) {
12841285
scope_->parent()->AddVariable(variable);
12851286
} else {
12861287
scope_->AddVariable(variable);

runtime/vm/object.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6036,6 +6036,7 @@ class Context : public Object {
60366036

60376037
static const intptr_t kAwaitJumpVarIndex = 0;
60386038
static const intptr_t kAsyncCompleterIndex = 1;
6039+
static const intptr_t kControllerIndex = 1;
60396040

60406041
static intptr_t variable_offset(intptr_t context_index) {
60416042
return OFFSET_OF_RETURNED_VALUE(RawContext, data) +

runtime/vm/scopes.cc

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ VariableIndex LocalScope::AllocateVariables(VariableIndex first_parameter_index,
210210

211211
LocalVariable* await_jump_var = nullptr;
212212
LocalVariable* async_completer = nullptr;
213+
LocalVariable* controller = nullptr;
213214
for (intptr_t i = 0; i < num_variables(); i++) {
214215
LocalVariable* variable = VariableAt(i);
215216
if (variable->owner() == this) {
@@ -218,6 +219,8 @@ VariableIndex LocalScope::AllocateVariables(VariableIndex first_parameter_index,
218219
await_jump_var = variable;
219220
} else if (variable->name().Equals(Symbols::AsyncCompleter())) {
220221
async_completer = variable;
222+
} else if (variable->name().Equals(Symbols::Controller())) {
223+
controller = variable;
221224
}
222225
}
223226
}
@@ -234,6 +237,11 @@ VariableIndex LocalScope::AllocateVariables(VariableIndex first_parameter_index,
234237
*found_captured_variables = true;
235238
ASSERT(async_completer->index().value() == Context::kAsyncCompleterIndex);
236239
}
240+
if (controller != nullptr) {
241+
AllocateContextVariable(controller, &context_owner);
242+
*found_captured_variables = true;
243+
ASSERT(controller->index().value() == Context::kControllerIndex);
244+
}
237245

238246
while (pos < num_parameters) {
239247
LocalVariable* parameter = VariableAt(pos);
@@ -263,7 +271,8 @@ VariableIndex LocalScope::AllocateVariables(VariableIndex first_parameter_index,
263271
if (variable->owner() == this) {
264272
if (variable->is_captured()) {
265273
// Skip the two variables already pre-allocated above.
266-
if (variable != await_jump_var && variable != async_completer) {
274+
if (variable != await_jump_var && variable != async_completer &&
275+
variable != controller) {
267276
AllocateContextVariable(variable, &context_owner);
268277
*found_captured_variables = true;
269278
}

0 commit comments

Comments
 (0)