[SDK] Adds --lazy-async-stack support for async*.

Bug: https://github.com/dart-lang/sdk/issues/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>
diff --git a/pkg/kernel/lib/transformations/continuation.dart b/pkg/kernel/lib/transformations/continuation.dart
index 367e1e7..bc07276 100644
--- a/pkg/kernel/lib/transformations/continuation.dart
+++ b/pkg/kernel/lib/transformations/continuation.dart
@@ -18,6 +18,7 @@
   static const asyncCompleter = ':async_completer';
   static const awaitContextVar = ':await_ctx_var';
   static const asyncStackTraceVar = ':async_stack_trace';
+  static const controller = ':controller';
   static const controllerStreamVar = ':controller_stream';
   static const exceptionParam = ':exception';
   static const stackTraceParam = ':stack_trace';
@@ -965,7 +966,8 @@
     final elementType = elementTypeFromReturnType(helper.streamClass);
 
     // _AsyncStarStreamController<T> :controller;
-    controllerVariable = new VariableDeclaration(":controller",
+    controllerVariable = new VariableDeclaration(
+        ContinuationVariables.controller,
         type: new InterfaceType(helper.asyncStarStreamControllerClass,
             staticTypeContext.nullable, [elementType]));
     statements.add(controllerVariable);
diff --git a/pkg/vm/lib/bytecode/local_vars.dart b/pkg/vm/lib/bytecode/local_vars.dart
index ad0d675..0e59577 100644
--- a/pkg/vm/lib/bytecode/local_vars.dart
+++ b/pkg/vm/lib/bytecode/local_vars.dart
@@ -19,6 +19,7 @@
 // Keep in sync with runtime/vm/object.h:Context::kAwaitJumpVarIndex.
 const int awaitJumpVarContextIndex = 0;
 const int asyncCompleterContextIndex = 1;
+const int controllerContextIndex = 1;
 
 class LocalVariables {
   final _scopes = new Map<TreeNode, Scope>();
@@ -443,12 +444,17 @@
             ._getVarDesc(_currentFrame
                 .getSyntheticVar(ContinuationVariables.awaitJumpVar))
             .moveToScope(_currentScope);
-      }
-      if (_currentFrame.dartAsyncMarker == AsyncMarker.Async) {
-        locals
-            ._getVarDesc(_currentFrame
-                .getSyntheticVar(ContinuationVariables.asyncCompleter))
-            .moveToScope(_currentScope);
+        if (_currentFrame.dartAsyncMarker == AsyncMarker.Async) {
+          locals
+              ._getVarDesc(_currentFrame
+                  .getSyntheticVar(ContinuationVariables.asyncCompleter))
+              .moveToScope(_currentScope);
+        } else if (_currentFrame.dartAsyncMarker == AsyncMarker.AsyncStar) {
+          locals
+              ._getVarDesc(_currentFrame
+                  .getSyntheticVar(ContinuationVariables.controller))
+              .moveToScope(_currentScope);
+        }
       }
     }
 
@@ -1110,6 +1116,12 @@
         assert(locals._getVarDesc(asyncCompleter).index ==
             asyncCompleterContextIndex);
       }
+      if (_currentFrame.dartAsyncMarker == AsyncMarker.AsyncStar) {
+        final controller =
+            _currentFrame.getSyntheticVar(ContinuationVariables.controller);
+        _allocateVariable(controller);
+        assert(locals._getVarDesc(controller).index == controllerContextIndex);
+      }
       _allocateParameters(node, function);
       _allocateSpecialVariables();
 
@@ -1172,6 +1184,8 @@
       assert(locals._getVarDesc(node).index == awaitJumpVarContextIndex);
     } else if (node.name == ContinuationVariables.asyncCompleter) {
       assert(locals._getVarDesc(node).index == asyncCompleterContextIndex);
+    } else if (node.name == ContinuationVariables.controller) {
+      assert(locals._getVarDesc(node).index == controllerContextIndex);
     } else {
       _allocateVariable(node);
     }
diff --git a/runtime/tests/vm/dart/causal_stacks/utils.dart b/runtime/tests/vm/dart/causal_stacks/utils.dart
index 3060172..6d4ccc29 100644
--- a/runtime/tests/vm/dart/causal_stacks/utils.dart
+++ b/runtime/tests/vm/dart/causal_stacks/utils.dart
@@ -96,6 +96,37 @@
 
 Future nonAsyncNoStack2() async => Future.value(0).then((_) => throwAsync());
 
