Version 2.19.0-23.0.dev

Merge commit 'eaac498a9abc1128ffa3f5572d2a385369ae7763' into 'dev'
diff --git a/DEPS b/DEPS
index c7b269e..f45a066 100644
--- a/DEPS
+++ b/DEPS
@@ -108,7 +108,7 @@
   # For more details, see https://github.com/dart-lang/sdk/issues/30164.
   "dart_style_rev": "d7b73536a8079331c888b7da539b80e6825270ea", # manually rev'd
 
-  "dartdoc_rev": "e6c8861ad3559a6dd61066a12bb81310ce131ae5",
+  "dartdoc_rev": "d3b0b724972ffcc70a47c35a31051af2c3ca012d",
   "devtools_rev": "95d292626da26505b02417735f77e8922783b477",
   "ffi_rev": "18b2b549d55009ff594600b04705ff6161681e07",
   "file_rev": "0132eeedea2933513bf230513a766a8baeab0c4f",
@@ -132,7 +132,7 @@
   "mockito_rev": "d8a2ddd2054390bd03d34bf64c940884e6f7a596",
   "oauth2_rev": "199ebf15cbd5b07958438184f32e41c4447a57bf",
   "package_config_rev": "cff98c90acc457a3b0750f0a7da0e351a35e5d0c",
-  "path_rev": "7a0ed40280345b1c11df4c700c71e590738f4257",
+  "path_rev": "9955b27b9bb98d87591208e19eb01c51d29fd467",
   "ply_rev": "604b32590ffad5cbb82e4afef1d305512d06ae93",
   "pool_rev": "fa84ddd0e39f45bf3f09dcc5d6b9fbdda7820fef",
   "protobuf_rev": "14c9c0b2d5542e73198a98054d93f0cb4acc846a",
@@ -143,7 +143,7 @@
   "shelf_rev": "f2cca46bdeb9888f29e685626bbe40654e1ca296",
   "source_map_stack_trace_rev": "72dbf21a33293b2b8434d0a9751e36f9463981ac",
   "source_maps_rev": "e93565b43a7b6b367789de8ffba969c4ebeeb317",
-  "source_span_rev": "24151fd80e4557a626f81f2bc0d6a2ebde172cae",
+  "source_span_rev": "ff03af16474ce91c89eb3bc28182866f4cb6dfb0",
   "sse_rev": "2df072848a6090d3ed67f30c69e86ec4d6b96cd6",
   "stack_trace_rev": "17f09c2c6845bb31c7c385acecce5befb8527a13",
   "stream_channel_rev": "8e0d7ef1f4a3fb97fbd82e11cd539093f58511f3",
@@ -161,7 +161,7 @@
   "web_components_rev": "8f57dac273412a7172c8ade6f361b407e2e4ed02",
   "web_socket_channel_rev": "99dbdc5769e19b9eeaf69449a59079153c6a8b1f",
   "WebCore_rev": "bcb10901266c884e7b3740abc597ab95373ab55c",
