Version 2.18.0-243.0.dev

Merge commit 'abedfaf62a2a426d44142dc97aa342524feedf8f' into 'dev'
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index d19cc50..bd9d063 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -85,7 +85,7 @@
 /// TODO(scheglov) Clean up the list of implicitly analyzed files.
 class AnalysisDriver implements AnalysisDriverGeneric {
   /// The version of data format, should be incremented on every format change.
-  static const int DATA_VERSION = 226;
+  static const int DATA_VERSION = 227;
 
   /// The number of exception contexts allowed to write. Once this field is
   /// zero, we stop writing any new exception contexts in this process.
diff --git a/pkg/analyzer/lib/src/dart/analysis/file_state.dart b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
index 8e11c437..9fd787f 100644
--- a/pkg/analyzer/lib/src/dart/analysis/file_state.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
@@ -150,15 +150,16 @@
   Source? get exportedSource => null;
 }
 
-/// [ExportDirectiveState] that has a valid URI that references a file.
-class ExportDirectiveWithFile extends ExportDirectiveState {
+/// [ExportDirectiveWithUri] that has a valid URI that references a file.
+class ExportDirectiveWithFile extends ExportDirectiveWithUri {
   final LibraryOrAugmentationFileKind container;
   final FileState exportedFile;
 
   ExportDirectiveWithFile({
-    required super.directive,
     required this.container,
+    required super.directive,
     required this.exportedFile,
+    required super.selectedUriStr,
   }) {
     exportedFile.referencingFiles.add(container.file);
   }
@@ -189,14 +190,15 @@
   }
 }
 
-/// [ExportDirectiveState] with a URI that resolves to [InSummarySource].
-class ExportDirectiveWithInSummarySource extends ExportDirectiveState {
+/// [ExportDirectiveWithUri] with a URI that resolves to [InSummarySource].
+class ExportDirectiveWithInSummarySource extends ExportDirectiveWithUri {
   @override
   final InSummarySource exportedSource;
 
   ExportDirectiveWithInSummarySource({
     required super.directive,
     required this.exportedSource,
+    required super.selectedUriStr,
   });
 
   @override
@@ -209,6 +211,16 @@
   }
 }
 