+// ----
+// Scenario: async*:
+// ----
+
+Future awaitEveryAsyncStarThrowSync() async {
+  await for (Future v in asyncStarThrowSync()) {
+    await v;
+  }
+}
+
+Stream<Future> asyncStarThrowSync() async* {
+  for (int i = 0; i < 2; i++) {
+    await i;
+    yield throwSync();
+  }
+}
+
+Future awaitEveryAsyncStarThrowAsync() async {
+  await for (Future v in asyncStarThrowAsync()) {
+    await v;
+  }
+}
+
+Stream<Future> asyncStarThrowAsync() async* {
+  for (int i = 0; i < 2; i++) {
+    await i;
+    yield Future.value(i);
+    await throwAsync();
+  }
+}
+
 // Helpers:
 
 void assertStack(List<String> expects, StackTrace stackTrace) {
@@ -354,6 +385,93 @@
   await doTestAwait(nonAsyncNoStack, nonAsyncNoStackExpected);
   await doTestAwaitThen(nonAsyncNoStack, nonAsyncNoStackExpected);
   await doTestAwaitCatchError(nonAsyncNoStack, nonAsyncNoStackExpected);
+
+  final asyncStarThrowSyncExpected = const <String>[
+    r'^#0      throwSync \(.*/utils.dart:(16|16:3)\)$',
+    r'^#1      asyncStarThrowSync \(.*/utils.dart:(112|112:11)\)$',
+    r'^<asynchronous suspension>$',
+    r'^#2      awaitEveryAsyncStarThrowSync \(.+\)$',
+  ];
+  await doTestAwait(
+      awaitEveryAsyncStarThrowSync,
+      asyncStarThrowSyncExpected +
+          const <String>[
+            r'^#3      doTestAwait \(.+\)$',
+            r'^#4      doTestsCausal \(.+\)$',
+            r'^<asynchronous suspension>$',
+            r'^#5      main \(.+\)$',
+            r'^#6      _startIsolate.<anonymous closure> \(.+\)$',
+            r'^#7      _RawReceivePortImpl._handleMessage \(.+\)$',
+            r'^$',
+          ]);
+  await doTestAwaitThen(
+      awaitEveryAsyncStarThrowSync,
+      asyncStarThrowSyncExpected +
+          const <String>[
+            r'^#3      doTestAwaitThen \(.+\)$',
+            r'^#4      doTestsCausal \(.+\)$',
+            r'^<asynchronous suspension>$',
+            r'^#5      main \(.+\)$',
+            r'^#6      _startIsolate.<anonymous closure> \(.+\)$',
+            r'^#7      _RawReceivePortImpl._handleMessage \(.+\)$',
+            r'^$',
+          ]);
+  await doTestAwaitCatchError(
+      awaitEveryAsyncStarThrowSync,
+      asyncStarThrowSyncExpected +
+          const <String>[
+            r'^#3      doTestAwaitCatchError \(.+\)$',
+            r'^#4      doTestsCausal \(.+\)$',
+            r'^<asynchronous suspension>$',
+            r'^#5      main \(.+\)$',
+            r'^#6      _startIsolate.<anonymous closure> \(.+\)$',
+            r'^#7      _RawReceivePortImpl._handleMessage \(.+\)$',
+            r'^$',
+          ]);
+
+  final asyncStarThrowAsyncExpected = const <String>[
+    r'^#0      throwAsync \(.*/utils.dart:(21|21:3)\)$',
+    r'^<asynchronous suspension>$',
+    r'^#1      asyncStarThrowAsync \(.*/utils.dart:(126|126:11)\)$',
+    r'^<asynchronous suspension>$',
+    r'^#2      awaitEveryAsyncStarThrowAsync \(.+\)$',
+  ];
+  await doTestAwait(
+      awaitEveryAsyncStarThrowAsync,
+      asyncStarThrowAsyncExpected +
+          const <String>[
+            r'^#3      doTestAwait \(.+\)$',
+            r'^#4      doTestsCausal \(.+\)$',
+            r'^<asynchronous suspension>$',
+            r'^#5      main \(.+\)$',
+            r'^#6      _startIsolate.<anonymous closure> \(.+\)$',
+            r'^#7      _RawReceivePortImpl._handleMessage \(.+\)$',
+            r'^$',
+          ]);
+  await doTestAwaitThen(
+      awaitEveryAsyncStarThrowAsync,
+      asyncStarThrowAsyncExpected +
+          const <String>[
+            r'^#3      doTestAwaitThen \(.+\)$',
+            r'^#4      doTestsCausal \(.+\)$',
+            r'^<asynchronous suspension>$',
+            r'^#5      main \(.+\)$',
+            r'^#6      _startIsolate.<anonymous closure> \(.+\)$',
+            r'^#7      _RawReceivePortImpl._handleMessage \(.+\)$',
+            r'^$',
+          ]);
+  await doTestAwaitCatchError(
+      awaitEveryAsyncStarThrowAsync,
+      asyncStarThrowAsyncExpected +
+          const <String>[
+            r'^#3      doTestAwaitCatchError \(.+\)$',
+            r'^#4      doTestsCausal \(.+\)$',
+            r'^<asynchronous suspension>$',
+            r'^#5      main \(.+\)$',
+            r'^#6      _startIsolate.<anonymous closure> \(.+\)$',
+            r'^#7      _RawReceivePortImpl._handleMessage \(.+\)$',
+            r'^$',
+          ]);
 }
 
 // For: --no-causal-async-stacks