-  "webdev_rev": "b697974bc5218ac443e0e08c42dbe4f70cb60d23",
+  "webdev_rev": "3d2ad34a5354cfb2324d155d903b26590d79bd19",
   "webdriver_rev": "e1a9ad671ee82e05eee463f922a34585ed2d2f15",
   "webkit_inspection_protocol_rev": "57522d6b29d94903b765c757079d906555d5a171",
   "yaml_edit_rev": "01589b3ce447b03aed991db49f1ec6445ad5476d",
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/commands/perform_refactor.dart b/pkg/analysis_server/lib/src/lsp/handlers/commands/perform_refactor.dart
index a27fa77..57dae65 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/commands/perform_refactor.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/commands/perform_refactor.dart
@@ -33,13 +33,10 @@
       final refactoring = await getRefactoring(
           RefactoringKind(kind), result, offset, length, options);
       return refactoring.mapResult((refactoring) async {
-        // If the token we were given is not cancellable, replace it with one that
-        // is for the rest of this request, as a future refactor may need to cancel
-        // this request.
-        // The original token should be kept and also checked for cancellation.
-        final cancelableToken = cancellationToken is CancelableToken
-            ? cancellationToken
-            : CancelableToken();
+        // If the token we were given is not cancelable, wrap it with one that
+        // is for the rest of this request as a future refactor may need to
+        // cancel this request.
+        final cancelableToken = cancellationToken.asCancelable();
         manager.begin(cancelableToken);
 
         try {
@@ -54,15 +51,13 @@
             return success(null);
           }
 
-          if (cancellationToken.isCancellationRequested ||
-              cancelableToken.isCancellationRequested) {
+          if (cancelableToken.isCancellationRequested) {
             return error(ErrorCodes.RequestCancelled, 'Request was cancelled');
           }
 
           final change = await refactoring.createChange();
 
-          if (cancellationToken.isCancellationRequested ||
-              cancelableToken.isCancellationRequested) {
+          if (cancelableToken.isCancellationRequested) {
             return error(ErrorCodes.RequestCancelled, 'Request was cancelled');
           }
 
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/commands/validate_refactor.dart b/pkg/analysis_server/lib/src/lsp/handlers/commands/validate_refactor.dart
index 72107e8..ba9d18f 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/commands/validate_refactor.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/commands/validate_refactor.dart
@@ -42,13 +42,10 @@
       final refactoring = await getRefactoring(
           RefactoringKind(kind), result, offset, length, options);
       return refactoring.mapResult((refactoring) async {
-        // If the token we were given is not cancellable, replace it with one that
-        // is for the rest of this request, as a future refactor may need to cancel
-        // this request.
-        // The original token should be kept and also checked for cancellation.
-        final cancelableToken = cancellationToken is CancelableToken
-            ? cancellationToken
-            : CancelableToken();
+        // If the token we were given is not cancelable, wrap it with one that
+        // is for the rest of this request as a future refactor may need to
+        // cancel this request.
+        final cancelableToken = cancellationToken.asCancelable();
         manager.begin(cancelableToken);
 
         try {
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
index 40fdf76..f9cbe3b 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
@@ -48,6 +48,15 @@
   /// debugging).
   late final Duration completionBudgetDuration;
 
+  /// A cancellation token for the previous completion request.
+  ///
+  /// A new completion request will cancel the previous request. We do not allow
+  /// concurrent completion requests.
+  ///
+  /// `null` if there is no previous request. It the previous request has
+  /// already completed, cancelling this token will not do anything.
+  CancelableToken? previousRequestCancellationToken;
+
   CompletionHandler(super.server, LspInitializationOptions options)
       : suggestFromUnimportedLibraries = options.suggestFromUnimportedLibraries,
         previewNotImportedCompletions = options.previewNotImportedCompletions {
@@ -73,6 +82,12 @@
       return serverNotInitializedError;
     }
 
+    // Cancel any existing in-progress completion request in case the client did
+    // not do it explicitly, because the results will not be useful and it may
+    // delay processing this one.
+    previousRequestCancellationToken?.cancel();
+    previousRequestCancellationToken = token.asCancelable();
+
     final requestLatency = message.timeSinceRequest;
     final triggerCharacter = params.context?.triggerCharacter;
     final pos = params.position;
diff --git a/pkg/analysis_server/lib/src/services/correction/organize_imports.dart b/pkg/analysis_server/lib/src/services/correction/organize_imports.dart
index 8206e04..b31d1f9 100644
--- a/pkg/analysis_server/lib/src/services/correction/organize_imports.dart
+++ b/pkg/analysis_server/lib/src/services/correction/organize_imports.dart
@@ -59,7 +59,8 @@
   bool _isUnusedImport(UriBasedDirective directive) {
     for (var error in errors) {
       if ((error.errorCode == HintCode.DUPLICATE_IMPORT ||
-              error.errorCode == HintCode.UNUSED_IMPORT) &&
+              error.errorCode == HintCode.UNUSED_IMPORT ||
+              error.errorCode == HintCode.UNNECESSARY_IMPORT) &&
           directive.uri.offset == error.offset) {
         return true;
       }
diff --git a/pkg/analysis_server/test/lsp/completion_dart_test.dart b/pkg/analysis_server/test/lsp/completion_dart_test.dart
index 8388626..30298a0 100644
--- a/pkg/analysis_server/test/lsp/completion_dart_test.dart
+++ b/pkg/analysis_server/test/lsp/completion_dart_test.dart
@@ -735,6 +735,27 @@
         request, throwsA(isResponseError(ErrorCodes.InvalidParams)));
   }
 
+  Future<void> test_concurrentRequestsCancellation() async {
+    // We expect a new completion request to cancel any in-flight request so
+    // send multiple without awaiting, then check only the last one completes.
+    final content = '^';
+
+    await initialize();
+    await openFile(mainFileUri, withoutMarkers(content));
+    final position = positionFromMarker(content);
+    final responseFutures = [
+      getCompletion(mainFileUri, position),
+      getCompletion(mainFileUri, position),
+      getCompletion(mainFileUri, position),
+    ];
+    expect(responseFutures[0],
+        throwsA(isResponseError(ErrorCodes.RequestCancelled)));
+    expect(responseFutures[1],
+        throwsA(isResponseError(ErrorCodes.RequestCancelled)));
+    final results = await responseFutures[2];
+    expect(results, isNotEmpty);
+  }
+
   Future<void> test_filterTextNotIncludeAdditionalText() async {
     // Some completions (eg. overrides) have additional text that is not part
     // of the label. That text should _not_ appear in filterText as it will
diff --git a/pkg/analysis_server/test/services/correction/organize_directives_test.dart b/pkg/analysis_server/test/services/correction/organize_directives_test.dart
index 3d59aee..e61dd22 100644
--- a/pkg/analysis_server/test/services/correction/organize_directives_test.dart
+++ b/pkg/analysis_server/test/services/correction/organize_directives_test.dart
@@ -245,6 +245,31 @@
 }''', removeUnused: true);
   }
 
+  Future<void> test_remove_unnecessaryImports() async {
+    newFile(
+      convertPath('$testPackageLibPath/declarations.dart'),
+      'class A {} class B {}',
+    );
+    newFile(
+      convertPath('$testPackageLibPath/exports.dart'),
+      'export "a.dart" show A;',
+    );
+    await _computeUnitAndErrors(r'''
+import 'declarations.dart';
+import 'exports.dart';
+
+A? a;
+B? b;
+''');
+    // validate change
+    _assertOrganize(r'''
+import 'declarations.dart';
+
+A? a;
+B? b;
+''', removeUnused: true);
+  }
+
   Future<void> test_remove_unusedImports() async {
     await _computeUnitAndErrors(r'''
 library lib;
diff --git a/pkg/analyzer/lib/src/dart/analysis/file_state.dart b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
index a133a30e..de3a78c 100644
--- a/pkg/analyzer/lib/src/dart/analysis/file_state.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
@@ -115,13 +115,22 @@
 
 /// [AugmentationImportState] that has a valid URI.
 class AugmentationImportWithUri<U extends DirectiveUriWithUri>
-    extends AugmentationImportState<U> {
+    extends AugmentationImportWithUriStr<U> {
   AugmentationImportWithUri({
     required super.unlinked,
     required super.uri,
   });
 }
 
+/// [AugmentationImportState] that has a relative URI string.
+class AugmentationImportWithUriStr<U extends DirectiveUriWithString>
+    extends AugmentationImportState<U> {
+  AugmentationImportWithUriStr({
+    required super.unlinked,
+    required super.uri,
+  });
+}
+
 /// The URI of the [unlinked] can be resolved.
 class AugmentationKnownFileKind extends AugmentationFileKind {
   /// The file that is referenced by the [unlinked].
@@ -1883,6 +1892,11 @@
           unlinked: unlinked,
           uri: uri,
         );
+      } else if (uri is DirectiveUriWithString) {
+        return AugmentationImportWithUriStr(
+          unlinked: unlinked,
+          uri: uri,
+        );
       } else {
         return AugmentationImportState(
           unlinked: unlinked,
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index f1c3150..9d7a8ac 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -562,49 +562,49 @@
       directive.uriSource = uriState.source;
     }
 
+    // TODO(scheglov) Similarly restructure imports/exports.
     final AugmentationFileKind? importedAugmentationKind;
-    if (state is AugmentationImportWithUri) {
-      if (state.importedSource == null) {
-        // TODO(scheglov) When do we have a valid URI here and in imports?
-        final errorCode = state.uri.isValid
-            ? CompileTimeErrorCode.URI_DOES_NOT_EXIST
-            : CompileTimeErrorCode.INVALID_URI;
+    if (state is AugmentationImportWithFile) {
+      importedAugmentationKind = state.importedAugmentation;
+      if (!state.importedFile.exists) {
+        final errorCode = isGeneratedSource(state.importedSource)
+            ? CompileTimeErrorCode.URI_HAS_NOT_BEEN_GENERATED
+            : CompileTimeErrorCode.URI_DOES_NOT_EXIST;
         errorReporter.reportErrorForNode(
           errorCode,
           directive.uri,
-          [state.uri.relativeUriStr],
+          [state.importedFile.uriStr],
         );
         return;
-      } else if (state is AugmentationImportWithFile) {
-        importedAugmentationKind = state.importedAugmentation;
-        if (!state.importedFile.exists) {
-          final errorCode = isGeneratedSource(state.importedSource)
-              ? CompileTimeErrorCode.URI_HAS_NOT_BEEN_GENERATED
-              : CompileTimeErrorCode.URI_DOES_NOT_EXIST;
-          errorReporter.reportErrorForNode(
-            errorCode,
-            directive.uri,
-            [state.importedFile.uriStr],
-          );
-          return;
-        } else if (importedAugmentationKind == null) {
-          errorReporter.reportErrorForNode(
-            CompileTimeErrorCode.IMPORT_OF_NOT_AUGMENTATION,
-            directive.uri,
-            [state.importedFile.uriStr],
-          );
-          return;
-        } else if (!seenAugmentations.add(importedAugmentationKind)) {
-          errorReporter.reportErrorForNode(
-            CompileTimeErrorCode.DUPLICATE_AUGMENTATION_IMPORT,
-            directive.uri,
-            [state.importedFile.uriStr],
-          );
-          return;
-        }
-      } else {
+      } else if (importedAugmentationKind == null) {
+        errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.IMPORT_OF_NOT_AUGMENTATION,
+          directive.uri,
+          [state.importedFile.uriStr],
+        );
+        return;
+      } else if (!seenAugmentations.add(importedAugmentationKind)) {
+        errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.DUPLICATE_AUGMENTATION_IMPORT,
+          directive.uri,
+          [state.importedFile.uriStr],
+        );
         return;
       }
+    } else if (state is AugmentationImportWithUri) {
+      errorReporter.reportErrorForNode(
+        CompileTimeErrorCode.URI_DOES_NOT_EXIST,
+        directive.uri,
+        [state.uri.relativeUriStr],
+      );
+      return;
+    } else if (state is AugmentationImportWithUriStr) {
+      errorReporter.reportErrorForNode(
+        CompileTimeErrorCode.INVALID_URI,
+        directive.uri,
+        [state.uri.relativeUriStr],
+      );
+      return;
     } else {
       errorReporter.reportErrorForNode(
         CompileTimeErrorCode.URI_WITH_INTERPOLATION,
diff --git a/pkg/analyzer/lib/src/utilities/cancellation.dart b/pkg/analyzer/lib/src/utilities/cancellation.dart
index 4dff1bd..44448b1 100644
--- a/pkg/analyzer/lib/src/utilities/cancellation.dart
+++ b/pkg/analyzer/lib/src/utilities/cancellation.dart
@@ -23,3 +23,37 @@
   @override
   bool get isCancellationRequested => false;
 }
+
+/// A cancellable wrapper over another cancellation token.
+///
+/// This token will be considered cancelled if either it is itself cancelled,
+/// or if [child] is cancelled.
+///
+/// Cancelling this token will also cancel [child] if it is a cancelable
+/// token.
+class _WrappedCancelableToken extends CancelableToken {
+  final CancellationToken _child;
+
+  _WrappedCancelableToken(this._child);
+
+  @override
+  bool get isCancellationRequested =>
+      super.isCancellationRequested || _child.isCancellationRequested;
+
+  @override
+  void cancel() {
+    super.cancel();
+    final child = _child;
+    if (child is CancelableToken) {
+      child.cancel();
+    }
+  }
+}
+
+extension CancellationTokenExtension on CancellationToken {
+  /// Wraps this token to make it cancelable if it is not already.
+  CancelableToken asCancelable() {
+    final token = this;
+    return token is CancelableToken ? token : _WrappedCancelableToken(token);
+  }
+}
diff --git a/pkg/analyzer/test/src/dart/analysis/analyzer_state_printer.dart b/pkg/analyzer/test/src/dart/analysis/analyzer_state_printer.dart
index 7c3818aa..1c394c3 100644
--- a/pkg/analyzer/test/src/dart/analysis/analyzer_state_printer.dart
+++ b/pkg/analyzer/test/src/dart/analysis/analyzer_state_printer.dart
@@ -112,10 +112,12 @@
           }
           sink.writeln();
         } else if (import is AugmentationImportWithUri) {
+          _writelnWithIndent('uri: ${import.uri.relativeUri}');
+        } else if (import is AugmentationImportWithUriStr) {
           final uriStr = _stringOfUriStr(import.uri.relativeUriStr);
-          _writelnWithIndent('uri: $uriStr');
+          _writelnWithIndent('uriStr: $uriStr');
         } else {
-          _writelnWithIndent('noUri');
+          _writelnWithIndent('noUriStr');
         }
       },
     );
diff --git a/pkg/analyzer/test/src/dart/analysis/file_state_test.dart b/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
index fa16196..02bf723 100644
--- a/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
@@ -1635,9 +1635,9 @@
 ''');
   }
 
-  test_newFile_library_augmentations_invalidUri_cannotParse() async {
+  test_newFile_library_augmentations_noRelativeUri() async {
     final a = newFile('$testPackageLibPath/a.dart', r'''
-import augment 'da:';
+import augment ':net';
 ''');
 
     fileStateFor(a);
@@ -1652,7 +1652,7 @@
         libraryImports
           library_1 dart:core synthetic
         augmentationImports
-          uri: da:
+          uriStr: :net
         cycle_0
           dependencies: dart:core
           libraries: library_0
@@ -1663,7 +1663,7 @@
 ''');
   }
 
-  test_newFile_library_augmentations_invalidUri_interpolation() async {
+  test_newFile_library_augmentations_noRelativeUriStr() async {
     final a = newFile('$testPackageLibPath/a.dart', r'''
 import augment '${'foo.dart'}';
 ''');
@@ -1680,7 +1680,35 @@
         libraryImports
           library_1 dart:core synthetic
         augmentationImports
-          noUri
+          noUriStr
+        cycle_0
+          dependencies: dart:core
+          libraries: library_0
+          apiSignature_0
+      unlinkedKey: k00
+libraryCycles
+elementFactory
+''');
+  }
+
+  test_newFile_library_augmentations_noSource() async {
+    final a = newFile('$testPackageLibPath/a.dart', r'''
+import augment 'foo:bar';
+''');
+
+    fileStateFor(a);
+
+    assertDriverStateString(testFile, r'''
+files
+  /home/test/lib/a.dart
+    uri: package:test/a.dart
+    current
+      id: file_0
+      kind: library_0
+        libraryImports
+          library_1 dart:core synthetic
+        augmentationImports
+          uri: foo:bar
         cycle_0
           dependencies: dart:core
           libraries: library_0
diff --git a/pkg/analyzer/test/src/dart/resolution/augmentation_import_test.dart b/pkg/analyzer/test/src/dart/resolution/augmentation_import_test.dart
index 8cfd850..cedb204 100644
--- a/pkg/analyzer/test/src/dart/resolution/augmentation_import_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/augmentation_import_test.dart
@@ -87,38 +87,6 @@
 ''');
   }
 
-  test_inAugmentation_notAugmentation_invalidUri() async {
-    newFile('$testPackageLibPath/a.dart', r'''
-import augment 'b.dart';
-''');
-
-    final b = newFile('$testPackageLibPath/b.dart', r'''
-library augment 'a.dart';
-import augment 'da:';
-''');
-
-    await resolveFile2(b.path);
-    assertErrorsInResult([
-      error(CompileTimeErrorCode.INVALID_URI, 41, 5),
-    ]);
-
-    final node = findNode.augmentationImportDirective('da:');
-    assertResolvedNodeText(node, r'''
-AugmentationImportDirective
-  importKeyword: import
-  augmentKeyword: augment
-  uri: SimpleStringLiteral
-    literal: 'da:'
-  semicolon: ;
-  element: AugmentationImportElement
-    uri: DirectiveUriWithRelativeUri
-      relativeUri: da:
-  uriContent: da:
-  uriElement: <null>
-  uriSource: <null>
-''');
-  }
-
   test_inAugmentation_notAugmentation_library() async {
     newFile('$testPackageLibPath/a.dart', r'''
 import augment 'b.dart';
@@ -153,35 +121,109 @@
 ''');
   }
 
-  test_inAugmentation_notAugmentation_uriDoesNotExist() async {
+  test_inAugmentation_notAugmentation_noRelativeUri() async {
     newFile('$testPackageLibPath/a.dart', r'''
 import augment 'b.dart';
 ''');
 
     final b = newFile('$testPackageLibPath/b.dart', r'''
 library augment 'a.dart';
-import augment 'c.dart';
+import augment ':net';
 ''');
 
     await resolveFile2(b.path);
     assertErrorsInResult([
-      error(CompileTimeErrorCode.URI_DOES_NOT_EXIST, 41, 8),
+      error(CompileTimeErrorCode.INVALID_URI, 41, 6),
     ]);
 
-    final node = findNode.augmentationImportDirective('c.dart');
+    final node = findNode.augmentationImportDirective(':net');
     assertResolvedNodeText(node, r'''
 AugmentationImportDirective
   importKeyword: import
   augmentKeyword: augment
   uri: SimpleStringLiteral
-    literal: 'c.dart'
+    literal: ':net'
   semicolon: ;
   element: AugmentationImportElement
-    uri: DirectiveUriWithSource
-      source: package:test/c.dart
-  uriContent: c.dart
+    uri: DirectiveUriWithRelativeUriString
+      relativeUriString: :net
+  uriContent: :net
   uriElement: <null>
-  uriSource: package:test/c.dart
+  uriSource: <null>
+''');
+  }
+
+  test_inAugmentation_notAugmentation_noRelativeUriStr() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+import augment 'b.dart';
+''');
+
+    final b = newFile('$testPackageLibPath/b.dart', r'''
+library augment 'a.dart';
+import augment '${'foo'}.dart';
+''');
+
+    await resolveFile2(b.path);
+    assertErrorsInResult([
+      error(CompileTimeErrorCode.URI_WITH_INTERPOLATION, 41, 15),
+    ]);
+
+    final node = findNode.augmentationImportDirective('foo');
+    assertResolvedNodeText(node, r'''
+AugmentationImportDirective
+  importKeyword: import
+  augmentKeyword: augment
+  uri: StringInterpolation
+    elements
+      InterpolationString
+        contents: '
+      InterpolationExpression
+        leftBracket: ${
+        expression: SimpleStringLiteral
+          literal: 'foo'
+        rightBracket: }
+      InterpolationString
+        contents: .dart'
+    staticType: String
+    stringValue: null
+  semicolon: ;
+  element: AugmentationImportElement
+    uri: DirectiveUri
+  uriContent: null
+  uriElement: <null>
+  uriSource: <null>
+''');
+  }
+
+  test_inAugmentation_notAugmentation_noSource() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+import augment 'b.dart';
+''');
+
+    final b = newFile('$testPackageLibPath/b.dart', r'''
+library augment 'a.dart';
+import augment 'foo:bar';
+''');
+
+    await resolveFile2(b.path);
+    assertErrorsInResult([
+      error(CompileTimeErrorCode.URI_DOES_NOT_EXIST, 41, 9),
+    ]);
+
+    final node = findNode.augmentationImportDirective('foo:bar');
+    assertResolvedNodeText(node, r'''
+AugmentationImportDirective
+  importKeyword: import
+  augmentKeyword: augment
+  uri: SimpleStringLiteral
+    literal: 'foo:bar'
+  semicolon: ;
+  element: AugmentationImportElement
+    uri: DirectiveUriWithRelativeUri
+      relativeUri: foo:bar
+  uriContent: foo:bar
+  uriElement: <null>
+  uriSource: <null>
 ''');
   }
 
@@ -240,6 +282,30 @@
 ''');
   }
 