+/// [ExportDirectiveState] that has a valid URI string.
+class ExportDirectiveWithUri extends ExportDirectiveState {
+  final String selectedUriStr;
+
+  ExportDirectiveWithUri({
+    required super.directive,
+    required this.selectedUriStr,
+  });
+}
+
 /// A library from [SummaryDataStore].
 class ExternalLibrary {
   final InSummarySource source;
@@ -366,6 +378,9 @@
   List<FileState?> get exportedFiles {
     return _exportedFiles ??= _unlinked2!.exports.map((directive) {
       var uri = _selectRelativeUri(directive);
+      if (uri == null) {
+        return null;
+      }
       return _fileForRelativeUri(uri).map(
         (file) {
           file?.referencingFiles.add(this);
@@ -383,6 +398,9 @@
   List<FileState?> get importedFiles {
     return _importedFiles ??= _unlinked2!.imports.map((directive) {
       var uri = _selectRelativeUri(directive);
+      if (uri == null) {
+        return null;
+      }
       return _fileForRelativeUri(uri).map(
         (file) {
           file?.referencingFiles.add(this);
@@ -452,16 +470,6 @@
     return other is FileState && other.uri == uri;
   }
 
-  /// Collect all files that are transitively referenced by this file via
-  /// imports, exports, and parts.
-  void collectAllReferencedFiles(Set<String> referencedFiles) {
-    for (final file in directReferencedFiles) {
-      if (referencedFiles.add(file.path)) {
-        file.collectAllReferencedFiles(referencedFiles);
-      }
-    }
-  }
-
   /// Return a new parsed unresolved [CompilationUnit].
   CompilationUnitImpl parse([AnalysisErrorListener? errorListener]) {
     errorListener ??= AnalysisErrorListener.NULL_LISTENER;
@@ -590,10 +598,6 @@
   Either2<FileState?, ExternalLibrary> _fileForRelativeUri(
     String relativeUri,
   ) {
-    if (relativeUri.isEmpty) {
-      return Either2.t1(null);
-    }
-
     Uri absoluteUri;
     try {
       absoluteUri = resolveRelativeUri(uri, Uri.parse(relativeUri));
@@ -661,7 +665,10 @@
 
     var paths = <String>{};
 
-    void addRelativeUri(String relativeUriStr) {
+    void addRelativeUri(String? relativeUriStr) {
+      if (relativeUriStr == null) {
+        return;
+      }
       final Uri absoluteUri;
       try {
         final relativeUri = Uri.parse(relativeUriStr);
@@ -689,7 +696,7 @@
   }
 
   /// TODO(scheglov) move to _fsState?
-  String _selectRelativeUri(UnlinkedNamespaceDirective directive) {
+  String? _selectRelativeUri(UnlinkedNamespaceDirective directive) {
     for (var configuration in directive.configurations) {
       var name = configuration.name;
       var value = configuration.value;
@@ -711,12 +718,14 @@
     final partOfNameDirective = unlinked2.partOfNameDirective;
     final partOfUriDirective = unlinked2.partOfUriDirective;
     if (libraryAugmentationDirective != null) {
-      final uri = libraryAugmentationDirective.uri;
+      final uriStr = libraryAugmentationDirective.uri;
       // TODO(scheglov) This could be a useful method of `Either`.
-      final uriFile = _fileForRelativeUri(uri).map(
-        (file) => file,
-        (_) => null,
-      );
+      final uriFile = uriStr != null
+          ? _fileForRelativeUri(uriStr).map(
+              (file) => file,
+              (_) => null,
+            )
+          : null;
       if (uriFile != null) {
         _kind = AugmentationKnownFileStateKind(
           file: this,
@@ -740,11 +749,13 @@
         directive: partOfNameDirective,
       );
     } else if (partOfUriDirective != null) {
-      final uri = partOfUriDirective.uri;
-      final uriFile = _fileForRelativeUri(uri).map(
-        (file) => file,
-        (_) => null,
-      );
+      final uriStr = partOfUriDirective.uri;
+      final uriFile = uriStr != null
+          ? _fileForRelativeUri(uriStr).map(
+              (file) => file,
+              (_) => null,
+            )
+          : null;
       if (uriFile != null) {
         _kind = PartOfUriKnownFileStateKind(
           file: this,
@@ -787,7 +798,7 @@
         if (directive.augmentKeyword != null) {
           augmentations.add(
             UnlinkedImportAugmentationDirective(
-              uri: directive.uri.stringValue ?? '',
+              uri: directive.uri.stringValue,
             ),
           );
         } else {
@@ -816,7 +827,7 @@
       } else if (directive is PartDirective) {
         parts.add(
           UnlinkedPartDirective(
-            uri: directive.uri.stringValue ?? '',
+            uri: directive.uri.stringValue,
           ),
         );
       } else if (directive is PartOfDirective) {
@@ -939,10 +950,10 @@
         return UnlinkedNamespaceDirectiveConfiguration(
           name: name,
           value: value,
-          uri: configuration.uri.stringValue ?? '',
+          uri: configuration.uri.stringValue,
         );
       }).toList(),
-      uri: directive.uri.stringValue ?? '',
+      uri: directive.uri.stringValue,
     );
   }
 }
@@ -1309,24 +1320,23 @@
   }
 
   /// Computes the set of [FileState]'s used/not used to analyze the given
-  /// [files]. Removes the [FileState]'s of the files not used for analysis from
+  /// [paths]. Removes the [FileState]'s of the files not used for analysis from
   /// the cache. Returns the set of unused [FileState]'s.
-  Set<FileState> removeUnusedFiles(List<String> files) {
-    var allReferenced = <String>{};
-    for (var path in files) {
-      allReferenced.add(path);
-      _pathToFile[path]?.collectAllReferencedFiles(allReferenced);
+  Set<FileState> removeUnusedFiles(List<String> paths) {
+    final referenced = <FileState>{};
+    for (final path in paths) {
+      final library = _pathToFile[path]?.kind.library;
+      library?.collectTransitive(referenced);
     }
 
-    var unusedPaths = _pathToFile.keys.toSet();
-    unusedPaths.removeAll(allReferenced);
-
-    var removedFiles = <FileState>{};
-    for (var path in unusedPaths) {
-      changeFile(path, removedFiles);
+    final removed = <FileState>{};
+    for (final file in _pathToFile.values.toList()) {
+      if (!referenced.contains(file)) {
+        changeFile(file.path, removed);
+      }
     }
 
-    return removedFiles;
+    return removed;
   }
 
   /// Clear all [FileState] data - all maps from path or URI, etc.
@@ -1457,7 +1467,7 @@
   Source? get importedSource => null;
 }
 
-/// [PartDirectiveState] that has a valid URI that references a file.
+/// [ImportAugmentationWithUri] that has a valid URI that references a file.
 class ImportAugmentationDirectiveWithFile
     extends ImportAugmentationDirectiveState {
   final LibraryOrAugmentationFileKind container;
@@ -1490,6 +1500,16 @@
   }
 }
 
+/// [ImportAugmentationDirectiveState] that has a valid URI.
+class ImportAugmentationWithUri extends ImportAugmentationDirectiveState {
+  final String uriStr;
+
+  ImportAugmentationWithUri({
+    required super.directive,
+    required this.uriStr,
+  });
+}
+
 /// Information about a single `import` directive.
 class ImportDirectiveState extends DirectiveState {
   final UnlinkedNamespaceDirective directive;
@@ -1511,8 +1531,8 @@
   bool get isSyntheticDartCoreImport => directive.isSyntheticDartCoreImport;
 }
 
-/// [ImportDirectiveState] that has a valid URI that references a file.
-class ImportDirectiveWithFile extends ImportDirectiveState {
+/// [ImportDirectiveWithUri] that has a valid URI that references a file.
+class ImportDirectiveWithFile extends ImportDirectiveWithUri {
   final LibraryOrAugmentationFileKind container;
   final FileState importedFile;
 
@@ -1520,6 +1540,7 @@
     required this.container,
     required super.directive,
     required this.importedFile,
+    required super.selectedUriStr,
   }) {
     importedFile.referencingFiles.add(container.file);
   }
@@ -1550,14 +1571,15 @@
   }
 }
 
-/// [ImportDirectiveState] with a URI that resolves to [InSummarySource].
-class ImportDirectiveWithInSummarySource extends ImportDirectiveState {
+/// [ImportDirectiveWithUri] with a URI that resolves to [InSummarySource].
+class ImportDirectiveWithInSummarySource extends ImportDirectiveWithUri {
   @override
   final InSummarySource importedSource;
 
   ImportDirectiveWithInSummarySource({
     required super.directive,
     required this.importedSource,
+    required super.selectedUriStr,
   });
 
   @override
@@ -1570,6 +1592,16 @@
   }
 }
 
+/// [ImportDirectiveState] that has a valid URI.
+class ImportDirectiveWithUri extends ImportDirectiveState {
+  final String selectedUriStr;
+
+  ImportDirectiveWithUri({
+    required super.directive,
+    required this.selectedUriStr,
+  });
+}
+
 class LibraryFileStateKind extends LibraryOrAugmentationFileKind {
   /// The name of the library from the `library` directive.
   /// Or `null` if no `library` directive.
@@ -1626,32 +1658,53 @@
 
   List<PartDirectiveState> get parts {
     return _parts ??= file.unlinked2.parts.map((directive) {
-      return file._fileForRelativeUri(directive.uri).map(
-        (refFile) {
-          if (refFile != null) {
-            return PartDirectiveWithFile(
+      final uriStr = directive.uri;
+      if (uriStr != null) {
+        return file._fileForRelativeUri(uriStr).map(
+          (refFile) {
+            if (refFile != null) {
+              return PartDirectiveWithFile(
+                library: this,
+                directive: directive,
+                includedFile: refFile,
+                uriStr: uriStr,
+              );
+            } else {
+              return PartDirectiveWithUri(
+                library: this,
+                directive: directive,
+                uriStr: uriStr,
+              );
+            }
+          },
+          (externalLibrary) {
+            return PartDirectiveWithUri(
               library: this,
               directive: directive,
-              includedFile: refFile,
+              uriStr: uriStr,
             );
-          } else {
-            return PartDirectiveState(
-              library: this,
-              directive: directive,
-            );
-          }
-        },
-        (externalLibrary) {
-          return PartDirectiveState(
-            library: this,
-            directive: directive,
-          );
-        },
-      );
+          },
+        );
+      } else {
+        return PartDirectiveState(
+          library: this,
+          directive: directive,
+        );
+      }
     }).toList();
   }
 
   @override
+  void collectTransitive(Set<FileState> files) {
+    super.collectTransitive(files);
+    for (final part in parts) {
+      if (part is PartDirectiveWithFile) {
+        files.add(part.includedFile);
+      }
+    }
+  }
+
+  @override
   void discoverReferencedFiles() {
     super.discoverReferencedFiles();
     parts;
@@ -1697,83 +1750,132 @@
 
   List<ImportAugmentationDirectiveState> get augmentations {
     return _augmentations ??= file.unlinked2.augmentations.map((directive) {
-      return file._fileForRelativeUri(directive.uri).map(
-        (refFile) {
-          if (refFile != null) {
-            return ImportAugmentationDirectiveWithFile(
-              container: this,
+      final uriStr = directive.uri;
+      if (uriStr != null) {
+        return file._fileForRelativeUri(uriStr).map(
+          (refFile) {
+            if (refFile != null) {
+              return ImportAugmentationDirectiveWithFile(
+                container: this,
+                directive: directive,
+                importedFile: refFile,
+              );
+            } else {
+              return ImportAugmentationWithUri(
+                directive: directive,
+                uriStr: uriStr,
+              );
+            }
+          },
+          (externalLibrary) {
+            return ImportAugmentationWithUri(
               directive: directive,
-              importedFile: refFile,
+              uriStr: uriStr,
             );
-          } else {
-            return ImportAugmentationDirectiveState(
-              directive: directive,
-            );
-          }
-        },
-        (externalLibrary) {
-          return ImportAugmentationDirectiveState(
-            directive: directive,
-          );
-        },
-      );
+          },
+        );
+      } else {
+        return ImportAugmentationDirectiveState(
+          directive: directive,
+        );
+      }
     }).toList();
   }
 
   List<ExportDirectiveState> get exports {
     return _exports ??= file.unlinked2.exports.map((directive) {
       final uriStr = file._selectRelativeUri(directive);
-      return file._fileForRelativeUri(uriStr).map(
-        (refFile) {
-          if (refFile != null) {
-            return ExportDirectiveWithFile(
-              container: this,
+      if (uriStr != null) {
+        return file._fileForRelativeUri(uriStr).map(
+          (refFile) {
+            if (refFile != null) {
+              return ExportDirectiveWithFile(
+                container: this,
+                directive: directive,
+                exportedFile: refFile,
+                selectedUriStr: uriStr,
+              );
+            } else {
+              return ExportDirectiveWithUri(
+                directive: directive,
+                selectedUriStr: uriStr,
+              );
+            }
+          },
+          (externalLibrary) {
+            return ExportDirectiveWithInSummarySource(
               directive: directive,
-              exportedFile: refFile,
+              exportedSource: externalLibrary.source,
+              selectedUriStr: uriStr,
             );
-          } else {
-            return ExportDirectiveState(
-              directive: directive,
-            );
-          }
-        },
-        (externalLibrary) {
-          return ExportDirectiveWithInSummarySource(
-            directive: directive,
-            exportedSource: externalLibrary.source,
-          );
-        },
-      );
+          },
+        );
+      } else {
+        return ExportDirectiveState(
+          directive: directive,
+        );
+      }
     }).toList();
   }
 
   List<ImportDirectiveState> get imports {
     return _imports ??= file.unlinked2.imports.map((directive) {
       final uriStr = file._selectRelativeUri(directive);
-      return file._fileForRelativeUri(uriStr).map(
-        (refFile) {
-          if (refFile != null) {
-            return ImportDirectiveWithFile(
-              container: this,
+      if (uriStr != null) {
+        return file._fileForRelativeUri(uriStr).map(
+          (refFile) {
+            if (refFile != null) {
+              return ImportDirectiveWithFile(
+                container: this,
+                directive: directive,
+                importedFile: refFile,
+                selectedUriStr: uriStr,
+              );
+            } else {
+              return ImportDirectiveWithUri(
+                directive: directive,
+                selectedUriStr: uriStr,
+              );
+            }
+          },
+          (externalLibrary) {
+            return ImportDirectiveWithInSummarySource(
               directive: directive,
-              importedFile: refFile,
+              importedSource: externalLibrary.source,
+              selectedUriStr: uriStr,
             );
-          } else {
-            return ImportDirectiveState(
-              directive: directive,
-            );
-          }
-        },
-        (externalLibrary) {
-          return ImportDirectiveWithInSummarySource(
-            directive: directive,
-            importedSource: externalLibrary.source,
-          );
-        },
-      );
+          },
+        );
+      } else {
+        return ImportDirectiveState(
+          directive: directive,
+        );
+      }
     }).toList();
   }
 
+  /// Collect files that are transitively referenced by this library.
+  @mustCallSuper
+  void collectTransitive(Set<FileState> files) {
+    if (files.add(file)) {
+      for (final augmentation in augmentations) {
+        if (augmentation is ImportAugmentationDirectiveWithFile) {
+          augmentation.importedAugmentation?.collectTransitive(files);
+        }
+      }
+      for (final export in exports) {
+        if (export is ExportDirectiveWithFile) {
+          export.exportedLibrary?.collectTransitive(files);
+        }
+      }
+      for (final import in imports) {
+        if (import is ImportDirectiveWithFile) {
+          import.importedLibrary?.collectTransitive(files);
+        }
+      }
+    }
+  }
+
   /// Directives are usually pulled lazily (so that we can parse a file
   /// without pulling all its transitive references), but when we output
   /// textual dumps we want to check that we reference only objects that
@@ -1826,13 +1928,14 @@
   Source? get includedSource => null;
 }
 
-/// [PartDirectiveState] that has a valid URI that references a file.
-class PartDirectiveWithFile extends PartDirectiveState {
+/// [PartDirectiveWithUri] that has a valid URI that references a file.
+class PartDirectiveWithFile extends PartDirectiveWithUri {
   final FileState includedFile;
 
   PartDirectiveWithFile({
     required super.library,
     required super.directive,
+    required super.uriStr,
     required this.includedFile,
   }) {
     includedFile.referencingFiles.add(library.file);
@@ -1857,6 +1960,17 @@
   }
 }
 
+/// [PartDirectiveState] that has a valid URI.
+class PartDirectiveWithUri extends PartDirectiveState {
+  final String uriStr;
+
+  PartDirectiveWithUri({
+    required super.library,
+    required super.directive,
+    required this.uriStr,
+  });
+}
+
 /// The file has `part of` directive.
 abstract class PartFileStateKind extends FileStateKind {
   PartFileStateKind({
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index a81f6e1..a584d14 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -618,13 +618,15 @@
             directive.prefix?.staticElement = importElement.prefix;
             final importDirectiveState = _library.imports[index];
             // TODO(scheglov) rewrite
-            if (importDirectiveState.importedSource != null) {
-              if (importDirectiveState.importedLibrarySource == null) {
-                libraryErrorReporter.reportErrorForNode(
-                  CompileTimeErrorCode.IMPORT_OF_NON_LIBRARY,
-                  directive.uri,
-                  [importDirectiveState.directive.uri],
-                );
+            if (importDirectiveState is ImportDirectiveWithUri) {
+              if (importDirectiveState.importedSource != null) {
+                if (importDirectiveState.importedLibrarySource == null) {
+                  libraryErrorReporter.reportErrorForNode(
+                    CompileTimeErrorCode.IMPORT_OF_NON_LIBRARY,
+                    directive.uri,
+                    [importDirectiveState.selectedUriStr],
+                  );
+                }
               }
             }
           }
@@ -637,13 +639,15 @@
             directive.element = exportElement;
             final exportDirectiveState = _library.exports[index];
             // TODO(scheglov) rewrite
-            if (exportDirectiveState.exportedSource != null) {
-              if (exportDirectiveState.exportedLibrarySource == null) {
-                libraryErrorReporter.reportErrorForNode(
-                  CompileTimeErrorCode.EXPORT_OF_NON_LIBRARY,
-                  directive.uri,
-                  [exportDirectiveState.directive.uri],
-                );
+            if (exportDirectiveState is ExportDirectiveWithUri) {
+              if (exportDirectiveState.exportedSource != null) {
+                if (exportDirectiveState.exportedLibrarySource == null) {
+                  libraryErrorReporter.reportErrorForNode(
+                    CompileTimeErrorCode.EXPORT_OF_NON_LIBRARY,
+                    directive.uri,
+                    [exportDirectiveState.selectedUriStr],
+                  );
+                }
               }
             }
           }
diff --git a/pkg/analyzer/lib/src/dart/analysis/unlinked_data.dart b/pkg/analyzer/lib/src/dart/analysis/unlinked_data.dart
index 41dc29c..0f37cef 100644
--- a/pkg/analyzer/lib/src/dart/analysis/unlinked_data.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/unlinked_data.dart
@@ -91,7 +91,7 @@
 }
 
 class UnlinkedImportAugmentationDirective {
-  final String uri;
+  final String? uri;
 
   UnlinkedImportAugmentationDirective({
     required this.uri,
@@ -101,17 +101,17 @@
     SummaryDataReader reader,
   ) {
     return UnlinkedImportAugmentationDirective(
-      uri: reader.readStringUtf8(),
+      uri: reader.readOptionalStringUtf8(),
     );
   }
 
   void write(BufferedSink sink) {
-    sink.writeStringUtf8(uri);
+    sink.writeOptionalStringUtf8(uri);
   }
 }
 
 class UnlinkedLibraryAugmentationDirective {
-  final String uri;
+  final String? uri;
   final UnlinkedSourceRange uriRange;
 
   UnlinkedLibraryAugmentationDirective({
@@ -123,13 +123,13 @@
     SummaryDataReader reader,
   ) {
     return UnlinkedLibraryAugmentationDirective(
-      uri: reader.readStringUtf8(),
+      uri: reader.readOptionalStringUtf8(),
       uriRange: UnlinkedSourceRange.read(reader),
     );
   }
 
   void write(BufferedSink sink) {
-    sink.writeStringUtf8(uri);
+    sink.writeOptionalStringUtf8(uri);
     uriRange.write(sink);
   }
 }
@@ -163,7 +163,7 @@
 
   /// The URI referenced by this directive, nad used by default when none
   /// of the [configurations] matches.
-  final String uri;
+  final String? uri;
 
   UnlinkedNamespaceDirective({
     required this.configurations,
@@ -176,7 +176,7 @@
       configurations: reader.readTypedList(
         () => UnlinkedNamespaceDirectiveConfiguration.read(reader),
       ),
-      uri: reader.readStringUtf8(),
+      uri: reader.readOptionalStringUtf8(),
       isSyntheticDartCoreImport: reader.readBool(),
     );
   }
@@ -188,7 +188,7 @@
         x.write(sink);
       },
     );
-    sink.writeStringUtf8(uri);
+    sink.writeOptionalStringUtf8(uri);
     sink.writeBool(isSyntheticDartCoreImport);
   }
 }
@@ -199,7 +199,7 @@
   final String name;
 
   /// The URI to be used if the condition is true.
-  final String uri;
+  final String? uri;
 
   /// The value to which the value of the declared variable will be compared,
   /// or the empty string if the condition does not include an equality test.
@@ -216,20 +216,20 @@
   ) {
     return UnlinkedNamespaceDirectiveConfiguration(
       name: reader.readStringUtf8(),
-      uri: reader.readStringUtf8(),
+      uri: reader.readOptionalStringUtf8(),
       value: reader.readStringUtf8(),
     );
   }
 
   void write(BufferedSink sink) {
     sink.writeStringUtf8(name);
-    sink.writeStringUtf8(uri);
+    sink.writeOptionalStringUtf8(uri);
     sink.writeStringUtf8(value);
   }
 }
 
 class UnlinkedPartDirective {
-  final String uri;
+  final String? uri;
 
   UnlinkedPartDirective({
     required this.uri,
@@ -239,12 +239,12 @@
     SummaryDataReader reader,
   ) {
     return UnlinkedPartDirective(
-      uri: reader.readStringUtf8(),
+      uri: reader.readOptionalStringUtf8(),
     );
   }
 
   void write(BufferedSink sink) {
-    sink.writeStringUtf8(uri);
+    sink.writeOptionalStringUtf8(uri);
   }
 }
 
@@ -273,7 +273,7 @@
 }
 
 class UnlinkedPartOfUriDirective {
-  final String uri;
+  final String? uri;
   final UnlinkedSourceRange uriRange;
 
   UnlinkedPartOfUriDirective({
@@ -285,13 +285,13 @@
     SummaryDataReader reader,
   ) {
     return UnlinkedPartOfUriDirective(
-      uri: reader.readStringUtf8(),
+      uri: reader.readOptionalStringUtf8(),
       uriRange: UnlinkedSourceRange.read(reader),
     );
   }
 
   void write(BufferedSink sink) {
-    sink.writeStringUtf8(uri);
+    sink.writeOptionalStringUtf8(uri);
     uriRange.write(sink);
   }
 }
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 1c43336..3580fac 100644
--- a/pkg/analyzer/test/src/dart/analysis/analyzer_state_printer.dart
+++ b/pkg/analyzer/test/src/dart/analysis/analyzer_state_printer.dart
@@ -110,10 +110,11 @@
             sink.write('notAugmentation ${idProvider.fileState(file)}');
           }
           sink.writeln();
+        } else if (import is ImportAugmentationWithUri) {
+          final uriStr = _stringOfUriStr(import.uriStr);
+          _writelnWithIndent('uri: $uriStr');
         } else {
-          sink.write(_indent);
-          sink.write('uri: ${_stringOfUriStr(import.directive.uri)}');
-          sink.writeln();
+          _writelnWithIndent('noUri');
         }
       },
     );
@@ -202,10 +203,11 @@
             sink.write(' notLibrary');
           }
           sink.writeln();
+        } else if (export is ExportDirectiveWithUri) {
+          final uriStr = _stringOfUriStr(export.selectedUriStr);
+          _writelnWithIndent('uri: $uriStr');
         } else {
-          sink.write(_indent);
-          sink.write('uri: ${_stringOfUriStr(export.directive.uri)}');
-          sink.writeln();
+          _writelnWithIndent('noUri');
         }
       },
     );
@@ -252,13 +254,15 @@
             sink.write(' synthetic');
           }
           sink.writeln();
-        } else {
+        } else if (import is ImportDirectiveWithUri) {
           sink.write(_indent);
-          sink.write('uri: ${_stringOfUriStr(import.directive.uri)}');
+          sink.write('uri: ${_stringOfUriStr(import.selectedUriStr)}');
           if (import.isSyntheticDartCoreImport) {
             sink.write(' synthetic');
           }
           sink.writeln();
+        } else {
+          _writelnWithIndent('noUri');
         }
       },
     );
@@ -548,10 +552,11 @@
           sink.write('notPart ${idProvider.fileState(file)}');
         }
         sink.writeln();
+      } else if (part is PartDirectiveWithUri) {
+        final uriStr = _stringOfUriStr(part.uriStr);
+        _writelnWithIndent('uri: $uriStr');
       } else {
-        sink.write(_indent);
-        sink.write('uri: ${_stringOfUriStr(part.directive.uri)}');
-        sink.writeln();
+        _writelnWithIndent('noUri');
       }
     });
   }
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 ef21d7a..1336d79 100644
--- a/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
@@ -1470,7 +1470,36 @@
 ''');
   }
 
-  test_newFile_library_augmentations_invalidRelativeUri() async {
+  test_newFile_library_augmentations_emptyUri() {
+    final a = newFile('$testPackageLibPath/a.dart', r'''
+import augment '';
+''');
+
+    fileStateFor(a);
+
+    assertDriverStateString(testFile, r'''
+files
+  /home/test/lib/a.dart
+    uri: package:test/a.dart
+    current
+      id: file_0
+      kind: library_0
+        imports
+          library_1 dart:core synthetic
+        augmentations
+          notAugmentation file_0
+        cycle_0
+          dependencies: dart:core
+          libraries: library_0
+          apiSignature_0
+      referencingFiles: file_0
+      unlinkedKey: k00
+libraryCycles
+elementFactory
+''');
+  }
+
+  test_newFile_library_augmentations_invalidUri_cannotParse() async {
     final a = newFile('$testPackageLibPath/a.dart', r'''
 import augment 'da:';
 ''');
@@ -1498,9 +1527,9 @@
 ''');
   }
 
-  test_newFile_library_augmentations_invalidRelativeUri_empty() {
+  test_newFile_library_augmentations_invalidUri_interpolation() async {
     final a = newFile('$testPackageLibPath/a.dart', r'''
-import augment '';
+import augment '${'foo.dart'}';
 ''');
 
     fileStateFor(a);
@@ -1515,7 +1544,7 @@
         imports
           library_1 dart:core synthetic
         augmentations
-          uri: ''
+          noUri
         cycle_0
           dependencies: dart:core
           libraries: library_0
@@ -1623,6 +1652,35 @@
 ''');
   }
 
+  test_newFile_library_exports_emptyUri() {
+    final a = newFile('$testPackageLibPath/a.dart', r'''
+export '';
+''');
+
+    fileStateFor(a);
+
+    assertDriverStateString(testFile, r'''
+files
+  /home/test/lib/a.dart
+    uri: package:test/a.dart
+    current
+      id: file_0
+      kind: library_0
+        imports
+          library_1 dart:core synthetic
+        exports
+          library_0
+        cycle_0
+          dependencies: dart:core
+          libraries: library_0
+          apiSignature_0
+      referencingFiles: file_0
+      unlinkedKey: k00
+libraryCycles
+elementFactory
+''');
+  }
+
   test_newFile_library_exports_inSummary_library() async {
     // Prepare a bundle where `package:foo/foo.dart` is a library.
     final librarySummaryFiles = <File>[];
@@ -1801,7 +1859,7 @@
 ''');
   }
 
-  test_newFile_library_exports_invalidRelativeUri() async {
+  test_newFile_library_exports_invalidUri_cannotParse() async {
     final a = newFile('$testPackageLibPath/a.dart', r'''
 export 'net:';
 ''');
@@ -1829,9 +1887,9 @@
 ''');
   }
 
-  test_newFile_library_exports_invalidRelativeUri_empty() {
+  test_newFile_library_exports_invalidUri_interpolation() async {
     final a = newFile('$testPackageLibPath/a.dart', r'''
-export '';
+export '${'foo.dart'}';
 ''');
 
     fileStateFor(a);
@@ -1846,7 +1904,7 @@
         imports
           library_1 dart:core synthetic
         exports
-          uri: ''
+          noUri
         cycle_0
           dependencies: dart:core
           libraries: library_0
@@ -2008,7 +2066,35 @@
 ''');
   }
 
-  test_newFile_library_imports_invalidRelativeUri() async {
+  test_newFile_library_imports_emptyUri() {
+    final a = newFile('$testPackageLibPath/a.dart', r'''
+import '';
+''');
+
+    fileStateFor(a);
+
+    assertDriverStateString(testFile, r'''
+files
+  /home/test/lib/a.dart
+    uri: package:test/a.dart
+    current
+      id: file_0
+      kind: library_0
+        imports
+          library_0
+          library_1 dart:core synthetic
+        cycle_0
+          dependencies: dart:core
+          libraries: library_0
+          apiSignature_0
+      referencingFiles: file_0
+      unlinkedKey: k00
+libraryCycles
+elementFactory
+''');
+  }
+
+  test_newFile_library_imports_invalidUri_cannotParse() async {
     final a = newFile('$testPackageLibPath/a.dart', r'''
 import 'da:';
 ''');
@@ -2035,9 +2121,9 @@
 ''');
   }
 
-  test_newFile_library_imports_invalidRelativeUri_empty() {
+  test_newFile_library_imports_invalidUri_interpolation() async {
     final a = newFile('$testPackageLibPath/a.dart', r'''
-import '';
+import '${'foo.dart'}';
 ''');
 
     fileStateFor(a);
@@ -2050,7 +2136,7 @@
       id: file_0
       kind: library_0
         imports
-          uri: ''
+          noUri
           library_1 dart:core synthetic
         cycle_0
           dependencies: dart:core
@@ -2519,7 +2605,36 @@
 ''');
   }
 
-  test_newFile_library_parts_invalidRelativeUri() {
+  test_newFile_library_parts_emptyUri() {
+    final a = newFile('$testPackageLibPath/a.dart', r'''
+part '';
+''');
+
+    fileStateFor(a);
+
+    assertDriverStateString(testFile, r'''
+files
+  /home/test/lib/a.dart
+    uri: package:test/a.dart
+    current
+      id: file_0
+      kind: library_0
+        imports
+          library_1 dart:core synthetic
+        parts
+          notPart file_0
+        cycle_0
+          dependencies: dart:core
+          libraries: library_0
+          apiSignature_0
+      referencingFiles: file_0
+      unlinkedKey: k00
+libraryCycles
+elementFactory
+''');
+  }
+
+  test_newFile_library_parts_invalidUri_cannotParse() {
     final a = newFile('$testPackageLibPath/a.dart', r'''
 part 'da:';
 ''');
@@ -2547,9 +2662,9 @@
 ''');
   }
 
-  test_newFile_library_parts_invalidRelativeUri_empty() {
+  test_newFile_library_parts_invalidUri_interpolation() {
     final a = newFile('$testPackageLibPath/a.dart', r'''
-part '';
+part '${'foo.dart'}';
 ''');
 
     fileStateFor(a);
@@ -2564,7 +2679,7 @@
         imports
           library_1 dart:core synthetic
         parts
-          uri: ''
+          noUri
         cycle_0
           dependencies: dart:core
           libraries: library_0
diff --git a/pkg/analyzer/test/src/summary/elements_test.dart b/pkg/analyzer/test/src/summary/elements_test.dart
index dc9ab8f..2c410d6 100644
--- a/pkg/analyzer/test/src/summary/elements_test.dart
+++ b/pkg/analyzer/test/src/summary/elements_test.dart
@@ -32861,6 +32861,7 @@
 ''');
   }
 
+  /// TODO(scheglov) The part should disappear after finishing [PartElement].
   test_part_emptyUri() async {
     var library = await buildLibrary(r'''
 part '';
@@ -32873,6 +32874,12 @@
       class B @15
         constructors
           synthetic @-1
+  parts
+
+      classes
+        class B @15
+          constructors
+            synthetic @-1
 ''');
   }
 
diff --git a/pkg/test_runner/lib/src/command.dart b/pkg/test_runner/lib/src/command.dart
index 8db8dfb..07241ef 100644
--- a/pkg/test_runner/lib/src/command.dart
+++ b/pkg/test_runner/lib/src/command.dart
@@ -267,6 +267,18 @@
             index: index);
 
   @override
+  Dart2jsCompilationCommand indexedCopy(int index) => Dart2jsCompilationCommand(
+      outputFile,
+      _bootstrapDependencies,
+      executable,
+      arguments,
+      environmentOverrides,
+      useSdk: useSdk,
+      alwaysCompile: _alwaysCompile,
+      workingDirectory: workingDirectory,
+      index: index);
+
+  @override
   CommandOutput createOutput(int exitCode, bool timedOut, List<int> stdout,
       List<int> stderr, Duration time, bool compilationSkipped,
       [int? pid = 0]) {
@@ -322,6 +334,15 @@
             index: index);
 
   @override
+  DevCompilerCompilationCommand indexedCopy(int index) =>
+      DevCompilerCompilationCommand(outputFile, _bootstrapDependencies,
+          executable, arguments, environmentOverrides,
+          compilerPath: compilerPath,
+          alwaysCompile: _alwaysCompile,
+          workingDirectory: workingDirectory,
+          index: index);
+
+  @override
   CommandOutput createOutput(int exitCode, bool timedOut, List<int> stdout,
       List<int> stderr, Duration time, bool compilationSkipped,
       [int pid = 0]) {
diff --git a/runtime/bin/gen_snapshot.cc b/runtime/bin/gen_snapshot.cc
index b3c5cbb..6a24139 100644
--- a/runtime/bin/gen_snapshot.cc
+++ b/runtime/bin/gen_snapshot.cc
@@ -957,8 +957,3 @@
 int main(int argc, char** argv) {
   return dart::bin::main(argc, argv);
 }
-
-// TODO(riscv): Why is this missing from libc?
-#if defined(__riscv)
-char __libc_single_threaded = 0;
-#endif
diff --git a/runtime/bin/main.cc b/runtime/bin/main.cc
index 2d8b531..3b07ebd 100644
--- a/runtime/bin/main.cc
+++ b/runtime/bin/main.cc
@@ -1442,8 +1442,3 @@
   dart::bin::main(argc, argv);
   UNREACHABLE();
 }
-
-// TODO(riscv): Why is this missing from libc?
-#if defined(__riscv)
-char __libc_single_threaded = 0;
-#endif
diff --git a/runtime/bin/run_vm_tests.cc b/runtime/bin/run_vm_tests.cc
index 994f542..db8c47d 100644
--- a/runtime/bin/run_vm_tests.cc
+++ b/runtime/bin/run_vm_tests.cc
@@ -426,8 +426,3 @@
 int main(int argc, const char** argv) {
   dart::bin::Platform::Exit(dart::Main(argc, argv));
 }
-
-// TODO(riscv): Why is this missing from libc?
-#if defined(__riscv)
-char __libc_single_threaded = 0;
-#endif
diff --git a/runtime/lib/async.cc b/runtime/lib/async.cc
index 5a11f34..f2e539b 100644
--- a/runtime/lib/async.cc
+++ b/runtime/lib/async.cc
@@ -22,4 +22,44 @@
   return Object::null();
 }
 
+// Instantiate generic [closure] using the type argument T
+// corresponding to Future<T> in the given [future] instance
+// (which may extend or implement Future).
+DEFINE_NATIVE_ENTRY(SuspendState_instantiateClosureWithFutureTypeArgument,
+                    0,
+                    2) {
+  GET_NON_NULL_NATIVE_ARGUMENT(Closure, closure, arguments->NativeArgAt(0));
+  GET_NON_NULL_NATIVE_ARGUMENT(Instance, future, arguments->NativeArgAt(1));
+  IsolateGroup* isolate_group = thread->isolate_group();
+
+  const auto& future_class =
+      Class::Handle(zone, isolate_group->object_store()->future_class());
+  ASSERT(future_class.NumTypeArguments() == 1);
+
+  const auto& cls = Class::Handle(zone, future.clazz());
+  auto& type =
+      AbstractType::Handle(zone, cls.GetInstantiationOf(zone, future_class));
+  ASSERT(!type.IsNull());
+  if (!type.IsInstantiated()) {
+    const auto& instance_type_args =
+        TypeArguments::Handle(zone, future.GetTypeArguments());
+    type =
+        type.InstantiateFrom(instance_type_args, Object::null_type_arguments(),
+                             kNoneFree, Heap::kNew);
+  }
+  auto& type_args = TypeArguments::Handle(zone, type.arguments());
+  if (type_args.Length() != 1) {
+    // Create a new TypeArguments vector of length 1.
+    type = type_args.TypeAtNullSafe(0);
+    type_args = TypeArguments::New(1);
+    type_args.SetTypeAt(0, type);
+  }
+  type_args = type_args.Canonicalize(thread, nullptr);
+
+  ASSERT(closure.delayed_type_arguments() ==
+         Object::empty_type_arguments().ptr());
+  closure.set_delayed_type_arguments(type_args);
+  return closure.ptr();
+}
+
 }  // namespace dart
diff --git a/runtime/vm/app_snapshot.cc b/runtime/vm/app_snapshot.cc
index 67d6ca8..dcf35fd 100644
--- a/runtime/vm/app_snapshot.cc
+++ b/runtime/vm/app_snapshot.cc
@@ -1988,14 +1988,12 @@
         // indirectly by passing the field to the runtime. A const closure
         // is a call target because its function may be called indirectly
         // via a closure call.
-        if (!only_call_targets || target->IsCode() || target->IsFunction() ||
-            target->IsField() || target->IsClosure()) {
+        intptr_t cid = target->GetClassIdMayBeSmi();
+        if (!only_call_targets || (cid == kCodeCid) || (cid == kFunctionCid) ||
+            (cid == kFieldCid) || (cid == kClosureCid)) {
           s->Push(target);
-        } else {
-          intptr_t cid = target->GetClassIdMayBeSmi();
-          if (cid >= kNumPredefinedCids) {
-            s->Push(s->isolate_group()->class_table()->At(cid));
-          }
+        } else if (cid >= kNumPredefinedCids) {
+          s->Push(s->isolate_group()->class_table()->At(cid));
         }
       }
     }
diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h
index c2b2ddc..dbbaa95 100644
--- a/runtime/vm/bootstrap_natives.h
+++ b/runtime/vm/bootstrap_natives.h
@@ -66,6 +66,7 @@
   V(SendPortImpl_sendInternal_, 2)                                             \
   V(Smi_bitNegate, 1)                                                          \
   V(Smi_bitLength, 1)                                                          \
+  V(SuspendState_instantiateClosureWithFutureTypeArgument, 2)                  \
   V(Mint_bitNegate, 1)                                                         \
   V(Mint_bitLength, 1)                                                         \
   V(Developer_debugger, 2)                                                     \
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index 0e7442a..98a84ec 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -9500,8 +9500,7 @@
     JSONArray jstream(&obj, "available");
     Timeline::PrintFlagsToJSONArray(&jstream);
     const char* js_str = js.ToCString();
-#define TIMELINE_STREAM_CHECK(name, fuchsia_name)                              \
-  EXPECT_SUBSTRING(#name, js_str);
+#define TIMELINE_STREAM_CHECK(name, ...) EXPECT_SUBSTRING(#name, js_str);
     TIMELINE_STREAM_LIST(TIMELINE_STREAM_CHECK)
 #undef TIMELINE_STREAM_CHECK
   }
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index 85cf403..af7a225 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -296,7 +296,7 @@
 #if defined(SUPPORT_TIMELINE)
 static const char* const timeline_streams_enum_names[] = {
     "all",
-#define DEFINE_NAME(name, unused) #name,
+#define DEFINE_NAME(name, ...) #name,
     TIMELINE_STREAM_LIST(DEFINE_NAME)
 #undef DEFINE_NAME
         NULL};