@@ -586,6 +704,47 @@
   await doTestAwait(nonAsyncNoStack, nonAsyncNoStackExpected);
   await doTestAwaitThen(nonAsyncNoStack, nonAsyncNoStackExpected);
   await doTestAwaitCatchError(nonAsyncNoStack, nonAsyncNoStackExpected);
+
+  final asyncStarThrowSyncExpected = const <String>[
+    r'^#0      throwSync \(.+/utils.dart:(16|16:3)\)$',
+    r'^#1      asyncStarThrowSync \(.+/utils.dart:(112|112:11)\)$',
+    r'^#2      _RootZone.runUnary \(.+\)$',
+    r'^#3      _FutureListener.handleValue \(.+\)$',
+    r'^#4      Future._propagateToListeners.handleValueCallback \(.+\)$',
+    r'^#5      Future._propagateToListeners \(.+\)$',
+    // TODO(dart-vm): Figure out why this is inconsistent:
+    r'^#6      Future.(_addListener|_prependListeners).<anonymous closure> \(.+\)$',
+    r'^#7      _microtaskLoop \(.+\)$',
+    r'^#8      _startMicrotaskLoop \(.+\)$',
+    r'^#9      _runPendingImmediateCallback \(.+\)$',
+    r'^#10     _RawReceivePortImpl._handleMessage \(.+\)$',
+    r'^$',
+  ];
+  await doTestAwait(awaitEveryAsyncStarThrowSync, asyncStarThrowSyncExpected);
+  await doTestAwaitThen(
+      awaitEveryAsyncStarThrowSync, asyncStarThrowSyncExpected);
+  await doTestAwaitCatchError(
+      awaitEveryAsyncStarThrowSync, asyncStarThrowSyncExpected);
+
+  final asyncStarThrowAsyncExpected = const <String>[
+    r'^#0      throwAsync \(.*/utils.dart:(21|21:3)\)$',
+    r'^#1      _RootZone.runUnary ',
+    r'^#2      _FutureListener.handleValue ',
+    r'^#3      Future._propagateToListeners.handleValueCallback ',
+    r'^#4      Future._propagateToListeners ',
+    // TODO(dart-vm): Figure out why this is inconsistent:
+    r'^#5      Future.(_addListener|_prependListeners).<anonymous closure> ',
+    r'^#6      _microtaskLoop ',
+    r'^#7      _startMicrotaskLoop ',
+    r'^#8      _runPendingImmediateCallback ',
+    r'^#9      _RawReceivePortImpl._handleMessage ',
+    r'^$',
+  ];
+  await doTestAwait(awaitEveryAsyncStarThrowAsync, asyncStarThrowAsyncExpected);
+  await doTestAwaitThen(
+      awaitEveryAsyncStarThrowAsync, asyncStarThrowAsyncExpected);
+  await doTestAwaitCatchError(
+      awaitEveryAsyncStarThrowAsync, asyncStarThrowAsyncExpected);
 }
 
 // For: --lazy-async-stacks
