Version 2.12.0-70.0.dev

Merge commit '2149369ffde332a585c274ec50565f92fa6fd03e' into 'dev'
diff --git a/pkg/analyzer/lib/src/dart/micro/cider_byte_store.dart b/pkg/analyzer/lib/src/dart/micro/cider_byte_store.dart
index 9fd158d..2d4c16b 100644
--- a/pkg/analyzer/lib/src/dart/micro/cider_byte_store.dart
+++ b/pkg/analyzer/lib/src/dart/micro/cider_byte_store.dart
@@ -35,10 +35,17 @@
   void release(Iterable<int> ids);
 }
 
+class CiderByteStoreTestView {
+  int length = 0;
+}
+
 class CiderCachedByteStore implements CiderByteStore {
   final Cache<String, CiderCacheEntry> _cache;
   int idCounter = 0;
 
+  /// This field gets value only during testing.
+  CiderByteStoreTestView testView;
+
   CiderCachedByteStore(int maxCacheSize)
       : _cache = Cache<String, CiderCacheEntry>(
             maxCacheSize, (v) => v.data.bytes.length);
@@ -59,6 +66,7 @@
     idCounter++;
     var entry = CiderCacheEntry(signature, CacheData(idCounter, bytes));
     _cache.put(key, entry);
+    testView?.length++;
     return entry.data;
   }
 
diff --git a/pkg/analyzer/lib/src/dart/micro/library_graph.dart b/pkg/analyzer/lib/src/dart/micro/library_graph.dart
index 96b4548..b9b4b8e 100644
--- a/pkg/analyzer/lib/src/dart/micro/library_graph.dart
+++ b/pkg/analyzer/lib/src/dart/micro/library_graph.dart
@@ -519,6 +519,13 @@
     }
   }
 
+  /// Clears all the cached files. Returns the list of ids of all the removed
+  /// files.
+  Set<int> collectSharedDataIdentifiers() {
+    var files = _pathToFile.values.map((file) => file.id).toSet();
+    return files;
+  }
+
   FeatureSet contextFeatureSet(
     String path,
     Uri uri,
diff --git a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
index 26bed2a..82e250c 100644
--- a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
+++ b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
@@ -147,6 +147,13 @@
     }
   }
 
+  /// Collects all the cached artifacts and add all the cache id's for the
+  /// removed artifacts to [removedCacheIds].
+  void collectSharedDataIdentifiers() {
+    removedCacheIds.addAll(fsState.collectSharedDataIdentifiers());
+    removedCacheIds.addAll(libraryContext.collectSharedDataIdentifiers());
+  }
+
   @deprecated
   void dispose() {}
 
@@ -631,6 +638,14 @@
     );
   }
 