@@ -328,7 +328,7 @@
     return false;
   }
 
-#define SET_ENABLE_STREAM(name, unused)                                        \
+#define SET_ENABLE_STREAM(name, ...)                                           \
   Timeline::SetStream##name##Enabled(HasStream(streams, #name));
   TIMELINE_STREAM_LIST(SET_ENABLE_STREAM);
 #undef SET_ENABLE_STREAM
diff --git a/runtime/vm/timeline.cc b/runtime/vm/timeline.cc
index 90b0a23..f5140c6 100644
--- a/runtime/vm/timeline.cc
+++ b/runtime/vm/timeline.cc
@@ -202,7 +202,7 @@
   ASSERT(recorder_ != NULL);
   enabled_streams_ = GetEnabledByDefaultTimelineStreams();
 // Global overrides.
-#define TIMELINE_STREAM_FLAG_DEFAULT(name, fuchsia_name)                       \
+#define TIMELINE_STREAM_FLAG_DEFAULT(name, ...)                                \
   stream_##name##_.set_enabled(HasStream(enabled_streams_, #name));
   TIMELINE_STREAM_LIST(TIMELINE_STREAM_FLAG_DEFAULT)
 #undef TIMELINE_STREAM_FLAG_DEFAULT
@@ -218,7 +218,7 @@
 #endif
 
 // Disable global streams.