@@ -744,4 +903,32 @@
           const <String>[
             r'^$',
           ]);
+
+  final asyncStarThrowSyncExpected = const <String>[
+    r'^#0      throwSync \(.+/utils.dart:(16|16:3)\)$',
+    r'^#1      asyncStarThrowSync \(.+/utils.dart:(112|112:11)\)$',
+    r'^<asynchronous suspension>$',
+    // Non-visible _onData frame.
+    r'^<asynchronous suspension>$',
+    r'^$',
+  ];
+  await doTestAwait(awaitEveryAsyncStarThrowSync, asyncStarThrowSyncExpected);
+  await doTestAwaitThen(
+      awaitEveryAsyncStarThrowSync, asyncStarThrowSyncExpected);
+  await doTestAwaitCatchError(
+      awaitEveryAsyncStarThrowSync, asyncStarThrowSyncExpected);
+
+  final asyncStarThrowAsyncExpected = const <String>[
+    r'^#0      throwAsync \(.*/utils.dart:(21|21:3)\)$',
+    r'^<asynchronous suspension>$',
+    r'^#1      asyncStarThrowAsync \(.*/utils.dart:(0|126|126:5)\)$',
+    r'^<asynchronous suspension>$',
+    // Non-visible _onData frame.
+    r'^<asynchronous suspension>$',
+  ];
+  await doTestAwait(awaitEveryAsyncStarThrowAsync, asyncStarThrowAsyncExpected);
+  await doTestAwaitThen(
+      awaitEveryAsyncStarThrowAsync, asyncStarThrowAsyncExpected);
+  await doTestAwaitCatchError(
+      awaitEveryAsyncStarThrowAsync, asyncStarThrowAsyncExpected);
 }
diff --git a/runtime/vm/compiler/frontend/scope_builder.cc b/runtime/vm/compiler/frontend/scope_builder.cc
index 391a04b..66348aa 100644
--- a/runtime/vm/compiler/frontend/scope_builder.cc
+++ b/runtime/vm/compiler/frontend/scope_builder.cc
@@ -1280,7 +1280,8 @@
   // This way we can allocate them in the outermost context at fixed indices,
   // allowing support for --lazy-async-stacks implementation to find awaiters.
   if (name.Equals(Symbols::AwaitJumpVar()) ||
-      name.Equals(Symbols::AsyncCompleter())) {
+      name.Equals(Symbols::AsyncCompleter()) ||
+      name.Equals(Symbols::Controller())) {
     scope_->parent()->AddVariable(variable);
   } else {
     scope_->AddVariable(variable);
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 402e025e..bc8e461 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -6036,6 +6036,7 @@
 
   static const intptr_t kAwaitJumpVarIndex = 0;
   static const intptr_t kAsyncCompleterIndex = 1;
+  static const intptr_t kControllerIndex = 1;
 
   static intptr_t variable_offset(intptr_t context_index) {
     return OFFSET_OF_RETURNED_VALUE(RawContext, data) +
diff --git a/runtime/vm/scopes.cc b/runtime/vm/scopes.cc
index 27031de..bd68c5e 100644
--- a/runtime/vm/scopes.cc
+++ b/runtime/vm/scopes.cc
@@ -210,6 +210,7 @@
 
   LocalVariable* await_jump_var = nullptr;
   LocalVariable* async_completer = nullptr;
+  LocalVariable* controller = nullptr;
   for (intptr_t i = 0; i < num_variables(); i++) {
     LocalVariable* variable = VariableAt(i);
     if (variable->owner() == this) {
@@ -218,6 +219,8 @@
           await_jump_var = variable;
         } else if (variable->name().Equals(Symbols::AsyncCompleter())) {
           async_completer = variable;
+        } else if (variable->name().Equals(Symbols::Controller())) {
+          controller = variable;
         }
       }
     }
@@ -234,6 +237,11 @@
     *found_captured_variables = true;
     ASSERT(async_completer->index().value() == Context::kAsyncCompleterIndex);
   }