+  /// Clears all the loaded libraries. Returns the cache ids for the removed
+  /// artifacts.
+  Set<int> collectSharedDataIdentifiers() {
+    var ids = loadedBundles.map((cycle) => cycle.id).toSet();
+    loadedBundles.clear();
+    return ids;
+  }
+
   /// Load data required to access elements of the given [targetLibrary].
   void load2({
     @required FileState targetLibrary,
diff --git a/pkg/analyzer/test/src/dart/micro/file_resolution.dart b/pkg/analyzer/test/src/dart/micro/file_resolution.dart
index b03a488..1cf1d10 100644
--- a/pkg/analyzer/test/src/dart/micro/file_resolution.dart
+++ b/pkg/analyzer/test/src/dart/micro/file_resolution.dart
@@ -22,7 +22,7 @@
 class FileResolutionTest with ResourceProviderMixin, ResolutionTest {
   static final String _testFile = '/workspace/dart/test/lib/test.dart';
 
-  final CiderByteStore byteStore =
+  final CiderCachedByteStore byteStore =
       CiderCachedByteStore(20 * 1024 * 1024 /* 20 MB */);
 
   final StringBuffer logBuffer = StringBuffer();
@@ -45,6 +45,7 @@
       convertPath(_testFile),
     );
 
+    byteStore.testView = CiderByteStoreTestView();
     fileResolver = FileResolver.from(
       logger: logger,
       resourceProvider: resourceProvider,
diff --git a/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart b/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
index 082445b..b462f39 100644
--- a/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
+++ b/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analyzer/src/dart/error/syntactic_errors.dart';
+import 'package:analyzer/src/dart/micro/cider_byte_store.dart';
 import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer/src/lint/registry.dart';
 import 'package:matcher/matcher.dart';
@@ -309,6 +310,19 @@
     assertType(findElement.topVar('b').type, 'int');
   }
 
+  test_collectSharedDataIdentifiers() async {
+    var aPath = convertPath('/workspace/third_party/dart/aaa/lib/a.dart');
+
+    newFile(aPath, content: r'''
+class A {}
+''');
+
+    await resolveFile(aPath);
+    fileResolver.collectSharedDataIdentifiers();
+    expect(fileResolver.removedCacheIds.length,
+        (fileResolver.byteStore as CiderCachedByteStore).testView.length);
+  }
+
   test_getErrors() {
     addTestFile(r'''
 var a = b;
diff --git a/runtime/tests/vm/dart/causal_stacks/utils.dart b/runtime/tests/vm/dart/causal_stacks/utils.dart
index a0fbb82..c77ce92 100644
--- a/runtime/tests/vm/dart/causal_stacks/utils.dart
+++ b/runtime/tests/vm/dart/causal_stacks/utils.dart
@@ -13,12 +13,12 @@
 // Test functions:
 
 Future<void> throwSync() {
-  throw '';
+  throw 'throw from throwSync';
 }
 
 Future<void> throwAsync() async {
   await 0;
-  throw '';
+  throw 'throw from throwAsync';
 }
 
 // ----
@@ -170,6 +170,14 @@
   ]);
 }
 
+// ----
+// Scenario: Future.whenComplete:
+// ----
+
+Future futureSyncWhenComplete() {
+  return Future.sync(throwAsync).whenComplete(() => 'nop');
+}
+
 // Helpers:
 
 // We want lines that either start with a frame index or an async gap marker.
@@ -664,7 +672,7 @@
 
   final awaitTimeoutExpected = const <String>[
     r'^#0      throwAsync \(.*/utils.dart:21(:3)?\)$',
-    r'^^<asynchronous suspension>$',
+    r'^<asynchronous suspension>$',
     r'^#1      awaitTimeout ',
   ];
   await doTestAwait(
@@ -706,7 +714,7 @@
 
   final awaitWaitExpected = const <String>[
     r'^#0      throwAsync \(.*/utils.dart:21(:3)?\)$',
-    r'^^<asynchronous suspension>$',
+    r'^<asynchronous suspension>$',
     r'^#1      awaitWait ',
   ];
   await doTestAwait(
@@ -745,6 +753,49 @@
             r'^#6      _RawReceivePortImpl._handleMessage ',
           ],
       debugInfoFilename);
+
+  final futureSyncWhenCompleteExpected = const <String>[
+    r'^#0      throwAsync \(.*/utils.dart:21(:3)?\)$',
+    r'^<asynchronous suspension>$',
+    r'^#1      new Future.sync ',
+    r'^#2      futureSyncWhenComplete ',
+  ];
+  await doTestAwait(
+      futureSyncWhenComplete,
+      futureSyncWhenCompleteExpected +
+          const <String>[
+            r'^#3      doTestAwait ',
+            r'^#4      doTestsCausal ',
+            r'^<asynchronous suspension>$',
+            r'^#5      main \(.+\)$',
+            r'^#6      _delayEntrypointInvocation.<anonymous closure> ',
+            r'^#7      _RawReceivePortImpl._handleMessage ',
+          ],
+      debugInfoFilename);
+  await doTestAwaitThen(
+      futureSyncWhenComplete,
+      futureSyncWhenCompleteExpected +
+          const <String>[
+            r'^#3      doTestAwaitThen ',
+            r'^#4      doTestsCausal ',
+            r'^<asynchronous suspension>$',
+            r'^#5      main \(.+\)$',
+            r'^#6      _delayEntrypointInvocation.<anonymous closure> ',
+            r'^#7      _RawReceivePortImpl._handleMessage ',
+          ],
+      debugInfoFilename);
+  await doTestAwaitCatchError(
+      futureSyncWhenComplete,
+      futureSyncWhenCompleteExpected +
+          const <String>[
+            r'^#3      doTestAwaitCatchError ',
+            r'^#4      doTestsCausal ',
+            r'^<asynchronous suspension>$',
+            r'^#5      main \(.+\)$',
+            r'^#6      _delayEntrypointInvocation.<anonymous closure> ',
+            r'^#7      _RawReceivePortImpl._handleMessage ',
+          ],
+      debugInfoFilename);
 }
 
 // For: --no-causal-async-stacks --no-lazy-async-stacks
@@ -1065,12 +1116,10 @@
     r'^#8      _runPendingImmediateCallback ',
     r'^#9      _RawReceivePortImpl._handleMessage ',
   ];
-  await doTestAwait(
-      awaitTimeout, awaitTimeoutExpected + const <String>[], debugInfoFilename);
-  await doTestAwaitThen(
-      awaitTimeout, awaitTimeoutExpected + const <String>[], debugInfoFilename);
+  await doTestAwait(awaitTimeout, awaitTimeoutExpected, debugInfoFilename);
+  await doTestAwaitThen(awaitTimeout, awaitTimeoutExpected, debugInfoFilename);
   await doTestAwaitCatchError(
-      awaitTimeout, awaitTimeoutExpected + const <String>[], debugInfoFilename);
+      awaitTimeout, awaitTimeoutExpected, debugInfoFilename);
 
   final awaitWaitExpected = const <String>[
     r'#0      throwAsync \(.*/utils.dart:21(:3)?\)$',
@@ -1084,12 +1133,28 @@
     r'^#8      _runPendingImmediateCallback ',
     r'^#9      _RawReceivePortImpl._handleMessage ',
   ];
-  await doTestAwait(
-      awaitWait, awaitWaitExpected + const <String>[], debugInfoFilename);
-  await doTestAwaitThen(
-      awaitWait, awaitWaitExpected + const <String>[], debugInfoFilename);
-  await doTestAwaitCatchError(
-      awaitWait, awaitWaitExpected + const <String>[], debugInfoFilename);
+  await doTestAwait(awaitWait, awaitWaitExpected, debugInfoFilename);
+  await doTestAwaitThen(awaitWait, awaitWaitExpected, debugInfoFilename);
+  await doTestAwaitCatchError(awaitWait, awaitWaitExpected, debugInfoFilename);
+
+  {
+    final expected = const <String>[
+      r'^#0      throwAsync \(.*/utils.dart:21(:3)?\)$',
+      r'^#1      _RootZone.runUnary ',
+      r'^#2      _FutureListener.handleValue ',
+      r'^#3      Future._propagateToListeners.handleValueCallback ',
+      r'^#4      Future._propagateToListeners ',
+      r'^#5      Future.(_addListener|_prependListeners).<anonymous closure> ',
+      r'^#6      _microtaskLoop ',
+      r'^#7      _startMicrotaskLoop ',
+      r'^#8      _runPendingImmediateCallback ',
+      r'^#9      _RawReceivePortImpl._handleMessage ',
+    ];
+    await doTestAwait(futureSyncWhenComplete, expected, debugInfoFilename);
+    await doTestAwaitThen(futureSyncWhenComplete, expected, debugInfoFilename);
+    await doTestAwaitCatchError(
+        futureSyncWhenComplete, expected, debugInfoFilename);
+  }
 }
 
 // For: --lazy-async-stacks
@@ -1381,7 +1446,7 @@
           ],
       debugInfoFilename);
   await doTestAwaitCatchError(
-      awaitTimeout, awaitTimeoutExpected + const <String>[], debugInfoFilename);
+      awaitTimeout, awaitTimeoutExpected, debugInfoFilename);
 
   final awaitWaitExpected = const <String>[
     r'^#0      throwAsync \(.*/utils.dart:21(:3)?\)$',
@@ -1411,6 +1476,34 @@
             r'^<asynchronous suspension>$',
           ],
       debugInfoFilename);
-  await doTestAwaitCatchError(
-      awaitWait, awaitWaitExpected + const <String>[], debugInfoFilename);
+  await doTestAwaitCatchError(awaitWait, awaitWaitExpected, debugInfoFilename);
+
+  {
+    final expected = const <String>[
+      r'^#0      throwAsync \(.*/utils.dart:21(:3)?\)$',
+      r'^<asynchronous suspension>$',
+    ];
+    await doTestAwait(
+        futureSyncWhenComplete,
+        expected +
+            const <String>[
+              r'^#1      doTestAwait ',
+              r'^<asynchronous suspension>$',
+              r'^#2      doTestsLazy ',
+              r'^<asynchronous suspension>$',
+              r'^#3      main ',
+              r'^<asynchronous suspension>$',
+            ],
+        debugInfoFilename);
+    await doTestAwaitThen(
+        futureSyncWhenComplete,
+        expected +
+            const <String>[
+              r'^#1      doTestAwaitThen.<anonymous closure> ',
+              r'^<asynchronous suspension>$',
+            ],
+        debugInfoFilename);
+    await doTestAwaitCatchError(
+        futureSyncWhenComplete, expected, debugInfoFilename);
+  }
 }
diff --git a/runtime/tests/vm/dart_2/causal_stacks/utils.dart b/runtime/tests/vm/dart_2/causal_stacks/utils.dart
index 3b6facb..f511d4c 100644
--- a/runtime/tests/vm/dart_2/causal_stacks/utils.dart
+++ b/runtime/tests/vm/dart_2/causal_stacks/utils.dart
@@ -13,12 +13,12 @@
 // Test functions:
 
 Future<void> throwSync() {
-  throw '';
+  throw 'throw from throwSync';
 }
 
 Future<void> throwAsync() async {
   await 0;
-  throw '';
+  throw 'throw from throwAsync';
 }
 
 // ----
@@ -170,6 +170,14 @@
   ]);
 }
 
+// ----
+// Scenario: Future.whenComplete:
+// ----
+
+Future futureSyncWhenComplete() {
+  return Future.sync(throwAsync).whenComplete(() => 'nop');
+}
+
 // Helpers:
 
 // We want lines that either start with a frame index or an async gap marker.
@@ -664,7 +672,7 @@
 
   final awaitTimeoutExpected = const <String>[
     r'^#0      throwAsync \(.*/utils.dart:21(:3)?\)$',
-    r'^^<asynchronous suspension>$',
+    r'^<asynchronous suspension>$',
     r'^#1      awaitTimeout ',
   ];
   await doTestAwait(
@@ -706,7 +714,7 @@
 
   final awaitWaitExpected = const <String>[
     r'^#0      throwAsync \(.*/utils.dart:21(:3)?\)$',
-    r'^^<asynchronous suspension>$',
+    r'^<asynchronous suspension>$',
     r'^#1      awaitWait ',
   ];
   await doTestAwait(
@@ -745,6 +753,49 @@
             r'^#6      _RawReceivePortImpl._handleMessage ',
           ],
       debugInfoFilename);
+
+  final futureSyncWhenCompleteExpected = const <String>[
+    r'^#0      throwAsync \(.*/utils.dart:21(:3)?\)$',
+    r'^<asynchronous suspension>$',
+    r'^#1      new Future.sync ',
+    r'^#2      futureSyncWhenComplete ',
+  ];
+  await doTestAwait(
+      futureSyncWhenComplete,
+      futureSyncWhenCompleteExpected +
+          const <String>[
+            r'^#3      doTestAwait ',
+            r'^#4      doTestsCausal ',
+            r'^<asynchronous suspension>$',
+            r'^#5      main \(.+\)$',
+            r'^#6      _delayEntrypointInvocation.<anonymous closure> ',
+            r'^#7      _RawReceivePortImpl._handleMessage ',
+          ],
+      debugInfoFilename);
+  await doTestAwaitThen(
+      futureSyncWhenComplete,
+      futureSyncWhenCompleteExpected +
+          const <String>[
+            r'^#3      doTestAwaitThen ',
+            r'^#4      doTestsCausal ',
+            r'^<asynchronous suspension>$',
+            r'^#5      main \(.+\)$',
+            r'^#6      _delayEntrypointInvocation.<anonymous closure> ',
+            r'^#7      _RawReceivePortImpl._handleMessage ',
+          ],
+      debugInfoFilename);
+  await doTestAwaitCatchError(
+      futureSyncWhenComplete,
+      futureSyncWhenCompleteExpected +
+          const <String>[
+            r'^#3      doTestAwaitCatchError ',
+            r'^#4      doTestsCausal ',
+            r'^<asynchronous suspension>$',
+            r'^#5      main \(.+\)$',
+            r'^#6      _delayEntrypointInvocation.<anonymous closure> ',
+            r'^#7      _RawReceivePortImpl._handleMessage ',
+          ],
+      debugInfoFilename);
 }
 
 // For: --no-causal-async-stacks --no-lazy-async-stacks
@@ -1065,12 +1116,10 @@
     r'^#8      _runPendingImmediateCallback ',
     r'^#9      _RawReceivePortImpl._handleMessage ',
   ];
-  await doTestAwait(
-      awaitTimeout, awaitTimeoutExpected + const <String>[], debugInfoFilename);
-  await doTestAwaitThen(
-      awaitTimeout, awaitTimeoutExpected + const <String>[], debugInfoFilename);
+  await doTestAwait(awaitTimeout, awaitTimeoutExpected, debugInfoFilename);
+  await doTestAwaitThen(awaitTimeout, awaitTimeoutExpected, debugInfoFilename);
   await doTestAwaitCatchError(
-      awaitTimeout, awaitTimeoutExpected + const <String>[], debugInfoFilename);
+      awaitTimeout, awaitTimeoutExpected, debugInfoFilename);
 
   final awaitWaitExpected = const <String>[
     r'#0      throwAsync \(.*/utils.dart:21(:3)?\)$',
@@ -1084,12 +1133,28 @@
     r'^#8      _runPendingImmediateCallback ',
     r'^#9      _RawReceivePortImpl._handleMessage ',
   ];
-  await doTestAwait(
-      awaitWait, awaitWaitExpected + const <String>[], debugInfoFilename);
-  await doTestAwaitThen(
-      awaitWait, awaitWaitExpected + const <String>[], debugInfoFilename);
-  await doTestAwaitCatchError(
-      awaitWait, awaitWaitExpected + const <String>[], debugInfoFilename);
+  await doTestAwait(awaitWait, awaitWaitExpected, debugInfoFilename);
+  await doTestAwaitThen(awaitWait, awaitWaitExpected, debugInfoFilename);
+  await doTestAwaitCatchError(awaitWait, awaitWaitExpected, debugInfoFilename);
+
+  {
+    final expected = const <String>[
+      r'^#0      throwAsync \(.*/utils.dart:21(:3)?\)$',
+      r'^#1      _RootZone.runUnary ',
+      r'^#2      _FutureListener.handleValue ',
+      r'^#3      Future._propagateToListeners.handleValueCallback ',
+      r'^#4      Future._propagateToListeners ',
+      r'^#5      Future.(_addListener|_prependListeners).<anonymous closure> ',
+      r'^#6      _microtaskLoop ',
+      r'^#7      _startMicrotaskLoop ',
+      r'^#8      _runPendingImmediateCallback ',
+      r'^#9      _RawReceivePortImpl._handleMessage ',
+    ];
+    await doTestAwait(futureSyncWhenComplete, expected, debugInfoFilename);
+    await doTestAwaitThen(futureSyncWhenComplete, expected, debugInfoFilename);
+    await doTestAwaitCatchError(
+        futureSyncWhenComplete, expected, debugInfoFilename);
+  }
 }
 
 // For: --lazy-async-stacks
@@ -1381,7 +1446,7 @@
           ],
       debugInfoFilename);
   await doTestAwaitCatchError(
-      awaitTimeout, awaitTimeoutExpected + const <String>[], debugInfoFilename);
+      awaitTimeout, awaitTimeoutExpected, debugInfoFilename);
 
   final awaitWaitExpected = const <String>[
     r'^#0      throwAsync \(.*/utils.dart:21(:3)?\)$',
@@ -1411,6 +1476,34 @@
             r'^<asynchronous suspension>$',
           ],
       debugInfoFilename);
-  await doTestAwaitCatchError(
-      awaitWait, awaitWaitExpected + const <String>[], debugInfoFilename);
+  await doTestAwaitCatchError(awaitWait, awaitWaitExpected, debugInfoFilename);
+
+  {
+    final expect = const <String>[
+      r'^#0      throwAsync \(.*/utils.dart:21(:3)?\)$',
+      r'^<asynchronous suspension>$',
+    ];
+    await doTestAwait(
+        futureSyncWhenComplete,
+        expect +
+            const <String>[
+              r'^#1      doTestAwait ',
+              r'^<asynchronous suspension>$',
+              r'^#2      doTestsLazy ',
+              r'^<asynchronous suspension>$',
+              r'^#3      main ',
+              r'^<asynchronous suspension>$',
+            ],
+        debugInfoFilename);
+    await doTestAwaitThen(
+        futureSyncWhenComplete,
+        expect +
+            const <String>[
+              r'^#1      doTestAwaitThen.<anonymous closure> ',
+              r'^<asynchronous suspension>$',
+            ],
+        debugInfoFilename);
+    await doTestAwaitCatchError(
+        futureSyncWhenComplete, expect, debugInfoFilename);
+  }
 }
diff --git a/runtime/vm/regexp_assembler_ir.cc b/runtime/vm/regexp_assembler_ir.cc
index 0111496..003ceb7 100644
--- a/runtime/vm/regexp_assembler_ir.cc
+++ b/runtime/vm/regexp_assembler_ir.cc
@@ -166,7 +166,7 @@
   char_in_capture_ = Local(Symbols::char_in_capture());
   char_in_match_ = Local(Symbols::char_in_match());
   index_temp_ = Local(Symbols::index_temp());
-  result_ = Local(Symbols::result());
+  result_ = Local(Symbols::c_result());
 
   string_param_ = Parameter(Symbols::string_param(),
                             RegExpMacroAssembler::kParamStringIndex);
diff --git a/runtime/vm/stack_trace.cc b/runtime/vm/stack_trace.cc
index 5b146ff..502a4f9 100644
--- a/runtime/vm/stack_trace.cc
+++ b/runtime/vm/stack_trace.cc
@@ -12,7 +12,9 @@
 
 // Keep in sync with
 // sdk/lib/async/stream_controller.dart:_StreamController._STATE_SUBSCRIBED.
-const intptr_t kStreamController_StateSubscribed = 1;
+const intptr_t k_StreamController__STATE_SUBSCRIBED = 1;
+// sdk/lib/async/future_impl.dart:_FutureListener.stateWhencomplete.
+const intptr_t k_FutureListener_stateWhencomplete = 8;
 
 // Find current yield index from async closure.
 // Async closures contains a variable, :await_jump_var that holds the index into
@@ -66,6 +68,8 @@
       stream_iterator_class(Class::Handle(zone)),
       future_result_or_listeners_field(Field::Handle(zone)),
       callback_field(Field::Handle(zone)),
+      future_listener_state_field(Field::Handle(zone)),
+      future_listener_result_field(Field::Handle(zone)),
       controller_controller_field(Field::Handle(zone)),
       var_data_field(Field::Handle(zone)),
       state_field(Field::Handle(zone)),
@@ -107,6 +111,12 @@
   callback_field =
       future_listener_class.LookupFieldAllowPrivate(Symbols::callback());
   ASSERT(!callback_field.IsNull());
+  future_listener_state_field =
+      future_listener_class.LookupFieldAllowPrivate(Symbols::state());
+  ASSERT(!future_listener_state_field.IsNull());
+  future_listener_result_field =
+      future_listener_class.LookupFieldAllowPrivate(Symbols::result());
+  ASSERT(!future_listener_result_field.IsNull());
   // - async*:
   controller_controller_field =
       async_start_stream_controller_class.LookupFieldAllowPrivate(
@@ -126,16 +136,24 @@
   ASSERT(!state_data_field.IsNull());
 }
 
-ClosurePtr CallerClosureFinder::GetCallerInFutureImpl(const Object& future_) {
-  ASSERT(!future_.IsNull());
-  ASSERT(future_.GetClassId() == future_impl_class.id());
+ClosurePtr CallerClosureFinder::GetCallerInFutureImpl(const Object& future) {
+  ASSERT(!future.IsNull());
+  ASSERT(future.GetClassId() == future_impl_class.id());
 
-  listener_ =
-      Instance::Cast(future_).GetField(future_result_or_listeners_field);
+  listener_ = Instance::Cast(future).GetField(future_result_or_listeners_field);
   if (listener_.GetClassId() != future_listener_class.id()) {
     return Closure::null();
   }
 
+  // If the _FutureListener is a whenComplete listener, follow the Future being
+  // completed, `result`, instead of the dangling whenComplete `callback`.
+  state_ = Instance::Cast(listener_).GetField(future_listener_state_field);
+  ASSERT(state_.IsSmi());
+  if (Smi::Cast(state_).Value() == k_FutureListener_stateWhencomplete) {
+    future_ = Instance::Cast(listener_).GetField(future_listener_result_field);
+    return GetCallerInFutureImpl(future_);
+  }
+
   callback_ = Instance::Cast(listener_).GetField(callback_field);
   // This happens for e.g.: await f().catchError(..);
   if (callback_.IsNull()) {
@@ -166,7 +184,7 @@
 
   state_ = Instance::Cast(controller_).GetField(state_field);
   ASSERT(state_.IsSmi());
-  if (Smi::Cast(state_).Value() != kStreamController_StateSubscribed) {
+  if (Smi::Cast(state_).Value() != k_StreamController__STATE_SUBSCRIBED) {
     return Closure::null();
   }
 
diff --git a/runtime/vm/stack_trace.h b/runtime/vm/stack_trace.h
index 660746a..460074b 100644
--- a/runtime/vm/stack_trace.h
+++ b/runtime/vm/stack_trace.h
@@ -54,6 +54,8 @@
 
   Field& future_result_or_listeners_field;
   Field& callback_field;
+  Field& future_listener_state_field;
+  Field& future_listener_result_field;
   Field& controller_controller_field;
   Field& var_data_field;
   Field& state_field;
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index bbee27e..6e408a0 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -481,10 +481,12 @@
   V(position_registers, ":position_registers")                                 \
   V(print, "print")                                                            \
   V(removeLast, "removeLast")                                                  \
-  V(result, ":result")                                                         \
+  V(c_result, ":result")                                                       \
+  V(result, "result")                                                          \
   V(stack, ":stack")                                                           \
   V(stack_pointer, ":stack_pointer")                                           \
   V(start_index_param, ":start_index_param")                                   \
+  V(state, "state")                                                            \
   V(string_param, ":string_param")                                             \
   V(string_param_length, ":string_param_length")                               \
   V(toString, "toString")                                                      \
diff --git a/sdk/lib/async/future_impl.dart b/sdk/lib/async/future_impl.dart
index 1b3e02f..862c871 100644
--- a/sdk/lib/async/future_impl.dart
+++ b/sdk/lib/async/future_impl.dart
@@ -74,15 +74,22 @@
   static const int maskType =
       maskValue | maskError | maskTestError | maskWhencomplete;
   static const int stateIsAwait = 16;
+
   // Listeners on the same future are linked through this link.
   _FutureListener? _nextListener;
+
   // The future to complete when this listener is activated.
+  @pragma("vm:entry-point")
   final _Future<T> result;
+
   // Which fields means what.
+  @pragma("vm:entry-point")
   final int state;
+
   // Used for then/whenDone callback and error test
   @pragma("vm:entry-point")
   final Function? callback;
+
   // Used for error callbacks.
   final Function? errorCallback;
 
diff --git a/tools/VERSION b/tools/VERSION
index d025231..da26c81 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 69
+PRERELEASE 70
 PRERELEASE_PATCH 0
\ No newline at end of file