-#define TIMELINE_STREAM_DISABLE(name, fuchsia_name)                            \
+#define TIMELINE_STREAM_DISABLE(name, ...)                                     \
   Timeline::stream_##name##_.set_enabled(false);
   TIMELINE_STREAM_LIST(TIMELINE_STREAM_DISABLE)
 #undef TIMELINE_STREAM_DISABLE
@@ -265,7 +265,7 @@
 
 #ifndef PRODUCT
 void Timeline::PrintFlagsToJSONArray(JSONArray* arr) {
-#define ADD_RECORDED_STREAM_NAME(name, fuchsia_name)                           \
+#define ADD_RECORDED_STREAM_NAME(name, ...)                                    \
   if (stream_##name##_.enabled()) {                                            \
     arr->AddValue(#name);                                                      \
   }
@@ -285,13 +285,13 @@
   }
   {
     JSONArray availableStreams(&obj, "availableStreams");
-#define ADD_STREAM_NAME(name, fuchsia_name) availableStreams.AddValue(#name);
+#define ADD_STREAM_NAME(name, ...) availableStreams.AddValue(#name);
     TIMELINE_STREAM_LIST(ADD_STREAM_NAME);
 #undef ADD_STREAM_NAME
   }
   {
     JSONArray recordedStreams(&obj, "recordedStreams");
-#define ADD_RECORDED_STREAM_NAME(name, fuchsia_name)                           \
+#define ADD_RECORDED_STREAM_NAME(name, ...)                                    \
   if (stream_##name##_.enabled()) {                                            \
     recordedStreams.AddValue(#name);                                           \
   }
@@ -402,8 +402,9 @@
 MallocGrowableArray<char*>* Timeline::enabled_streams_ = NULL;
 bool Timeline::recorder_discards_clock_values_ = false;
 
-#define TIMELINE_STREAM_DEFINE(name, fuchsia_name)                             \
-  TimelineStream Timeline::stream_##name##_(#name, fuchsia_name, false);
+#define TIMELINE_STREAM_DEFINE(name, fuchsia_name, static_labels)              \
+  TimelineStream Timeline::stream_##name##_(#name, fuchsia_name,               \
+                                            static_labels, false);
 TIMELINE_STREAM_LIST(TIMELINE_STREAM_DEFINE)
 #undef TIMELINE_STREAM_DEFINE
 
@@ -774,6 +775,7 @@
 
 TimelineStream::TimelineStream(const char* name,
                                const char* fuchsia_name,
+                               bool has_static_labels,
                                bool enabled)
     : name_(name),
       fuchsia_name_(fuchsia_name),
@@ -786,6 +788,7 @@
 #if defined(DART_HOST_OS_MACOS)
   if (__builtin_available(iOS 12.0, macOS 10.14, *)) {
     macos_log_ = os_log_create("Dart", name);
+    has_static_labels_ = has_static_labels;
   }
 #endif
 }
diff --git a/runtime/vm/timeline.h b/runtime/vm/timeline.h
index 7b89e4b..8071185 100644
--- a/runtime/vm/timeline.h
+++ b/runtime/vm/timeline.h
@@ -57,23 +57,26 @@
 #define STARTUP_RECORDER_NAME "Startup"
 #define SYSTRACE_RECORDER_NAME "Systrace"
 
-// (name, fuchsia_name).
+// (name, fuchsia_name, has_static_labels).
 #define TIMELINE_STREAM_LIST(V)                                                \
-  V(API, "dart:api")                                                           \
-  V(Compiler, "dart:compiler")                                                 \
-  V(CompilerVerbose, "dart:compiler.verbose")                                  \
-  V(Dart, "dart:dart")                                                         \
-  V(Debugger, "dart:debugger")                                                 \
-  V(Embedder, "dart:embedder")                                                 \
-  V(GC, "dart:gc")                                                             \
-  V(Isolate, "dart:isolate")                                                   \
-  V(VM, "dart:vm")
+  V(API, "dart:api", true)                                                     \
+  V(Compiler, "dart:compiler", true)                                           \
+  V(CompilerVerbose, "dart:compiler.verbose", true)                            \
+  V(Dart, "dart:dart", false)                                                  \
+  V(Debugger, "dart:debugger", true)                                           \
+  V(Embedder, "dart:embedder", false)                                          \
+  V(GC, "dart:gc", true)                                                       \
+  V(Isolate, "dart:isolate", true)                                             \
+  V(VM, "dart:vm", true)
 
 // A stream of timeline events. A stream has a name and can be enabled or
 // disabled (globally and per isolate).
 class TimelineStream {
  public:
-  TimelineStream(const char* name, const char* fuchsia_name, bool enabled);
+  TimelineStream(const char* name,
+                 const char* fuchsia_name,
+                 bool static_labels,
+                 bool enabled);
 
   const char* name() const { return name_; }
   const char* fuchsia_name() const { return fuchsia_name_; }
@@ -105,7 +108,8 @@
 #if defined(DART_HOST_OS_FUCHSIA)
   trace_site_t* trace_site() { return &trace_site_; }
 #elif defined(DART_HOST_OS_MACOS)
-  os_log_t macos_log() { return macos_log_; }
+  os_log_t macos_log() const { return macos_log_; }
+  bool has_static_labels() const { return has_static_labels_; }
 #endif
 
  private:
@@ -120,6 +124,7 @@
   trace_site_t trace_site_ = {};
 #elif defined(DART_HOST_OS_MACOS)
   os_log_t macos_log_ = {};
+  bool has_static_labels_ = false;
 #endif
 };
 
@@ -196,12 +201,12 @@
   static void PrintFlagsToJSONArray(JSONArray* arr);
 #endif
 
-#define TIMELINE_STREAM_ACCESSOR(name, fuchsia_name)                           \
+#define TIMELINE_STREAM_ACCESSOR(name, ...)                                    \
   static TimelineStream* Get##name##Stream() { return &stream_##name##_; }
   TIMELINE_STREAM_LIST(TIMELINE_STREAM_ACCESSOR)
 #undef TIMELINE_STREAM_ACCESSOR
 
-#define TIMELINE_STREAM_FLAGS(name, fuchsia_name)                              \
+#define TIMELINE_STREAM_FLAGS(name, ...)                                       \
   static void SetStream##name##Enabled(bool enabled) {                         \
     stream_##name##_.set_enabled(enabled);                                     \
   }