+  if (controller != nullptr) {
+    AllocateContextVariable(controller, &context_owner);
+    *found_captured_variables = true;
+    ASSERT(controller->index().value() == Context::kControllerIndex);
+  }
 
   while (pos < num_parameters) {
     LocalVariable* parameter = VariableAt(pos);
@@ -263,7 +271,8 @@
     if (variable->owner() == this) {
       if (variable->is_captured()) {
         // Skip the two variables already pre-allocated above.
-        if (variable != await_jump_var && variable != async_completer) {
+        if (variable != await_jump_var && variable != async_completer &&
+            variable != controller) {
           AllocateContextVariable(variable, &context_owner);
           *found_captured_variables = true;
         }
diff --git a/runtime/vm/stack_trace.cc b/runtime/vm/stack_trace.cc
index ed28773..9581646 100644
--- a/runtime/vm/stack_trace.cc
+++ b/runtime/vm/stack_trace.cc
@@ -11,6 +11,10 @@
 
 namespace dart {
 
+// Keep in sync with
+// sdk/lib/async/stream_controller.dart:_StreamController._STATE_SUBSCRIBED.
+const intptr_t kStreamController_StateSubscribed = 1;
+
 RawClosure* FindClosureInFrame(RawObject** last_object_in_caller,
                                const Function& function,
                                bool is_interpreted) {
@@ -98,14 +102,27 @@
         future_(Object::Handle(zone)),
         listener_(Object::Handle(zone)),
         callback_(Object::Handle(zone)),
+        controller_(Object::Handle(zone)),
+        state_(Object::Handle(zone)),
+        var_data_(Object::Handle(zone)),
         future_impl_class(Class::Handle(zone)),
         async_await_completer_class(Class::Handle(zone)),
         future_listener_class(Class::Handle(zone)),
+        async_start_stream_controller_class(Class::Handle(zone)),
+        stream_controller_class(Class::Handle(zone)),
+        controller_subscription_class(Class::Handle(zone)),
+        buffering_stream_subscription_class(Class::Handle(zone)),
+        async_stream_controller_class(Class::Handle(zone)),
         completer_future_field(Field::Handle(zone)),
         future_result_or_listeners_field(Field::Handle(zone)),
-        callback_field(Field::Handle(zone)) {
+        callback_field(Field::Handle(zone)),
+        controller_controller_field(Field::Handle(zone)),
+        var_data_field(Field::Handle(zone)),
+        state_field(Field::Handle(zone)),
+        on_data_field(Field::Handle(zone)) {
     const auto& async_lib = Library::Handle(zone, Library::AsyncLibrary());
-    // Look up classes.
+    // Look up classes:
+    // - async:
     future_impl_class =
         async_lib.LookupClassAllowPrivate(Symbols::FutureImpl());
     ASSERT(!future_impl_class.IsNull());
@@ -115,7 +132,25 @@
     future_listener_class =
         async_lib.LookupClassAllowPrivate(Symbols::_FutureListener());
     ASSERT(!future_listener_class.IsNull());
-    // Look up fields.
+    // - async*:
+    async_start_stream_controller_class = async_lib.LookupClassAllowPrivate(
+        Symbols::_AsyncStarStreamController());
+    ASSERT(!async_start_stream_controller_class.IsNull());
+    stream_controller_class =
+        async_lib.LookupClassAllowPrivate(Symbols::_StreamController());
+    ASSERT(!stream_controller_class.IsNull());
+    async_stream_controller_class =
+        async_lib.LookupClassAllowPrivate(Symbols::_AsyncStreamController());
+    ASSERT(!async_stream_controller_class.IsNull());
+    controller_subscription_class =
+        async_lib.LookupClassAllowPrivate(Symbols::_ControllerSubscription());
+    ASSERT(!controller_subscription_class.IsNull());
+    buffering_stream_subscription_class = async_lib.LookupClassAllowPrivate(
+        Symbols::_BufferingStreamSubscription());
+    ASSERT(!buffering_stream_subscription_class.IsNull());
+
+    // Look up fields:
+    // - async:
     completer_future_field =
         async_await_completer_class.LookupFieldAllowPrivate(Symbols::_future());
     ASSERT(!completer_future_field.IsNull());
@@ -126,18 +161,24 @@
     callback_field =
         future_listener_class.LookupFieldAllowPrivate(Symbols::callback());
     ASSERT(!callback_field.IsNull());
+    // - async*:
+    controller_controller_field =
+        async_start_stream_controller_class.LookupFieldAllowPrivate(
+            Symbols::controller());
+    ASSERT(!controller_controller_field.IsNull());
+    state_field =
+        stream_controller_class.LookupFieldAllowPrivate(Symbols::_state());
+    ASSERT(!state_field.IsNull());
+    var_data_field =
+        stream_controller_class.LookupFieldAllowPrivate(Symbols::_varData());
+    ASSERT(!var_data_field.IsNull());
+    on_data_field = buffering_stream_subscription_class.LookupFieldAllowPrivate(
+        Symbols::_onData());
+    ASSERT(!on_data_field.IsNull());
   }
 
-  RawClosure* FindCaller(const Closure& receiver_closure) {
-    receiver_function_ = receiver_closure.function();
-    if (!receiver_function_.IsAsyncClosure() &&
-        !receiver_function_.IsAsyncGenClosure()) {
-      return Closure::null();
-    }
-
-    receiver_context_ = receiver_closure.context();
-    context_entry_ = receiver_context_.At(Context::kAsyncCompleterIndex);
-
+  RawClosure* FindCallerInAsyncClosure(const Context& receiver_context) {
+    context_entry_ = receiver_context.At(Context::kAsyncCompleterIndex);
     ASSERT(context_entry_.IsInstance());
     ASSERT(context_entry_.GetClassId() == async_await_completer_class.id());
 
@@ -162,20 +203,73 @@
     return Closure::Cast(callback_).raw();
   }
 
+  RawClosure* FindCallerInAsyncGenClosure(const Context& receiver_context) {
+    context_entry_ = receiver_context.At(Context::kControllerIndex);
+    ASSERT(context_entry_.IsInstance());
+    ASSERT(context_entry_.GetClassId() ==
+           async_start_stream_controller_class.id());
+
+    const Instance& controller = Instance::Cast(context_entry_);
+    controller_ = controller.GetField(controller_controller_field);
+    ASSERT(!controller_.IsNull());
+    ASSERT(controller_.GetClassId() == async_stream_controller_class.id());
+
+    state_ = Instance::Cast(controller_).GetField(state_field);
+    ASSERT(state_.IsSmi());
+    if (Smi::Cast(state_).Value() != kStreamController_StateSubscribed) {
+      return Closure::null();
+    }
+
+    var_data_ = Instance::Cast(controller_).GetField(var_data_field);
+    ASSERT(var_data_.GetClassId() == controller_subscription_class.id());
+
+    callback_ = Instance::Cast(var_data_).GetField(on_data_field);
+    ASSERT(callback_.IsClosure());
+
+    return Closure::Cast(callback_).raw();
+  }
+
+  RawClosure* FindCaller(const Closure& receiver_closure) {
+    receiver_function_ = receiver_closure.function();
+    receiver_context_ = receiver_closure.context();
+
+    if (receiver_function_.IsAsyncClosure()) {
+      return FindCallerInAsyncClosure(receiver_context_);
+    } else if (receiver_function_.IsAsyncGenClosure()) {
+      return FindCallerInAsyncGenClosure(receiver_context_);
+    }
+
+    return Closure::null();
+  }
+
  private:
   Context& receiver_context_;
   Function& receiver_function_;
+
   Object& context_entry_;
   Object& future_;
   Object& listener_;
   Object& callback_;
+  Object& controller_;
+  Object& state_;
+  Object& var_data_;
 
   Class& future_impl_class;
   Class& async_await_completer_class;
   Class& future_listener_class;
+  Class& async_start_stream_controller_class;
+  Class& stream_controller_class;
+  Class& controller_subscription_class;
+  Class& buffering_stream_subscription_class;
+  Class& async_stream_controller_class;
+
   Field& completer_future_field;
   Field& future_result_or_listeners_field;
   Field& callback_field;
+  Field& controller_controller_field;
+  Field& var_data_field;
+  Field& state_field;
+  Field& on_data_field;
 };
 
 void StackTraceUtils::CollectFramesLazy(
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index ff63c44..3f47d8f 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -61,6 +61,7 @@
   V(ConstructorStacktracePrefix, "new ")                                       \
   V(Context, "Context")                                                        \
   V(ContextScope, "ContextScope")                                              \
+  V(Controller, ":controller")                                                 \
   V(ControllerStream, ":controller_stream")                                    \
   V(Current, "current")                                                        \
   V(CurrentContextVar, ":current_context_var")                                 \
@@ -252,6 +253,7 @@
   V(StackTraceParameter, ":stack_trace")                                       \
   V(StackTraceVar, ":stack_trace_var")                                         \
   V(Stream, "stream")                                                          \
+  V(StreamController, "StreamController")                                      \
   V(StreamIterator, "StreamIterator")                                          \
   V(StreamIteratorConstructor, "StreamIterator.")                              \
   V(StringBase, "_StringBase")                                                 \
@@ -300,7 +302,9 @@
   V(_AsyncStarListenHelper, "_asyncStarListenHelper")                          \
   V(_AsyncStarStreamController, "_AsyncStarStreamController")                  \
   V(_AsyncStarStreamControllerConstructor, "_AsyncStarStreamController.")      \
+  V(_AsyncStreamController, "_AsyncStreamController")                          \
   V(_Awaiter, "_awaiter")                                                      \
+  V(_BufferingStreamSubscription, "_BufferingStreamSubscription")              \
   V(_ByteBuffer, "_ByteBuffer")                                                \
   V(_ByteBufferDot_New, "_ByteBuffer._New")                                    \
   V(_ByteDataView, "_ByteDataView")                                            \
@@ -311,6 +315,7 @@
   V(_CombinatorMirror, "_CombinatorMirror")                                    \
   V(_CompileTimeError, "_CompileTimeError")                                    \
   V(_CompleteOnAsyncReturn, "_completeOnAsyncReturn")                          \
+  V(_ControllerSubscription, "_ControllerSubscription")                        \
   V(_DeletedEnumPrefix, "Deleted enum value from ")                            \
   V(_DeletedEnumSentinel, "_deleted_enum_sentinel")                            \
   V(_Double, "_Double")                                                        \
@@ -386,6 +391,7 @@
   V(_SourceLocation, "_SourceLocation")                                        \
   V(_SpecialTypeMirror, "_SpecialTypeMirror")                                  \
   V(_StackTrace, "_StackTrace")                                                \
+  V(_StreamController, "_StreamController")                                    \
   V(_StreamImpl, "_StreamImpl")                                                \
   V(_String, "String")                                                         \
   V(_SyncIterable, "_SyncIterable")                                            \
@@ -424,6 +430,7 @@
   V(_instanceOf, "_instanceOf")                                                \
   V(_lookupHandler, "_lookupHandler")                                          \
   V(_name, "_name")                                                            \
+  V(_onData, "_onData")                                                        \
   V(_rehashObjects, "_rehashObjects")                                          \
   V(_resultOrListeners, "_resultOrListeners")                                  \
   V(_runExtension, "_runExtension")                                            \
@@ -434,6 +441,7 @@
   V(_simpleInstanceOfTrue, "_simpleInstanceOfTrue")                            \
   V(_stackTrace, "_stackTrace")                                                \
   V(_state, "_state")                                                          \
+  V(_varData, "_varData")                                                      \
   V(_wordCharacterMap, "_wordCharacterMap")                                    \
   V(add, "add")                                                                \
   V(callback, "callback")                                                      \
@@ -442,6 +450,7 @@
   V(char_in_capture, ":char_in_capture")                                       \
   V(char_in_match, ":char_in_match")                                           \
   V(clear, "clear")                                                            \
+  V(controller, "controller")                                                  \
   V(current_character, ":current_character")                                   \
   V(current_position, ":current_position")                                     \
   V(getID, "getID")                                                            \
diff --git a/sdk/lib/_internal/vm/lib/async_patch.dart b/sdk/lib/_internal/vm/lib/async_patch.dart
index 374947d..c4266f4 100644
--- a/sdk/lib/_internal/vm/lib/async_patch.dart
+++ b/sdk/lib/_internal/vm/lib/async_patch.dart
@@ -134,6 +134,7 @@
 // async* generator functions.
 @pragma("vm:entry-point")
 class _AsyncStarStreamController<T> {
+  @pragma("vm:entry-point")
   StreamController<T> controller;
   Function asyncStarBody;
   bool isAdding = false;
diff --git a/sdk/lib/async/stream_controller.dart b/sdk/lib/async/stream_controller.dart
index f2854a9..50e9aa3 100644
--- a/sdk/lib/async/stream_controller.dart
+++ b/sdk/lib/async/stream_controller.dart
@@ -409,7 +409,12 @@
 
   /** The controller is in its initial state with no subscription. */
   static const int _STATE_INITIAL = 0;
-  /** The controller has a subscription, but hasn't been closed or canceled. */
+  /**
+   * The controller has a subscription, but hasn't been closed or canceled.
+   *
+   * Keep in sync with
+   * runtime/vm/stack_trace.cc:kStreamController_StateSubscribed.
+   */
   static const int _STATE_SUBSCRIBED = 1;
   /** The subscription is canceled. */
   static const int _STATE_CANCELED = 2;
@@ -447,9 +452,11 @@
    *
    * When [_state] is [_STATE_CANCELED] the field is currently not used.
    */
+  @pragma("vm:entry-point")
   var _varData;
 
   /** Current state of the controller. */
+  @pragma("vm:entry-point")
   int _state = _STATE_INITIAL;
 
   /**
diff --git a/sdk/lib/async/stream_impl.dart b/sdk/lib/async/stream_impl.dart
index b3969fc..0b7cc5a 100644
--- a/sdk/lib/async/stream_impl.dart
+++ b/sdk/lib/async/stream_impl.dart
@@ -89,6 +89,7 @@
   static const int _STATE_PAUSE_COUNT = 128;
 
   /* Event handlers provided in constructor. */
+  @pragma("vm:entry-point")
   _DataHandler<T> _onData;
   Function _onError;
   _DoneHandler _onDone;
diff --git a/sdk_nnbd/lib/_internal/vm/lib/async_patch.dart b/sdk_nnbd/lib/_internal/vm/lib/async_patch.dart
index 2edf7c2..fa10ad4 100644
--- a/sdk_nnbd/lib/_internal/vm/lib/async_patch.dart
+++ b/sdk_nnbd/lib/_internal/vm/lib/async_patch.dart
@@ -134,6 +134,7 @@
 // async* generator functions.
 @pragma("vm:entry-point")
 class _AsyncStarStreamController<T> {
+  @pragma("vm:entry-point")
   StreamController<T> controller;
   Function asyncStarBody;
   bool isAdding = false;
diff --git a/sdk_nnbd/lib/async/stream_controller.dart b/sdk_nnbd/lib/async/stream_controller.dart
index 9303b86..6f09d5d 100644
--- a/sdk_nnbd/lib/async/stream_controller.dart
+++ b/sdk_nnbd/lib/async/stream_controller.dart
@@ -407,7 +407,12 @@
 
   /** The controller is in its initial state with no subscription. */
   static const int _STATE_INITIAL = 0;
-  /** The controller has a subscription, but hasn't been closed or canceled. */
+  /**
+   * The controller has a subscription, but hasn't been closed or canceled.
+   *
+   * Keep in sync with
+   * runtime/vm/stack_trace.cc:kStreamController_StateSubscribed.
+   */
   static const int _STATE_SUBSCRIBED = 1;
   /** The subscription is canceled. */
   static const int _STATE_CANCELED = 2;
@@ -446,9 +451,11 @@
    * When [_state] is [_STATE_CANCELED] the field is currently not used,
    * and will contain `null`.
    */
+  @pragma("vm:entry-point")
   Object? _varData;
 
   /** Current state of the controller. */
+  @pragma("vm:entry-point")
   int _state = _STATE_INITIAL;
 
   /**
diff --git a/sdk_nnbd/lib/async/stream_impl.dart b/sdk_nnbd/lib/async/stream_impl.dart
index 5011a82..f8a4437 100644
--- a/sdk_nnbd/lib/async/stream_impl.dart
+++ b/sdk_nnbd/lib/async/stream_impl.dart
@@ -87,6 +87,7 @@
   static const int _STATE_PAUSE_COUNT = 128;
 
   /* Event handlers provided in constructor. */
+  @pragma("vm:entry-point")
   _DataHandler<T> _onData;
   Function _onError;
   _DoneHandler _onDone;