+  test_inLibrary_notAugmentation_fileDoesNotExist() async {
+    await assertErrorsInCode(r'''
+import augment 'a.dart';
+''', [
+      error(CompileTimeErrorCode.URI_DOES_NOT_EXIST, 15, 8),
+    ]);
+
+    final node = findNode.augmentationImportDirective('a.dart');
+    assertResolvedNodeText(node, r'''
+AugmentationImportDirective
+  importKeyword: import
+  augmentKeyword: augment
+  uri: SimpleStringLiteral
+    literal: 'a.dart'
+  semicolon: ;
+  element: AugmentationImportElement
+    uri: DirectiveUriWithSource
+      source: package:test/a.dart
+  uriContent: a.dart
+  uriElement: <null>
+  uriSource: package:test/a.dart
+''');
+  }
+
   test_inLibrary_notAugmentation_library() async {
     newFile('$testPackageLibPath/a.dart', '');
 
@@ -266,6 +332,88 @@
 ''');
   }
 
+  test_inLibrary_notAugmentation_noRelativeUri() async {
+    await assertErrorsInCode(r'''
+import augment ':net';
+''', [
+      error(CompileTimeErrorCode.INVALID_URI, 15, 6),
+    ]);
+
+    final node = findNode.augmentationImportDirective(':net');
+    assertResolvedNodeText(node, r'''
+AugmentationImportDirective
+  importKeyword: import
+  augmentKeyword: augment
+  uri: SimpleStringLiteral
+    literal: ':net'
+  semicolon: ;
+  element: AugmentationImportElement
+    uri: DirectiveUriWithRelativeUriString
+      relativeUriString: :net
+  uriContent: :net
+  uriElement: <null>
+  uriSource: <null>
+''');
+  }
+
+  test_inLibrary_notAugmentation_noRelativeUriStr() async {
+    await assertErrorsInCode(r'''
+import augment '${'foo'}.dart';
+''', [
+      error(CompileTimeErrorCode.URI_WITH_INTERPOLATION, 15, 15),
+    ]);
+
+    final node = findNode.augmentationImportDirective('foo');
+    assertResolvedNodeText(node, r'''
+AugmentationImportDirective
+  importKeyword: import
+  augmentKeyword: augment
+  uri: StringInterpolation
+    elements
+      InterpolationString
+        contents: '
+      InterpolationExpression
+        leftBracket: ${
+        expression: SimpleStringLiteral
+          literal: 'foo'
+        rightBracket: }
+      InterpolationString
+        contents: .dart'
+    staticType: String
+    stringValue: null
+  semicolon: ;
+  element: AugmentationImportElement
+    uri: DirectiveUri
+  uriContent: null
+  uriElement: <null>
+  uriSource: <null>
+''');
+  }
+
+  test_inLibrary_notAugmentation_noSource() async {
+    await assertErrorsInCode(r'''
+import augment 'foo:bar';
+''', [
+      error(CompileTimeErrorCode.URI_DOES_NOT_EXIST, 15, 9),
+    ]);
+
+    final node = findNode.augmentationImportDirective('foo:bar');
+    assertResolvedNodeText(node, r'''
+AugmentationImportDirective
+  importKeyword: import
+  augmentKeyword: augment
+  uri: SimpleStringLiteral
+    literal: 'foo:bar'
+  semicolon: ;
+  element: AugmentationImportElement
+    uri: DirectiveUriWithRelativeUri
+      relativeUri: foo:bar
+  uriContent: foo:bar
+  uriElement: <null>
+  uriSource: <null>
+''');
+  }
+
   test_inLibrary_notAugmentation_partOfName() async {
     newFile('$testPackageLibPath/a.dart', r'''
 part of my.lib;
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_uri_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_uri_test.dart
index 89560e1..d63fc0a 100644
--- a/pkg/analyzer/test/src/diagnostics/invalid_uri_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/invalid_uri_test.dart
@@ -17,7 +17,7 @@
 class InvalidUriTest extends PubPackageResolutionTest {
   test_augmentationImport_invalidScheme() async {
     await assertErrorsInCode('''
-import augment 'ht:';
+import augment ':da';
 ''', [
       error(CompileTimeErrorCode.INVALID_URI, 15, 5),
     ]);
diff --git a/pkg/analyzer/test/src/diagnostics/uri_does_not_exist_test.dart b/pkg/analyzer/test/src/diagnostics/uri_does_not_exist_test.dart
index 81707fb..25f897b 100644
--- a/pkg/analyzer/test/src/diagnostics/uri_does_not_exist_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/uri_does_not_exist_test.dart
@@ -15,14 +15,6 @@
 
 @reflectiveTest
 class UriDoesNotExistTest extends PubPackageResolutionTest {
-  test_augmentationImport() async {
-    await assertErrorsInCode('''
-import augment 'unknown.dart';
-''', [
-      error(CompileTimeErrorCode.URI_DOES_NOT_EXIST, 15, 14),
-    ]);
-  }
-
   test_libraryExport() async {
     await assertErrorsInCode('''
 export 'unknown.dart';
diff --git a/pkg/dart2wasm/lib/code_generator.dart b/pkg/dart2wasm/lib/code_generator.dart
index e10b3e2..0120221 100644
--- a/pkg/dart2wasm/lib/code_generator.dart
+++ b/pkg/dart2wasm/lib/code_generator.dart
@@ -256,7 +256,8 @@
       thisLocal = paramLocals[0];
       w.RefType thisType = info.nonNullableType;
       if (translator.needsConversion(paramLocals[0].type, thisType) &&
-          !(cls == translator.ffiPointerClass ||
+          !(cls == translator.objectInfo.cls ||
+              cls == translator.ffiPointerClass ||
               translator.isFfiCompound(cls))) {
         preciseThisLocal = addLocal(thisType);
         b.local_get(paramLocals[0]);
diff --git a/runtime/docs/async.md b/runtime/docs/async.md
new file mode 100644
index 0000000..34de12a
--- /dev/null
+++ b/runtime/docs/async.md
@@ -0,0 +1,549 @@
+# Suspendable Functions (`async`, `async*` and `sync*`)
+
+This document describes the implementation of _suspendable_ functions (functions with `async`,
+`async*` or `sync*` modifier) in Dart VM. The execution of such functions can be suspended in
+the middle at `await`/`yield`/`yield*` and resumed afterwards.
+
+When suspending a function, its local execution state (local variables and temporaries) is saved
+and the control is returned to the caller of the suspended function.
+When resuming a function, its local execution state is restored and execution continues within
+the suspendable function from the point where it was suspended.
+
+In order to minimize code size, the implementation is built using a variety of _stubs_ - reusable
+snippets of machine code generated by the VM/AOT.
+The high-level Dart logic used to implement suspendable functions (such as managing
+Futures/Streams/Iterators) is factored into helper Dart methods in core library.
+
+The rest of the document is organized as follows: first, general mechanisms for implementation of
+suspendable functions are described.
+After that, `async`, `async*` and `sync*` implementations are outlined using the general
+mechanisms introduced before.
+
+# Building blocks common to all suspendable functions
+
+## SuspendState objects
+
+SuspendState objects are allocated on the heap and encapsulate the saved state of a suspended
+function. When suspending a function, its local frame (including local variables, spill slots
+and expression stack) is copied from the stack to a SuspendState object on the heap.
+When resuming a function, the frame is recreated and copied back from the SuspendState object
+into the stack.
+
+SuspendState objects have variable size and keep frame in the "payload" following a few fixed
+fields.
+
+In addition to a stack frame, SuspendState records a PC in the code of the suspended function
+where execution was suspended and can be resumed.
+The PC is also used by GC to find a stack map and scan through the pointers in the copied frame.
+
+SuspendState object also holds data and callbacks specific to a particular kind of suspendable
+function.
+
+SuspendState object is allocated during the first suspension and can be reused for the subsequent
+suspensions of the same function.
+
+For the declaration of SuspendState see [object.h](https://github.com/dart-lang/sdk/blob/main/runtime/vm/object.h#:~:text=class%20SuspendState),
+UntaggedSuspendState is declared in [raw_object.h](https://github.com/dart-lang/sdk/blob/main/runtime/vm/raw_object.h#:~:text=class%20UntaggedSuspendState).
+
+There is also a corresponding Dart class `_SuspendState`, declared in [async_patch.dart](https://github.com/dart-lang/sdk/blob/main/sdk/lib/_internal/vm/lib/async_patch.dart#:~:text=class%20_SuspendState).
+It contains Dart methods which are used to customize implementation for a particular kind of
+suspendable function.
+
+## Frame of a suspendable function
+
+Suspendable functions are never inlined into other functions, so their local state is not mixed
+with the state of their callers (but other functions may be inlined into them).
+
+In order to have a single contiguous region of memory to copy during suspend/resume, parameters of
+suspendable functions are always copied into the local frame in the function prologue (see uses of
+`Function::MakesCopyOfParameters()` predicate).
+
+In order to keep and reuse SuspendState object, each suspendable function has an artificial local
+variable `:suspend_state` (see uses of `ParsedFunction::suspend_state_var()`), which is always
+allocated at the fixed offset in frame. It occupies the first local variable slot
+(`SuspendState::kSuspendStateVarIndex`) in case of unoptimized code or the first spill slot
+in case of optimized code (see `FlowGraphAllocator::AllocateSpillSlotForSuspendState`).
+The fixed location helps to find this variable in various stubs and runtime.
+
+## Prologue and InitSuspendableFunction stub
+
+At the very beginning of a suspendable function `null` is stored into `:suspend_state` variable.
+This guarantees that `:suspend_state` variable can be accessed any time by GC and exception
+handling.
+
+After checking bounds of type arguments and types of arguments, suspendable functions call
+InitSuspendableFunction stub.
+
+InitSuspendableFunction stub does the following:
+
+- It calls a static generic Dart method specific to a particular kind of suspendable function.
+  The argument of the stub is passed as type arguments to that method.
+  Dart method performs initialization specific to a particular kind of suspendable function
+  (for example, it creates `_Future<T>()` for async functions).
+  It returns the instance which is used as a function-specific data.
+
+- Stub puts the function-specific data to `:suspend_state` variable, where it can be found by
+  Suspend or Return stubs later.
+
+## Suspend stub
+
+Suspend stub is called from a suspendable function when its execution should be suspended.
+
+Suspend stub does the following:
+
+- It inspects `:suspend_state` variable and checks if it contains an instance of SuspendState.
+  If it doesn't, then stub allocates a new instance with a payload sufficient to hold a frame of
+  the suspendable function. The newly allocated SuspendState is stored into `:suspend_state`
+  variable, and previous value of `:suspend_state` (coming from InitSuspendableFunction stub) is
+  saved to `SuspendState.function_data`.
+
+- In JIT mode, size of the frame may vary over time - expression stack depth varies during
+  execution of unoptimized code and frame size may change during deoptimization and OSR.
+  In AOT mode size of the stack frame stays the same.
+  So, if stub finds an existing SuspendState object in JIT mode, it also checks if its frame
+  payload has a sufficient size to hold a frame of the suspendable function. If it is not
+  large enough, suspend stub calls `AllocateSuspendState` runtime entry to allocate a larger
+  SuspendState object. The same runtime entry is called for slow path when allocating
+  SuspendState for the first time.
+
+- The return address from Suspend stub to the suspendable function is saved to `SuspendState.pc`.
+  It will be used to resume execution later.
+
+- The contents of the stack frame of the suspendable function between FP and SP is copied into
+  SuspendState.
+
+- Write barrier: if SuspendState object resides in the old generation, then
+  EnsureRememberedAndMarkingDeferred runtime entry is called.
+
+- If implementation of particular kind of suspendable function uses a customized Dart method
+  for the suspension, then that method is called.
+  Suspend stub supports passing one argument to the customization method.
+  The result of the method is returned back to the caller of the suspendable function - it's
+  the result of the suspendable function.
+  If such method is not used, then Suspend stub returns its argument (so suspendable function
+  could customize its return value).
+
+- On architectures other than x64/ia32, the frame of the suspendable function is removed and
+  stub returns directly to the caller of the suspendable function.
+  On x64/ia32, in order to maintain call/return balance and avoid performance penalty,
+  Suspend stub returns to the suspendable function which immediately returns to its caller.
+
+For more details see `StubCodeCompiler::GenerateSuspendStub` in [stub_code_compiler.cc](https://github.com/dart-lang/sdk/blob/main/runtime/vm/compiler/stub_code_compiler.cc#:~:text=StubCodeCompiler::GenerateSuspendStub).
+
+## Resume stub
+
+Resume stub is tail-called from `_SuspendState._resume` recognized method (which is called
+from Dart helpers). It is used to resume execution of the previously suspended function.
+
+Resume stub does the following:
+
+- Allocates Dart frame on the stack, using `SuspendState.frame_size` to calculate its size.
+
+- Copies frame contents from SuspendState to the stack.
+
+- In JIT mode restores pool pointer (PP).
+
+- Checks for the following cases and calls ResumeFrame runtime entry if any of this is true:
+  + If resuming with an exception.
+  + In JIT mode, if Code of the suspendable function is disabled (deoptimized).
+  + In JIT mode, if there is a resumption breakpoint set by debugger.
+
+- Otherwise, jumps to `SuspendState.pc` to resume execution of the suspended function.
+  On x64/ia32 the continuation PC is adjusted by adding `SuspendStubABI::kResumePcDistance`
+  to skip over the epilogue which immediately follows the Suspend stub call to maintain
+  call/return balance.
+
+ResumeFrame runtime entry is called as if it was called from suspended function at continuation PC.
+It handles all corner cases by throwing an exception, lazy deoptimizing or calling into
+the debugger.
+
+For more details see `StubCodeCompiler::GenerateResumeStub` in [stub_code_compiler.cc](https://github.com/dart-lang/sdk/blob/main/runtime/vm/compiler/stub_code_compiler.cc#:~:text=StubCodeCompiler::GenerateResumeStub)
+and `ResumeFrame` in [runtime_entry.cc](https://github.com/dart-lang/sdk/blob/main/runtime/vm/runtime_entry.cc#:~:text=ResumeFrame).
+
+## Return stub
+
+Suspendable functions can use Return stub if they need to do something when execution of
+a function ends (for example, complete a Future or close a Stream). In such a case,
+suspendable function jumps to the Return stub instead of returning.
+
+Return stub does the following:
+
+- Removes the frame of the suspendable function (as if function epilogue was executed).
+
+- Calls a Dart method specific to a particular kind of suspendable function.
+  The customization method takes a value of `:suspend_state` variable and a return value
+  passed from the body of the suspendable function to the stub.
+
+- The value returned from the customization method is used as the result of
+  the suspendable function.
+
+For more details see `StubCodeCompiler::GenerateReturnStub` in [stub_code_compiler.cc](https://github.com/dart-lang/sdk/blob/main/runtime/vm/compiler/stub_code_compiler.cc#:~:text=StubCodeCompiler::GenerateReturnStub).
+
+## Exception handling and AsyncExceptionHandler stub
+
+Certain kinds of suspendable functions (async and async*) may need to catch all thrown exceptions
+which are not caught within the function body, and perform certain actions (such as completing
+the Future with an error).
+
+This is implemented by setting `has_async_handler` bit on `ExceptionHandlers` object.
+When looking for an exception handler, runtime checks if this bit is set and uses
+AsyncExceptionHandler stub as a handler (see `StackFrame::FindExceptionHandler`).
+
+AsyncExceptionHandler stub does the following:
+
+- It inspects the value of `:suspend_state` variable. If it is `null` (meaning the prologue has not
+  finished yet), the exception should not be handled and it is rethrown.
+  This makes it possible for argument type checks to throw an exception  synchronously
+  instead of completing a Future with an error.
+
+- Otherwise, stub removes the frame of the suspendable function (as if function epilogue was
+  executed) and calls `_SuspendState._handleException` Dart method. AsyncExceptionHandler stub
+  does not use separate Dart helper methods for async and async* functions as exception handling is
+  not performance sensitive and currently uses only one bit in `ExceptionHandlers` to select
+  a stub handler for simplicity.
+
+- The value returned from `_SuspendState._handleException` is used as the result of the
+  suspendable function.
+
+For more details see `StubCodeCompiler::GenerateAsyncExceptionHandlerStub` in [stub_code_compiler.cc](https://github.com/dart-lang/sdk/blob/main/runtime/vm/compiler/stub_code_compiler.cc#:~:text=StubCodeCompiler::GenerateAsyncExceptionHandlerStub).
+
+## IL instructions
+
+When compiling suspendable functions, the following IL instructions are used:
+
+- `Call1ArgStub` instruction is used to call one-argument stubs such as InitSuspendableFunction.
+
+- `Suspend` instruction is used to call Suspend stub. After calling Suspend stub,
+  on x64/ia32 it also generates an epilogue right after the stub, in order to
+  return to the caller after suspending without disrupting call/return balance.
+  Due to this extra epilogue following the Suspend stub call, the resumption PC is
+  not the same as the return address of the Suspend stub. So `Suspend` instruction
+  uses 2 distinct deopt ids for the Suspend stub call and resumption PC.
+
+- `Return` instruction jumps to a Return stub instead of returning for certain kinds
+  of suspendable functions (async and async*).
+
+# Combining all pieces together
+
+## Async functions
+
+See [async_patch.dart](https://github.com/dart-lang/sdk/blob/main/sdk/lib/_internal/vm/lib/async_patch.dart) for the corresponding Dart source code.
+
+Async functions use the following customized stubs:
+
+### InitAsync stub
+
+InitAsync = InitSuspendableFunction stub which calls `_SuspendState._initAsync`.
+
+`_SuspendState._initAsync` creates a `_Future<T>` instance which is used as the result of
+the async function. This `_Future<T>` instance is kept in `:suspend_state` variable until
+`_SuspendState` instance is created during the first `await`, and then kept in
+`_SuspendState._functionData`. This instance is returned from `_SuspendState._await`,
+`_SuspendState._returnAsync`, `_SuspendState._returnAsyncNotFuture` and
+`_SuspendState._handleException` methods to serve as the result of the async function.
+
+### Await stub
+
+Await = Suspend stub which calls `_SuspendState._await`. It implements the `await` expression.
+
+`_SuspendState._await` allocates 'then' and 'error' callback closures when called for
+the first time. These callback closures resume execution of the async function via Resume stub.
+It is possible to create callbacks eagerly in the InitAsync stub, but there is a significant
+fraction of async functions which don't have `await` at all, so creating callbacks lazily during
+the first `await` makes those functions more efficient.
+If an argument of `await` is a Future, then `_SuspendState._await` attaches 'then' and 'error'
+callbacks to that Future. Otherwise it schedules a micro-task to continue execution of
+the suspended function later.
+
+### ReturnAsync stub
+
+ReturnAsync stub = Return stub which calls `_SuspendState._returnAsync`.
+It is used to implement `return` statement (either explicit or implicit when reaching
+the end of function).
+
+`_SuspendState._returnAsync` completes `_Future<T>` which is used as the result of
+the async function.
+
+### ReturnAsyncNotFuture stub
+
+ReturnAsyncNotFuture stub = Return stub which calls `_SuspendState._returnAsyncNotFuture`.
+
+ReturnAsyncNotFuture is similar to ReturnAsync, but used when compiler can prove that
+return value is not a Future. It bypasses the expensive `is Future` test.
+
+### Execution flow in async functions
+
+The following diagram depicts how the control is passed in a typical async function:
+
+```
+Caller          Future<T> foo() async           Stubs        Dart _SuspendState methods
+  |
+  *-------------------> |
+                    (prologue) -------------> InitAsync
+                                                  |
+                                                  *----------> _initAsync
+                                                              (creates _Future<T>)
+                                                  | <---------
+                        | <-----------------------*
+                        |
+                        |
+                      (await) ----------------> AwaitAsync
+                                                  |
+                                                  *----------> _await
+                                                              (setups resumption)
+                                                              (returns _Future<T>)
+                                                  | <---------
+  | <---------------------------------------------*
+
+Awaited Future is completed
+  |
+  *------------------------------------------> Resume
+                                                  |
+                    (after await) <---------------*
+                        |
+                        |
+                      (return) ---------------> ReturnAsync/ReturnAsyncNotFuture
+                                                  |
+                                                  *----------> _returnAsync/_returnAsyncNotFuture
+                                                              (completes _Future<T>)
+                                                              (returns _Future<T>)
+                                                  | <---------
+  | <---------------------------------------------*
+```
+
+## Async* functions
+
+See [async_patch.dart](https://github.com/dart-lang/sdk/blob/main/sdk/lib/_internal/vm/lib/async_patch.dart)
+for the corresponding Dart source code.
+
+Async* functions use the following customized stubs:
+
+### InitAsyncStar stub
+
+InitAsyncStar = InitSuspendableFunction stub which calls `_SuspendState._initAsyncStar`.
+
+`_SuspendState._initAsyncStar` creates `_AsyncStarStreamController<T>` instance which is used
+to control the Stream returned from the async* function. `_AsyncStarStreamController<T>` is kept
+in `_SuspendState._functionData` (after the first suspension at the beginning of async* function).
+
+## YieldAsyncStar stub and `yield`/`yield*`
+
+YieldAsyncStar = Suspend stub which calls `_SuspendState._yieldAsyncStar`.
+
+This stub is used to suspend async* function at the beginning (until listener is attached to
+the Stream returned from async* function), and at `yield` / `yield*` statements.
+
+When `_SuspendState._yieldAsyncStar` is called at the beginning of async* function it creates
+a callback closure to resume body of the async* function (via Resume stub), creates and
+returns `Stream`.
+
+`yield` / `yield*` statements are implemented in the following way:
+
+```
+_AsyncStarStreamController controller = :suspend_state._functionData;
+if (controller.add/addStream(<expr>)) {
+  return;
+}
+if (YieldAsyncStar()) {
+  return;
+}
+```
+
+`_AsyncStarStreamController.add`, `_AsyncStarStreamController.addStream` and YieldAsyncStar stub
+can return `true` to indicate that Stream doesn't have a listener anymore and execution of
+async* function should end.
+
+Note that YieldAsyncStar stub returns a value passed to a Resume stub when resuming async*
+function, so the 2nd hasListeners check happens right before the async* function is resumed.
+
+See `StreamingFlowGraphBuilder::BuildYieldStatement` for more details about `yield` / `yield*`.
+
+### Await stub
+
+Async* functions use the same Await stub which is used by async functions.
+
+### ReturnAsyncStar stub
+
+ReturnAsyncStar stub = Return stub which calls `_SuspendState._returnAsyncStar`.
+
+`_SuspendState._returnAsyncStar` closes the Stream.
+
+### Execution flow in async* functions
+
+The following diagram depicts how the control is passed in a typical async* function:
+
+```
+Caller          Stream<T> foo() async*          Stubs        Dart helper methods
+  |
+  *-------------------> |
+                    (prologue) -------------> InitAsyncStar
+                                                  |
+                                                  *----------> _SuspendState._initAsyncStar
+                                                              (creates _AsyncStarStreamController<T>)
+                                                  | <---------
+                        | <-----------------------*
+                        * ------------------> YieldAsyncStar
+                                                  |
+                                                  *----------> _SuspendState._yieldAsyncStar
+                                                              (setups resumption)
+                                                              (returns _AsyncStarStreamController.stream)
+                                                  | <---------
+  | <---------------------------------------------*
+
+Stream is listened
+  |
+  *------------------------------------------> Resume
+                                                  |
+                  (after prologue) <--------------*
+                        |
+                        |
+                      (yield) --------------------------------> _AsyncStarStreamController.add
+                                                              (adds value to Stream)
+                                                              (checks if there are listeners)
+                        | <-----------------------------------
+                        * ------------------> YieldAsyncStar
+                                                  |
+                                                  *----------> _SuspendState._yieldAsyncStar
+                                                  | <---------
+  | <---------------------------------------------*
+
+Micro-task to run async* body
+  |
+  *----------------------------------------------------------> _AsyncStarStreamController.runBody
+                                                              (checks if there are listeners)
+                                                Resume <-------
+                                                  |
+                    (after yield) <---------------*
+                        |
+                        |
+                      (return) ---------------> ReturnAsyncStar
+                                                  |
+                                                  *----------> _SuspendState._returnAsyncStar
+                                                              (closes _AsyncStarStreamController)
+                                                  | <---------
+  | <---------------------------------------------*
+```
+
+## Sync* functions
+
+See [async_patch.dart](https://github.com/dart-lang/sdk/blob/main/sdk/lib/_internal/vm/lib/async_patch.dart)
+for the corresponding Dart source code.
+
+Sync* functions use the following customized stubs:
+
+### InitSyncStar stub
+
+InitSyncStar = InitSuspendableFunction stub which calls `_SuspendState._initSyncStar`.
+
+`_SuspendState._initSyncStar` creates a `_SyncStarIterable<T>` instance which is returned
+from sync* function.
+
+### SuspendSyncStarAtStart stub
+
+SuspendSyncStarAtStart = Suspend stub which calls `_SuspendState._suspendSyncStarAtStart`.
+
+This stub is used to suspend execution of sync* at the beginning. It is called after
+InitSyncStar in the sync* function prologue. The body of sync* function doesn't run
+until Iterator is not obtained from Iterable (`_SyncStarIterable<T>`) which is returned from
+the sync* function.
+
+### CloneSuspendState stub
+
+This stub creates a copy of SuspendState object. It is used to clone state of sync*
+function (suspended at the beginning) for each Iterator instance obtained from
+Iterable.
+
+See `StubCodeCompiler::GenerateCloneSuspendStateStub`.
+
+### SuspendSyncStarAtYield stub and `yield`/`yield*`
+
+SuspendSyncStarAtYield = Suspend stub which doesn't call helper Dart methods.
+
+SuspendSyncStarAtYield is used to implement `yield` / `yield*` statements in sync* functions.
+
+`yield` / `yield*` statements are implemented in the following way:
+
+```
+_SyncStarIterator iterator = :suspend_state._functionData;
+
+iterator._current = <expr>;             // yield <expr>
+  OR
+iterator._yieldStarIterable = <expr>;   // yield* <expr>
+
+SuspendSyncStarAtYield(true);
+```
+
+See `StreamingFlowGraphBuilder::BuildYieldStatement` for more details about `yield` / `yield*`.
+
+The value passed to SuspendSyncStarAtYield is returned back from the invocation of
+Resume stub. `true` indicates that iteration can continue.
+
+### Returning from sync* functions.
+
+Sync* function do not use Return stubs. Instead, return statements are rewritten to return `false`
+in order to indicate that iteration is finished.
+
+### Execution flow in sync* functions
+
+The following diagram depicts how the control is passed in a typical sync* function:
+
+```
+Caller          Iterable<T> foo() sync*          Stubs        Dart helpers
+  |
+  *-------------------> |
+                    (prologue) -------------> InitSyncStar
+                                                  |
+                                                  *----------> _SuspendState._initSyncStar
+                                                              (creates _SyncStarIterable<T>)
+                                                  | <---------
+                        | <-----------------------*
+                        * ------------------> SuspendSyncStarAtStart
+                                                  |
+                                                  *----------> _SuspendState._suspendSyncStarAtStart
+                                                              (remembers _SuspendState at start)
+                                                              (returns _SyncStarIterable<T>)
+                                                  | <---------
+  | <---------------------------------------------*
+
+Iterable.iterator is called
+  |
+  *----------------------------------------------------------> _SyncStarIterable<T>.iterator
+                                                              (creates _SyncStarIterator<T>)
+                                                                |
+                                      CloneSuspendState <-------*
+                                      (makes a copy of _SuspendState at start)
+                                                  |
+                                                  *-----------> |
+  | <------------------------------------------------------- (returns _SyncStarIterator<T>)
+
+Iterator.moveNext is called
+  |
+  *----------------------------------------------------------> _SyncStarIterator<T>.moveNext
+                                                              (iterates over the cached yield* iterator, if any)
+                                                              (resumes sync* body to get the next element)
+                                                Resume <-------
+                                                  |
+                  (after prologue) <--------------*
+                        |
+                        |
+                      (yield) ---------------> SuspendSyncStarAtYield(true)
+                                                  |
+                                                  *---------->
+                                                              (the next element is cached in _SyncStarIterator<T>._current)
+                                                              (returns true indicating that the next element is available)
+  | <----------------------------------------------------------
+
+Iterator.moveNext is called
+  |
+  *----------------------------------------------------------> _SyncStarIterator<T>.moveNext
+                                                              (iterates over the cached yield* iterator, if any)
+                                                              (resumes sync* body to get the next element)
+                                                Resume <-------
+                                                  |
+                  (after yield) <-----------------*
+                        |
+                        |
+                  (return false) ----------------------------->
+                                                              (returns false indicating that iteration is finished)
+  | <----------------------------------------------------------
+```
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index ac54b5f..818a051 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -949,12 +949,11 @@
     return Object::null();
   }
 
-  if (function().NumOptionalParameters() > 0) {
-    // If the function has optional parameters, the first positional parameter
-    // can be in a number of places in the caller's frame depending on how many
-    // were actually supplied at the call site, but they are copied to a fixed
-    // place in the callee's frame.
-
+  if (function().MakesCopyOfParameters()) {
+    // Function parameters are copied to a fixed place in the callee's frame.
+    if (function().IsSuspendableFunction()) {
+      ++index;  // Skip slot reserved for :suspend_state variable.
+    }
     return GetVariableValue(LocalVarAddress(
         fp(), runtime_frame_layout.FrameSlotForVariableIndex(-index)));
   } else {
diff --git a/sdk/lib/_internal/wasm/lib/object_patch.dart b/sdk/lib/_internal/wasm/lib/object_patch.dart
index 8b727d2..e61e064 100644
--- a/sdk/lib/_internal/wasm/lib/object_patch.dart
+++ b/sdk/lib/_internal/wasm/lib/object_patch.dart
@@ -39,7 +39,7 @@
 
   /// Concrete subclasses of [Object] will have overrides of [_typeArguments]
   /// which return their type arguments.
-  List<Type> get _typeArguments => const [];
+  List<_Type> get _typeArguments => const [];
 
   /// We use [_runtimeType] for internal type testing, because objects can
   /// override [runtimeType].
diff --git a/tools/VERSION b/tools/VERSION
index fe46c13..e499039 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 19
 PATCH 0
-PRERELEASE 22
+PRERELEASE 23
 PRERELEASE_PATCH 0
\ No newline at end of file