@@ -216,7 +221,7 @@
   static MallocGrowableArray<char*>* enabled_streams_;
   static bool recorder_discards_clock_values_;
 
-#define TIMELINE_STREAM_DECLARE(name, fuchsia_name)                            \
+#define TIMELINE_STREAM_DECLARE(name, ...)                                     \
   static TimelineStream stream_##name##_;
   TIMELINE_STREAM_LIST(TIMELINE_STREAM_DECLARE)
 #undef TIMELINE_STREAM_DECLARE
diff --git a/runtime/vm/timeline_macos.cc b/runtime/vm/timeline_macos.cc
index d187617..55e0b24 100644
--- a/runtime/vm/timeline_macos.cc
+++ b/runtime/vm/timeline_macos.cc
@@ -30,39 +30,45 @@
   }
 
   const char* label = event->label();
+  bool is_static_label = event->stream_->has_static_labels();
   uint8_t _Alignas(16) buffer[64];
   buffer[0] = 0;
 
   switch (event->event_type()) {
-    case TimelineEvent::kInstant: {
-      _os_signpost_emit_with_name_impl(&__dso_handle, log, OS_SIGNPOST_EVENT,
-                                       OS_SIGNPOST_ID_EXCLUSIVE, label, "",
-                                       buffer, sizeof(buffer));
+    case TimelineEvent::kInstant:
+      if (is_static_label) {
+        _os_signpost_emit_with_name_impl(&__dso_handle, log, OS_SIGNPOST_EVENT,
+                                         OS_SIGNPOST_ID_EXCLUSIVE, label, "",
+                                         buffer, sizeof(buffer));
+      } else {
+        os_signpost_event_emit(log, OS_SIGNPOST_ID_EXCLUSIVE, "Event", "%s",
+                               label);
+      }
       break;
-    }
     case TimelineEvent::kBegin:
-    case TimelineEvent::kAsyncBegin: {
-      _os_signpost_emit_with_name_impl(&__dso_handle, log,
-                                       OS_SIGNPOST_INTERVAL_BEGIN, event->Id(),
-                                       label, "", buffer, sizeof(buffer));
+    case TimelineEvent::kAsyncBegin:
+      if (is_static_label) {
+        _os_signpost_emit_with_name_impl(
+            &__dso_handle, log, OS_SIGNPOST_INTERVAL_BEGIN, event->Id(), label,
+            "", buffer, sizeof(buffer));
+      } else {
+        os_signpost_interval_begin(log, event->Id(), "Event", "%s", label);
+      }
       break;
-    }
     case TimelineEvent::kEnd:
-    case TimelineEvent::kAsyncEnd: {
-      _os_signpost_emit_with_name_impl(&__dso_handle, log,
-                                       OS_SIGNPOST_INTERVAL_END, event->Id(),
-                                       label, "", buffer, sizeof(buffer));
+    case TimelineEvent::kAsyncEnd:
+      if (is_static_label) {
+        _os_signpost_emit_with_name_impl(&__dso_handle, log,
+                                         OS_SIGNPOST_INTERVAL_END, event->Id(),
+                                         label, "", buffer, sizeof(buffer));
+      } else {
+        os_signpost_interval_end(log, event->Id(), "Event");
+      }
       break;
-    }
-    case TimelineEvent::kCounter: {
-      const char* fmt = "%s";
-      Utils::SNPrint(reinterpret_cast<char*>(buffer), sizeof(buffer), fmt,
-                     event->arguments()[0].value);
-      _os_signpost_emit_with_name_impl(&__dso_handle, log, OS_SIGNPOST_EVENT,
-                                       OS_SIGNPOST_ID_EXCLUSIVE, label, fmt,
-                                       buffer, sizeof(buffer));
+    case TimelineEvent::kCounter:
+      os_signpost_event_emit(log, OS_SIGNPOST_ID_EXCLUSIVE, "Counter", "%s=%s",
+                             label, event->arguments()[0].value);
       break;
-    }
     default:
       break;
   }
diff --git a/runtime/vm/timeline_test.cc b/runtime/vm/timeline_test.cc
index 3c5ec04..09d226b 100644
--- a/runtime/vm/timeline_test.cc
+++ b/runtime/vm/timeline_test.cc
@@ -105,7 +105,7 @@
 
 TEST_CASE(TimelineEventIsValid) {
   // Create a test stream.
-  TimelineStream stream("testStream", "testStream", true);
+  TimelineStream stream("testStream", "testStream", false, true);
 
   TimelineEvent event;
   TimelineTestHelper::SetStream(&event, &stream);
@@ -124,7 +124,7 @@
 
 TEST_CASE(TimelineEventDuration) {
   // Create a test stream.
-  TimelineStream stream("testStream", "testStream", true);
+  TimelineStream stream("testStream", "testStream", false, true);
 
   // Create a test event.
   TimelineEvent event;
@@ -139,7 +139,7 @@
 
 TEST_CASE(TimelineEventDurationPrintJSON) {
   // Create a test stream.
-  TimelineStream stream("testStream", "testStream", true);
+  TimelineStream stream("testStream", "testStream", false, true);
 
   // Create a test event.
   TimelineEvent event;
@@ -169,7 +169,7 @@
   char buffer[kBufferLength];
 
   // Create a test stream.
-  TimelineStream stream("testStream", "testStream", true);
+  TimelineStream stream("testStream", "testStream", false, true);
 
   // Create a test event.
   TimelineEvent event;
@@ -213,7 +213,7 @@
 
 TEST_CASE(TimelineEventArguments) {
   // Create a test stream.
-  TimelineStream stream("testStream", "testStream", true);
+  TimelineStream stream("testStream", "testStream", false, true);
 
   // Create a test event.
   TimelineEvent event;
@@ -233,7 +233,7 @@
 
 TEST_CASE(TimelineEventArgumentsPrintJSON) {
   // Create a test stream.
-  TimelineStream stream("testStream", "testStream", true);
+  TimelineStream stream("testStream", "testStream", false, true);
 
   // Create a test event.
   TimelineEvent event;
@@ -296,7 +296,7 @@
   }
 
   // Create a test stream.
-  TimelineStream stream("testStream", "testStream", true);
+  TimelineStream stream("testStream", "testStream", false, true);
 
   TimelineEvent* event = NULL;
 
@@ -341,7 +341,7 @@
 }
 
 TEST_CASE(TimelineRingRecorderJSONOrder) {
-  TimelineStream stream("testStream", "testStream", true);
+  TimelineStream stream("testStream", "testStream", false, true);
 
   TimelineEventRingRecorder* recorder =
       new TimelineEventRingRecorder(TimelineEventBlock::kBlockSize * 2);
diff --git a/sdk/lib/_internal/vm/lib/async_patch.dart b/sdk/lib/_internal/vm/lib/async_patch.dart
index 50a419b..976860f 100644
--- a/sdk/lib/_internal/vm/lib/async_patch.dart
+++ b/sdk/lib/_internal/vm/lib/async_patch.dart
@@ -419,6 +419,26 @@
     zone.scheduleMicrotask(run);
   }
 
+  @pragma("vm:invisible")
+  @pragma("vm:prefer-inline")
+  void _awaitUserDefinedFuture(Future future) {
+    // Create a generic callback closure and instantiate it
+    // using the type argument of Future.
+    // This is needed to avoid unsoundness which may happen if user-defined
+    // Future.then casts callback to Function(dynamic) passes a value of
+    // incorrect type.
+    @pragma("vm:invisible")
+    dynamic typedCallback<T>(T value) {
+      return unsafeCast<dynamic Function(dynamic)>(_thenCallback)(value);
+    }
+
+    future.then(
+        unsafeCast<dynamic Function(dynamic)>(
+            _instantiateClosureWithFutureTypeArgument(typedCallback, future)),
+        onError:
+            unsafeCast<dynamic Function(Object, StackTrace)>(_errorCallback));
+  }
+
   @pragma("vm:entry-point", "call")
   @pragma("vm:invisible")
   Object? _await(Object? object) {
@@ -437,9 +457,7 @@
     } else if (object is! Future) {
       _awaitNotFuture(object);
     } else {
-      object.then(unsafeCast<dynamic Function(dynamic)>(_thenCallback),
-          onError:
-              unsafeCast<dynamic Function(Object, StackTrace)>(_errorCallback));
+      _awaitUserDefinedFuture(object);
     }
     return _functionData;
   }
@@ -612,6 +630,11 @@
   @pragma("vm:recognized", "other")
   @pragma("vm:prefer-inline")
   external _SuspendState _clone();
+
+  @pragma("vm:external-name",
+      "SuspendState_instantiateClosureWithFutureTypeArgument")
+  external static Function _instantiateClosureWithFutureTypeArgument(
+      dynamic Function<T>(T) closure, Future future);
 }
 
 class _SyncStarIterable<T> extends Iterable<T> {
diff --git a/tests/language/async/await_user_defined_future_soundness_test.dart b/tests/language/async/await_user_defined_future_soundness_test.dart
new file mode 100644
index 0000000..351d2ae
--- /dev/null
+++ b/tests/language/async/await_user_defined_future_soundness_test.dart
@@ -0,0 +1,51 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Verifies that user-define Future cannot provide a value of incorrect
+// type by casting 'onValue' callback.
+// Regression test for https://github.com/dart-lang/sdk/issues/49345.
+
+import 'dart:async';
+
+import "package:expect/expect.dart";
+
+import 'dart:async';
+
+bool checkpoint1 = false;
+bool checkpoint2 = false;
+bool checkpoint3 = false;
+bool checkpoint4 = false;
+
+Future<void> foo(Future<String> f) async {
+  checkpoint1 = true;
+  final String result = await f;
+  checkpoint3 = true;
+  print(result.runtimeType);
+}
+
+class F implements Future<String> {
+  Future<R> then<R>(FutureOr<R> Function(String) onValue, {Function? onError}) {
+    checkpoint2 = true;
+    final result = (onValue as FutureOr<R> Function(dynamic))(10);
+    checkpoint4 = true;
+    return Future.value(result);
+  }
+
+  @override
+  dynamic noSuchMethod(i) => throw 'Unimplimented';
+}
+
+void main() {
+  bool seenError = false;
+  runZoned(() {
+    foo(F());
+  }, onError: (e, st) {
+    seenError = true;
+  });
+  Expect.isTrue(checkpoint1);
+  Expect.isTrue(checkpoint2);
+  Expect.isFalse(checkpoint3);
+  Expect.isFalse(checkpoint4);
+  Expect.isTrue(seenError);
+}
diff --git a/tests/language_2/async/await_user_defined_future_soundness_test.dart b/tests/language_2/async/await_user_defined_future_soundness_test.dart
new file mode 100644
index 0000000..351cdd9
--- /dev/null
+++ b/tests/language_2/async/await_user_defined_future_soundness_test.dart
@@ -0,0 +1,53 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart = 2.9
+
+// Verifies that user-define Future cannot provide a value of incorrect
+// type by casting 'onValue' callback.
+// Regression test for https://github.com/dart-lang/sdk/issues/49345.
+
+import 'dart:async';
+
+import "package:expect/expect.dart";
+
+import 'dart:async';
+
+bool checkpoint1 = false;
+bool checkpoint2 = false;
+bool checkpoint3 = false;
+bool checkpoint4 = false;
+
+Future<void> foo(Future<String> f) async {
+  checkpoint1 = true;
+  final String result = await f;
+  checkpoint3 = true;
+  print(result.runtimeType);
+}
+
+class F implements Future<String> {
+  Future<R> then<R>(FutureOr<R> Function(String) onValue, {Function onError}) {
+    checkpoint2 = true;
+    final result = (onValue as FutureOr<R> Function(dynamic))(10);
+    checkpoint4 = true;
+    return Future.value(result);
+  }
+
+  @override
+  dynamic noSuchMethod(i) => throw 'Unimplimented';
+}
+
+void main() {
+  bool seenError = false;
+  runZoned(() {
+    foo(F());
+  }, onError: (e, st) {
+    seenError = true;
+  });
+  Expect.isTrue(checkpoint1);
+  Expect.isTrue(checkpoint2);
+  Expect.isFalse(checkpoint3);
+  Expect.isFalse(checkpoint4);
+  Expect.isTrue(seenError);
+}
diff --git a/tools/VERSION b/tools/VERSION
index ef321a3..bdb2bf8 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 18
 PATCH 0
-PRERELEASE 242
+PRERELEASE 243
 PRERELEASE_PATCH 0
\ No newline at end of file