Version 2.16.0-28.0.dev

Merge commit '001df490a7c6f922f6c9e3b0d8e73443e3003de1' into 'dev'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8e49630..fc40635 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,17 +1,13 @@
-## 2.17.0
-
-### Core libraries
-
-- **Breaking Change** [#47653](https://github.com/dart-lang/sdk/issues/47653):
-  On Windows, `Directory.rename` will no longer delete a directory if
-  `newPath` specifies one. Instead, a `FileSystemException` will be thrown.
-
 ## 2.16.0
 
 ### Core libraries
 
 #### `dart:core`
 
+- **Breaking Change** [#47653](https://github.com/dart-lang/sdk/issues/47653):
+  On Windows, `Directory.rename` will no longer delete a directory if
+  `newPath` specifies one. Instead, a `FileSystemException` will be thrown.
+
 - Add `Error.throwWithStackTrace` which can `throw` an
   error with an existing stack trace, instead of creating
   a new stack trace.
diff --git a/pkg/analysis_server/lib/src/domain_execution.dart b/pkg/analysis_server/lib/src/domain_execution.dart
index f047af1..c96814e 100644
--- a/pkg/analysis_server/lib/src/domain_execution.dart
+++ b/pkg/analysis_server/lib/src/domain_execution.dart
@@ -125,7 +125,7 @@
       if (source.uriKind != UriKind.FILE_URI) {
         uri = source.uri.toString();
       } else {
-        uri = sourceFactory.restoreUri(source).toString();
+        uri = sourceFactory.pathToUri(file).toString();
       }
       return ExecutionMapUriResult(uri: uri).toResponse(request.id);
     } else if (uri != null) {
diff --git a/pkg/analysis_server/lib/src/services/refactoring/move_file.dart b/pkg/analysis_server/lib/src/services/refactoring/move_file.dart
index 6e97072..f3cae42 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/move_file.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/move_file.dart
@@ -158,9 +158,7 @@
     var refDir = pathContext.dirname(reference.file);
     // Try to keep package: URI
     if (_isPackageReference(reference)) {
-      Source newSource =
-          NonExistingSource(newFile, pathos.toUri(newFile), UriKind.FILE_URI);
-      var restoredUri = driver.sourceFactory.restoreUri(newSource);
+      var restoredUri = driver.sourceFactory.pathToUri(newFile);
       // If the new URI is not a package: URI, fall back to computing a relative
       // URI below.
       if (restoredUri?.isScheme('package') ?? false) {
diff --git a/pkg/analyzer/CHANGELOG.md b/pkg/analyzer/CHANGELOG.md
index d672ae5..d9a4742 100644
--- a/pkg/analyzer/CHANGELOG.md
+++ b/pkg/analyzer/CHANGELOG.md
@@ -2,6 +2,8 @@
 * Deprecations and renames for `getXyz` methods in `AnalysisDriver`.
 * Removed uppercase named constants from `double` in mock SDK.
 * Deprecated `path` and `uri` from `AnalysisResult`.
+* Deprecated `UriResolver.restoreAbsolute`, use `pathToUri` instead.
+* Deprecated `SourceFactory.restoreAbsolute`, use `pathToUri` instead.
 
 ## 2.7.0
 * Updated `ConstructorElement.displayName` to either `Class` or `Class.constructor`.
diff --git a/pkg/analyzer/lib/src/context/source.dart b/pkg/analyzer/lib/src/context/source.dart
index ff628f6..b603111 100644
--- a/pkg/analyzer/lib/src/context/source.dart
+++ b/pkg/analyzer/lib/src/context/source.dart
@@ -102,6 +102,17 @@
   }
 
   @override
+  Uri? pathToUri(String path) {
+    for (var resolver in resolvers) {
+      var uri = resolver.pathToUri(path);
+      if (uri != null) {
+        return uri;
+      }
+    }
+    return null;
+  }
+
+  @override
   Source? resolveUri(Source? containingSource, String? containedUri) {
     if (containedUri == null) {
       return null;
@@ -128,21 +139,13 @@
     }
   }
 
+  @Deprecated('Use pathToUri() instead')
   @override
   Uri? restoreUri(Source source) {
     if (source is InSummarySource) {
       return source.uri;
     }
-    for (UriResolver resolver in resolvers) {
-      // First see if a resolver can restore the URI.
-      Uri? uri = resolver.restoreAbsolute(source);
-
-      if (uri != null) {
-        return uri;
-      }
-    }
-
-    return null;
+    return pathToUri(source.fullName);
   }
 
   /// Return a source object representing the URI that results from resolving
diff --git a/pkg/analyzer/lib/src/dart/analysis/file_state.dart b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
index a275ba2..8061657 100644
--- a/pkg/analyzer/lib/src/dart/analysis/file_state.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
@@ -832,8 +832,7 @@
     var file = _pathToFile[path];
     if (file == null) {
       File resource = _resourceProvider.getFile(path);
-      Source fileSource = resource.createSource();
-      Uri uri = _sourceFactory.restoreUri(fileSource)!;
+      Uri uri = _sourceFactory.pathToUri(path)!;
       file = _newFile(resource, path, uri);
     }
     return file;
@@ -897,10 +896,8 @@
   bool hasUri(String path) {
     bool? flag = _hasUriForPath[path];
     if (flag == null) {
-      File resource = _resourceProvider.getFile(path);
-      Source fileSource = resource.createSource();
-      Uri? uri = _sourceFactory.restoreUri(fileSource);
-      Source? uriSource = _sourceFactory.forUri2(uri!);
+      Uri uri = _sourceFactory.pathToUri(path)!;
+      Source? uriSource = _sourceFactory.forUri2(uri);
       flag = uriSource?.fullName == path;
       _hasUriForPath[path] = flag;
     }
diff --git a/pkg/analyzer/lib/src/dart/analysis/uri_converter.dart b/pkg/analyzer/lib/src/dart/analysis/uri_converter.dart
index 9e6bf4a..bda06b6 100644
--- a/pkg/analyzer/lib/src/dart/analysis/uri_converter.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/uri_converter.dart
@@ -5,7 +5,6 @@
 import 'package:analyzer/dart/analysis/uri_converter.dart';
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/src/dart/analysis/driver.dart';
-import 'package:analyzer/src/generated/source.dart';
 import 'package:path/src/context.dart';
 
 /// An implementation of a URI converter based on an analysis driver.
@@ -32,8 +31,7 @@
         }
       }
     }
-    Source source = provider.getFile(path).createSource();
-    return driver.sourceFactory.restoreUri(source);
+    return driver.sourceFactory.pathToUri(path);
   }
 
   @override
diff --git a/pkg/analyzer/lib/src/dart/micro/analysis_context.dart b/pkg/analyzer/lib/src/dart/micro/analysis_context.dart
index ef663ca..10b3313 100644
--- a/pkg/analyzer/lib/src/dart/micro/analysis_context.dart
+++ b/pkg/analyzer/lib/src/dart/micro/analysis_context.dart
@@ -208,9 +208,7 @@
 
   @override
   Uri? pathToUri(String path, {String? containingPath}) {
-    var fileUri = resourceProvider.pathContext.toUri(path);
-    var fileSource = sourceFactory.forUri2(fileUri)!;
-    return sourceFactory.restoreUri(fileSource);
+    return sourceFactory.pathToUri(path);
   }
 
   @override
diff --git a/pkg/analyzer/lib/src/dart/micro/library_graph.dart b/pkg/analyzer/lib/src/dart/micro/library_graph.dart
index 581a44e..d18f5bae 100644
--- a/pkg/analyzer/lib/src/dart/micro/library_graph.dart
+++ b/pkg/analyzer/lib/src/dart/micro/library_graph.dart
@@ -335,10 +335,7 @@
       return file;
     }
 
-    var fileUri = _resourceProvider.pathContext.toUri(path);
-    var uri = _sourceFactory.restoreUri(
-      _FakeSource(path, fileUri),
-    );
+    var uri = _sourceFactory.pathToUri(path);
     if (uri == null) {
       throw StateError('Unable to convert path to URI: $path');
     }
@@ -589,19 +586,6 @@
   });
 }
 
-class _FakeSource implements Source {
-  @override
-  final String fullName;
-
-  @override
-  final Uri uri;
-
-  _FakeSource(this.fullName, this.uri);
-
-  @override
-  dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
-}
-
 class _FileStateFiles {
   final List<FileState> imported = [];
   final List<FileState> exported = [];
diff --git a/pkg/analyzer/lib/src/dart/sdk/sdk.dart b/pkg/analyzer/lib/src/dart/sdk/sdk.dart
index b23e091..3520c90 100644
--- a/pkg/analyzer/lib/src/dart/sdk/sdk.dart
+++ b/pkg/analyzer/lib/src/dart/sdk/sdk.dart
@@ -128,6 +128,23 @@
     return source;
   }
 
+  @override
+  Uri? pathToUri(String path) {
+    var file = resourceProvider.getFile(path);
+
+    var uriStr = _getPath(file);
+    if (uriStr == null) {
+      return null;
+    }
+
+    try {
+      return Uri.parse(uriStr);
+    } on FormatException {
+      return null;
+    }
+  }
+
+  /// TODO(scheglov) This name is misleading, returns `dart:foo/bar.dart`.
   String? _getPath(File file) {
     List<SdkLibrary> libraries = libraryMap.sdkLibraries;
     int length = libraries.length;
diff --git a/pkg/analyzer/lib/src/file_system/file_system.dart b/pkg/analyzer/lib/src/file_system/file_system.dart
index 16c7b2a..b7b4655 100644
--- a/pkg/analyzer/lib/src/file_system/file_system.dart
+++ b/pkg/analyzer/lib/src/file_system/file_system.dart
@@ -18,6 +18,11 @@
   ResourceProvider get provider => _provider;
 
   @override
+  Uri pathToUri(String path) {
+    return _provider.pathContext.toUri(path);
+  }
+
+  @override
   Source? resolveAbsolute(Uri uri) {
     if (!isFileUri(uri)) {
       return null;
@@ -27,9 +32,9 @@
     return file.createSource(uri);
   }
 
+  @Deprecated('Use pathToUri() instead')
   @override
-  Uri restoreAbsolute(Source source) =>
-      _provider.pathContext.toUri(source.fullName);
+  Uri restoreAbsolute(Source source) => pathToUri(source.fullName);
 
   /// Return `true` if the given [uri] is a `file` URI.
   static bool isFileUri(Uri uri) => uri.scheme == FILE_SCHEME;
diff --git a/pkg/analyzer/lib/src/generated/sdk.dart b/pkg/analyzer/lib/src/generated/sdk.dart
index 1fd1ec8..265ce79 100644
--- a/pkg/analyzer/lib/src/generated/sdk.dart
+++ b/pkg/analyzer/lib/src/generated/sdk.dart
@@ -59,6 +59,10 @@
   /// Return the source representing the library with the given 'dart:' [uri],
   /// or `null` if the given URI does not denote a library in this SDK.
   Source? mapDartUri(String uri);
+
+  /// Return the `dart` URI representing the given [path] if the file is in
+  /// this SDK, or `null` if the file is not in this SDK.
+  Uri? pathToUri(String path);
 }
 
 /// Manages the DartSdk's that have been created. Clients need to create
diff --git a/pkg/analyzer/lib/src/generated/source.dart b/pkg/analyzer/lib/src/generated/source.dart
index 69ac8ca..bb1bbd3 100644
--- a/pkg/analyzer/lib/src/generated/source.dart
+++ b/pkg/analyzer/lib/src/generated/source.dart
@@ -61,6 +61,11 @@
   DartSdk get dartSdk => _sdk;
 
   @override
+  Uri? pathToUri(String path) {
+    return _sdk.pathToUri(path);
+  }
+
+  @override
   Source? resolveAbsolute(Uri uri) {
     if (!isDartUri(uri)) {
       return null;
@@ -68,12 +73,6 @@
     return _sdk.mapDartUri(uri.toString());
   }
 
-  @override
-  Uri? restoreAbsolute(Source source) {
-    var dartSource = _sdk.fromFileUri(source.uri);
-    return dartSource?.uri;
-  }
-
   /// Return `true` if the given URI is a `dart:` URI.
   ///
   /// @param uri the URI being tested
@@ -285,6 +284,13 @@
   /// @return a source object representing the absolute URI
   Source? forUri2(Uri absoluteUri);
 
+  /// Return the URI that should be used to reference the file at the absolute
+  /// [path], or `null` if there is no valid way to reference the file.
+  /// The file at that path is not required to exist.
+  ///
+  /// Throws an [ArgumentError] if the [path] is not a valid path.
+  Uri? pathToUri(String path);
+
   /// Return a source representing the URI that results from resolving the given
   /// (possibly relative) [containedUri] against the URI associated with the
   /// [containingSource], whether or not the resulting source exists, or `null`
@@ -297,6 +303,7 @@
   ///
   /// @param source the source to get URI for
   /// @return the absolute URI representing the given source
+  @Deprecated('Use pathToUri() instead')
   Uri? restoreUri(Source source);
 }
 
@@ -410,6 +417,14 @@
 /// used to resolve URI's for a source factory. Subclasses of this class are
 /// expected to resolve a single scheme of absolute URI.
 abstract class UriResolver {
+  /// Return the absolute URI that should be used to reference the file at the
+  /// absolute [path], or `null` if this resolver cannot reference this file.
+  /// The file at that path is not required to exist.
+  ///
+  /// Throws an [ArgumentError] if the [path] is not a valid path.
+  /// ignore: deprecated_member_use_from_same_package
+  Uri? pathToUri(String path) => restoreAbsolute(_FakeSource(path));
+
   /// Resolve the given absolute [uri]. Return a [Source] representing the file
   /// to which it was resolved, whether or not the resulting source exists, or
   /// `null` if it could not be resolved because the URI is invalid.
@@ -419,5 +434,21 @@
   /// valid URI cannot be computed.
   ///
   /// The computation should be based solely on [source.fullName].
-  Uri? restoreAbsolute(Source source) => null;
+  @Deprecated('Use pathToUri() instead')
+  Uri? restoreAbsolute(Source source) {
+    return pathToUri(source.fullName);
+  }
+}
+
+class _FakeSource implements Source {
+  @override
+  final String fullName;
+
+  _FakeSource(this.fullName);
+
+  @override
+  Uri get uri => pathos.toUri(fullName);
+
+  @override
+  dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
 }
diff --git a/pkg/analyzer/lib/src/source/package_map_resolver.dart b/pkg/analyzer/lib/src/source/package_map_resolver.dart
index 0feee31..9b96f99 100644
--- a/pkg/analyzer/lib/src/source/package_map_resolver.dart
+++ b/pkg/analyzer/lib/src/source/package_map_resolver.dart
@@ -37,6 +37,22 @@
   }
 
   @override
+  Uri? pathToUri(String path) {
+    pathos.Context pathContext = resourceProvider.pathContext;
+    for (String pkgName in packageMap.keys) {
+      Folder pkgFolder = packageMap[pkgName]![0];
+      String pkgFolderPath = pkgFolder.path;
+      if (path.startsWith(pkgFolderPath + pathContext.separator)) {
+        String relPath = path.substring(pkgFolderPath.length + 1);
+        List<String> relPathComponents = pathContext.split(relPath);
+        String relUriPath = pathos.posix.joinAll(relPathComponents);
+        return Uri.parse('$PACKAGE_SCHEME:$pkgName/$relUriPath');
+      }
+    }
+    return null;
+  }
+
+  @override
   Source? resolveAbsolute(Uri uri) {
     if (!isPackageUri(uri)) {
       return null;
@@ -61,23 +77,6 @@
     return null;
   }
 
-  @override
-  Uri? restoreAbsolute(Source source) {
-    String sourcePath = source.fullName;
-    pathos.Context pathContext = resourceProvider.pathContext;
-    for (String pkgName in packageMap.keys) {
-      Folder pkgFolder = packageMap[pkgName]![0];
-      String pkgFolderPath = pkgFolder.path;
-      if (sourcePath.startsWith(pkgFolderPath + pathContext.separator)) {
-        String relPath = sourcePath.substring(pkgFolderPath.length + 1);
-        List<String> relPathComponents = pathContext.split(relPath);
-        String relUriPath = pathos.posix.joinAll(relPathComponents);
-        return Uri.parse('$PACKAGE_SCHEME:$pkgName/$relUriPath');
-      }
-    }
-    return null;
-  }
-
   /// Returns `true` if [uri] is a `package` URI.
   static bool isPackageUri(Uri uri) {
     return uri.scheme == PACKAGE_SCHEME;
diff --git a/pkg/analyzer/lib/src/summary/package_bundle_reader.dart b/pkg/analyzer/lib/src/summary/package_bundle_reader.dart
index 03cce04..d39d264 100644
--- a/pkg/analyzer/lib/src/summary/package_bundle_reader.dart
+++ b/pkg/analyzer/lib/src/summary/package_bundle_reader.dart
@@ -95,6 +95,9 @@
   InSummaryUriResolver(this.resourceProvider, this._dataStore);
 
   @override
+  Uri? pathToUri(String path) => null;
+
+  @override
   Source? resolveAbsolute(Uri uri) {
     String uriString = uri.toString();
     String? summaryPath = _dataStore.uriToSummaryPath[uriString];
diff --git a/pkg/analyzer/lib/src/summary/summary_sdk.dart b/pkg/analyzer/lib/src/summary/summary_sdk.dart
index 1ad7efe..e53670d 100644
--- a/pkg/analyzer/lib/src/summary/summary_sdk.dart
+++ b/pkg/analyzer/lib/src/summary/summary_sdk.dart
@@ -90,4 +90,10 @@
     Uri uri = Uri.parse(uriStr);
     return _uriResolver.resolveAbsolute(uri);
   }
+
+  @override
+  Uri? pathToUri(String path) {
+    // Libraries from summaries don't have corresponding Dart files.
+    return null;
+  }
 }
diff --git a/pkg/analyzer/lib/src/util/uri.dart b/pkg/analyzer/lib/src/util/uri.dart
index 4192e3f..08e3008 100644
--- a/pkg/analyzer/lib/src/util/uri.dart
+++ b/pkg/analyzer/lib/src/util/uri.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/summary/package_bundle_reader.dart';
 import 'package:path/path.dart';
 
 String fileUriToNormalizedPath(Context context, Uri fileUri) {
@@ -34,5 +35,9 @@
     return null;
   }
 
-  return sourceFactory.restoreUri(source);
+  if (source is InSummarySource) {
+    return source.uri;
+  }
+
+  return sourceFactory.pathToUri(source.fullName);
 }
diff --git a/pkg/analyzer/lib/src/workspace/bazel.dart b/pkg/analyzer/lib/src/workspace/bazel.dart
index d7743a0..c8d032c 100644
--- a/pkg/analyzer/lib/src/workspace/bazel.dart
+++ b/pkg/analyzer/lib/src/workspace/bazel.dart
@@ -53,6 +53,24 @@
         _context = workspace.provider.pathContext;
 
   @override
+  Uri? pathToUri(String path) {
+    // Search in each root.
+    for (var root in [
+      ..._workspace.binPaths,
+      _workspace.genfiles,
+      _workspace.readonly,
+      _workspace.root
+    ]) {
+      var uriParts = _restoreUriParts(root, path);
+      if (uriParts != null) {
+        return Uri.parse('package:${uriParts[0]}/${uriParts[1]}');
+      }
+    }
+
+    return null;
+  }
+
+  @override
   Source? resolveAbsolute(Uri uri) {
     var source = _sourceCache[uri];
     if (source == null) {
@@ -64,26 +82,6 @@
     return source;
   }
 
-  @override
-  Uri? restoreAbsolute(Source source) {
-    String filePath = source.fullName;
-
-    // Search in each root.
-    for (var root in [
-      ..._workspace.binPaths,
-      _workspace.genfiles,
-      _workspace.readonly,
-      _workspace.root
-    ]) {
-      var uriParts = _restoreUriParts(root, filePath);
-      if (uriParts != null) {
-        return Uri.parse('package:${uriParts[0]}/${uriParts[1]}');
-      }
-    }
-
-    return null;
-  }
-
   Source? _resolveAbsolute(Uri uri) {
     if (uri.scheme == 'file') {
       var path = fileUriToNormalizedPath(_context, uri);
diff --git a/pkg/analyzer/lib/src/workspace/package_build.dart b/pkg/analyzer/lib/src/workspace/package_build.dart
index ba6e15c..53a293f 100644
--- a/pkg/analyzer/lib/src/workspace/package_build.dart
+++ b/pkg/analyzer/lib/src/workspace/package_build.dart
@@ -60,6 +60,18 @@
   Map<String, List<Folder>> get packageMap => _workspace.packageMap;
 
   @override
+  Uri? pathToUri(String path) {
+    if (_context.isWithin(_workspace.root, path)) {
+      var uriParts = _restoreUriParts(path);
+      if (uriParts != null) {
+        return Uri.parse('package:${uriParts[0]}/${uriParts[1]}');
+      }
+    }
+
+    return _normalUriResolver.pathToUri(path);
+  }
+
+  @override
   Source? resolveAbsolute(Uri uri) {
     if (uri.scheme != 'package') {
       return null;
@@ -90,20 +102,6 @@
     return basicResolverSource;
   }
 
-  @override
-  Uri? restoreAbsolute(Source source) {
-    String filePath = source.fullName;
-
-    if (_context.isWithin(_workspace.root, filePath)) {
-      var uriParts = _restoreUriParts(filePath);
-      if (uriParts != null) {
-        return Uri.parse('package:${uriParts[0]}/${uriParts[1]}');
-      }
-    }
-
-    return _normalUriResolver.restoreAbsolute(source);
-  }
-
   List<String>? _restoreUriParts(String filePath) {
     String relative = _context.relative(filePath, from: _workspace.root);
     List<String> components = _context.split(relative);
diff --git a/pkg/analyzer/test/file_system/resource_uri_resolver_test.dart b/pkg/analyzer/test/file_system/resource_uri_resolver_test.dart
index c6192fb..9ae396e 100644
--- a/pkg/analyzer/test/file_system/resource_uri_resolver_test.dart
+++ b/pkg/analyzer/test/file_system/resource_uri_resolver_test.dart
@@ -29,6 +29,12 @@
     expect(resolver, isNotNull);
   }
 
+  void test_pathToUri() {
+    var path = convertPath('/test.dart');
+    var uri = toUri(path);
+    expect(resolver.pathToUri(path), uri);
+  }
+
   void test_resolveAbsolute_file() {
     var uri = toUri('/test.dart');
 
@@ -59,6 +65,7 @@
     expect(source, isNull);
   }
 
+  @Deprecated('Use pathToUri() instead')
   void test_restoreAbsolute() {
     var uri = toUri('/test.dart');
 
diff --git a/pkg/analyzer/test/generated/all_the_rest_test.dart b/pkg/analyzer/test/generated/all_the_rest_test.dart
index 2fd5fff..1785d44 100644
--- a/pkg/analyzer/test/generated/all_the_rest_test.dart
+++ b/pkg/analyzer/test/generated/all_the_rest_test.dart
@@ -44,6 +44,18 @@
     expect(DartUriResolver.isDartUri(uri), isFalse);
   }
 
+  void test_pathToUri_library() {
+    var path = convertPath('/sdk/lib/core/core.dart');
+    var dartUri = resolver.pathToUri(path);
+    expect(dartUri.toString(), 'dart:core');
+  }
+
+  void test_pathToUri_part() {
+    var path = convertPath('/sdk/lib/core/int.dart');
+    var dartUri = resolver.pathToUri(path);
+    expect(dartUri.toString(), 'dart:core/int.dart');
+  }
+
   void test_resolve_dart_library() {
     var source = resolver.resolveAbsolute(Uri.parse('dart:core'));
     expect(source, isNotNull);
@@ -64,16 +76,18 @@
     expect(result, isNull);
   }
 
+  @Deprecated('Use pathToUri() instead')
   void test_restoreAbsolute_library() {
     _SourceMock source = _SourceMock();
-    source.uri = toUri('/sdk/lib/core/core.dart');
+    source.fullName = convertPath('/sdk/lib/core/core.dart');
     var dartUri = resolver.restoreAbsolute(source);
     expect(dartUri.toString(), 'dart:core');
   }
 
+  @Deprecated('Use pathToUri() instead')
   void test_restoreAbsolute_part() {
     _SourceMock source = _SourceMock();
-    source.uri = toUri('/sdk/lib/core/int.dart');
+    source.fullName = convertPath('/sdk/lib/core/int.dart');
     var dartUri = resolver.restoreAbsolute(source);
     expect(dartUri.toString(), 'dart:core/int.dart');
   }
@@ -351,6 +365,9 @@
 
 class _SourceMock implements Source {
   @override
+  late final String fullName;
+
+  @override
   late final Uri uri;
 
   @override
diff --git a/pkg/analyzer/test/generated/source_factory_test.dart b/pkg/analyzer/test/generated/source_factory_test.dart
index 98e3f04..f13f48d 100644
--- a/pkg/analyzer/test/generated/source_factory_test.dart
+++ b/pkg/analyzer/test/generated/source_factory_test.dart
@@ -39,6 +39,16 @@
     expect(SourceFactory([]), isNotNull);
   }
 
+  void test_pathToUri() {
+    File file1 = getFile("/some/file1.dart");
+    File file2 = getFile("/some/file2.dart");
+    Uri expected1 = Uri.parse("file:///my_file.dart");
+    SourceFactory factory =
+        SourceFactory([UriResolver_restoreUri(file1.path, expected1)]);
+    expect(factory.pathToUri(file1.path), expected1);
+    expect(factory.pathToUri(file2.path), isNull);
+  }
+
   void test_resolveUri_absolute() {
     UriResolver_absolute resolver = UriResolver_absolute();
     SourceFactory factory = SourceFactory([resolver]);
@@ -95,6 +105,7 @@
     expect(result.uri.toString(), 'package:package/dir/second.dart');
   }
 
+  @Deprecated('Use pathToUri() instead')
   void test_restoreUri() {
     File file1 = getFile("/some/file1.dart");
     File file2 = getFile("/some/file2.dart");
@@ -102,7 +113,7 @@
     Source source2 = FileSource(file2);
     Uri expected1 = Uri.parse("file:///my_file.dart");
     SourceFactory factory =
-        SourceFactory([UriResolver_restoreUri(source1, expected1)]);
+        SourceFactory([UriResolver_restoreUri(file1.path, expected1)]);
     expect(factory.restoreUri(source1), same(expected1));
     expect(factory.restoreUri(source2), isNull);
   }
@@ -121,20 +132,20 @@
 }
 
 class UriResolver_restoreUri extends UriResolver {
-  Source source1;
+  String path1;
   Uri expected1;
-  UriResolver_restoreUri(this.source1, this.expected1);
+  UriResolver_restoreUri(this.path1, this.expected1);
 
   @override
-  Source? resolveAbsolute(Uri uri) => null;
-
-  @override
-  Uri? restoreAbsolute(Source source) {
-    if (identical(source, source1)) {
+  Uri? pathToUri(String path) {
+    if (path == path1) {
       return expected1;
     }
     return null;
   }
+
+  @override
+  Source? resolveAbsolute(Uri uri) => null;
 }
 
 class UriResolver_SourceFactoryTest_test_fromEncoding_valid
diff --git a/pkg/analyzer/test/source/package_map_resolver_test.dart b/pkg/analyzer/test/source/package_map_resolver_test.dart
index 19aee6d..2cd49c3 100644
--- a/pkg/analyzer/test/source/package_map_resolver_test.dart
+++ b/pkg/analyzer/test/source/package_map_resolver_test.dart
@@ -39,6 +39,33 @@
     expect(PackageMapUriResolver.isPackageUri(uri), isFalse);
   }
 
+  void test_pathToUri() {
+    String pkgFileA = provider.convertPath('/pkgA/lib/libA.dart');
+    String pkgFileB = provider.convertPath('/pkgB/lib/src/libB.dart');
+    provider.newFile(pkgFileA, 'library lib_a;');
+    provider.newFile(pkgFileB, 'library lib_b;');
+    PackageMapUriResolver resolver =
+        PackageMapUriResolver(provider, <String, List<Folder>>{
+      'pkgA': <Folder>[provider.getFolder(provider.convertPath('/pkgA/lib'))],
+      'pkgB': <Folder>[provider.getFolder(provider.convertPath('/pkgB/lib'))]
+    });
+    {
+      var path = provider.convertPath('/pkgA/lib/libA.dart');
+      var uri = resolver.pathToUri(path);
+      expect(uri, Uri.parse('package:pkgA/libA.dart'));
+    }
+    {
+      var path = provider.convertPath('/pkgB/lib/src/libB.dart');
+      var uri = resolver.pathToUri(path);
+      expect(uri, Uri.parse('package:pkgB/src/libB.dart'));
+    }
+    {
+      var path = provider.convertPath('/no/such/file');
+      var uri = resolver.pathToUri(path);
+      expect(uri, isNull);
+    }
+  }
+
   void test_resolve_multiple_folders() {
     var a = provider.newFile(provider.convertPath('/aaa/a.dart'), '');
     var b = provider.newFile(provider.convertPath('/bbb/b.dart'), '');
@@ -144,6 +171,7 @@
     expect(result, isNull);
   }
 
+  @Deprecated('Use pathToUri() instead')
   void test_restoreAbsolute() {
     String pkgFileA = provider.convertPath('/pkgA/lib/libA.dart');
     String pkgFileB = provider.convertPath('/pkgB/lib/src/libB.dart');
diff --git a/pkg/analyzer/test/src/dart/analysis/base.dart b/pkg/analyzer/test/src/dart/analysis/base.dart
index a015fec..b6c9542 100644
--- a/pkg/analyzer/test/src/dart/analysis/base.dart
+++ b/pkg/analyzer/test/src/dart/analysis/base.dart
@@ -153,10 +153,10 @@
   void tearDown() {}
 }
 
-class _GeneratedUriResolverMock implements UriResolver {
+class _GeneratedUriResolverMock extends UriResolver {
   Source? Function(Uri)? resolveAbsoluteFunction;
 
-  Uri? Function(Source)? restoreAbsoluteFunction;
+  Uri? Function(String)? pathToUriFunction;
 
   @override
   noSuchMethod(Invocation invocation) {
@@ -164,17 +164,14 @@
   }
 
   @override
-  Source? resolveAbsolute(Uri uri) {
-    if (resolveAbsoluteFunction != null) {
-      return resolveAbsoluteFunction!(uri);
-    }
-    return null;
+  Uri? pathToUri(String path) {
+    return pathToUriFunction?.call(path);
   }
 
   @override
-  Uri? restoreAbsolute(Source source) {
-    if (restoreAbsoluteFunction != null) {
-      return restoreAbsoluteFunction!(source);
+  Source? resolveAbsolute(Uri uri) {
+    if (resolveAbsoluteFunction != null) {
+      return resolveAbsoluteFunction!(uri);
     }
     return null;
   }
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
index 35c3ab2..b04f7f3 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
@@ -1246,8 +1246,7 @@
     Source generatedSource = _SourceMock(generatedPath, uri);
 
     generatedUriResolver.resolveAbsoluteFunction = (uri) => generatedSource;
-    generatedUriResolver.restoreAbsoluteFunction = (Source source) {
-      String path = source.fullName;
+    generatedUriResolver.pathToUriFunction = (path) {
       if (path == templatePath || path == generatedPath) {
         return uri;
       } else {
diff --git a/pkg/analyzer/test/src/dart/analysis/feature_set_provider_test.dart b/pkg/analyzer/test/src/dart/analysis/feature_set_provider_test.dart
index dfcbe51..0f485b1 100644
--- a/pkg/analyzer/test/src/dart/analysis/feature_set_provider_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/feature_set_provider_test.dart
@@ -446,9 +446,7 @@
 
   FeatureSet _getPathFeatureSet(String path) {
     path = convertPath(path);
-    var fileUri = toUri(path);
-    var fileSource = sourceFactory.forUri2(fileUri)!;
-    var uri = sourceFactory.restoreUri(fileSource)!;
+    var uri = sourceFactory.pathToUri(path)!;
     return provider.getFeatureSet(path, uri);
   }
 
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 6899a94..b7e3039 100644
--- a/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
@@ -718,10 +718,10 @@
   }
 }
 
-class _GeneratedUriResolverMock implements UriResolver {
+class _GeneratedUriResolverMock extends UriResolver {
   Source? Function(Uri)? resolveAbsoluteFunction;
 
-  Uri? Function(Source)? restoreAbsoluteFunction;
+  Uri? Function(String)? pathToUriFunction;
 
   @override
   noSuchMethod(Invocation invocation) {
@@ -729,17 +729,14 @@
   }
 
   @override
-  Source? resolveAbsolute(Uri uri) {
-    if (resolveAbsoluteFunction != null) {
-      return resolveAbsoluteFunction!(uri);
-    }
-    return null;
+  Uri? pathToUri(String path) {
+    return pathToUriFunction?.call(path);
   }
 
   @override
-  Uri? restoreAbsolute(Source source) {
-    if (restoreAbsoluteFunction != null) {
-      return restoreAbsoluteFunction!(source);
+  Source? resolveAbsolute(Uri uri) {
+    if (resolveAbsoluteFunction != null) {
+      return resolveAbsoluteFunction!(uri);
     }
     return null;
   }
diff --git a/pkg/analyzer/test/src/summary/resynthesize_common.dart b/pkg/analyzer/test/src/summary/resynthesize_common.dart
index 450bc47..bdfa95c 100644
--- a/pkg/analyzer/test/src/summary/resynthesize_common.dart
+++ b/pkg/analyzer/test/src/summary/resynthesize_common.dart
@@ -72,8 +72,7 @@
 
   Source addSource(String path, String contents) {
     var file = newFile(path, content: contents);
-    var fileSource = file.createSource();
-    var uri = sourceFactory.restoreUri(fileSource)!;
+    var uri = sourceFactory.pathToUri(file.path)!;
     return sourceFactory.forUri2(uri)!;
   }
 
diff --git a/pkg/analyzer/test/src/workspace/bazel_test.dart b/pkg/analyzer/test/src/workspace/bazel_test.dart
index 88e983e..460a696 100644
--- a/pkg/analyzer/test/src/workspace/bazel_test.dart
+++ b/pkg/analyzer/test/src/workspace/bazel_test.dart
@@ -38,6 +38,12 @@
     expect(workspace.isBazel, isTrue);
   }
 
+  void test_pathToUri() {
+    Uri uri = toUri('/workspace/test.dart');
+    var source = resolver.resolveAbsolute(uri)!;
+    expect(resolver.pathToUri(source.fullName), uri);
+  }
+
   void test_resolveAbsolute_doesNotExist() {
     var source = _resolvePath('/workspace/foo.dart')!;
     expect(source.exists(), isFalse);
@@ -79,6 +85,7 @@
     expect(source, isNull);
   }
 
+  @Deprecated('Use pathToUri() instead')
   void test_restoreAbsolute() {
     Uri uri =
         resourceProvider.pathContext.toUri(convertPath('/workspace/test.dart'));
@@ -561,21 +568,25 @@
       {bool exists = true, bool restore = true}) {
     Uri uri = Uri.parse(uriStr);
     var source = resolver.resolveAbsolute(uri)!;
-    expect(source.fullName, convertPath(posixPath));
+    var path = source.fullName;
+    expect(path, convertPath(posixPath));
     expect(source.uri, uri);
     expect(source.exists(), exists);
     // If enabled, test also "restoreAbsolute".
     if (restore) {
-      var uri = resolver.restoreAbsolute(source);
-      expect(uri.toString(), uriStr);
+      expect(resolver.pathToUri(path), uri);
+      // ignore: deprecated_member_use_from_same_package
+      expect(resolver.restoreAbsolute(source), uri);
     }
   }
 
-  void _assertRestore(String posixPath, String? expectedUri) {
+  void _assertRestore(String posixPath, String? expectedUriStr) {
+    var expectedUri = expectedUriStr != null ? Uri.parse(expectedUriStr) : null;
     String path = convertPath(posixPath);
     _MockSource source = _MockSource(path);
-    var uri = resolver.restoreAbsolute(source);
-    expect(uri?.toString(), expectedUri);
+    expect(resolver.pathToUri(path), expectedUri);
+    // ignore: deprecated_member_use_from_same_package
+    expect(resolver.restoreAbsolute(source), expectedUri);
   }
 }
 
diff --git a/pkg/analyzer/test/src/workspace/package_build_test.dart b/pkg/analyzer/test/src/workspace/package_build_test.dart
index b7a9650..6e2cf99 100644
--- a/pkg/analyzer/test/src/workspace/package_build_test.dart
+++ b/pkg/analyzer/test/src/workspace/package_build_test.dart
@@ -20,23 +20,25 @@
 
 class MockUriResolver implements UriResolver {
   Map<Uri, File> uriToFile = {};
-  Map<String, Uri> pathToUri = {};
+  Map<String, Uri> pathToUriMap = {};
 
   void add(Uri uri, File file) {
     uriToFile[uri] = file;
-    pathToUri[file.path] = uri;
+    pathToUriMap[file.path] = uri;
   }
 
   @override
   noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
 
   @override
-  Source? resolveAbsolute(Uri uri) {
-    return uriToFile[uri]?.createSource(uri);
+  Uri? pathToUri(String path) {
+    return pathToUriMap[path];
   }
 
   @override
-  Uri? restoreAbsolute(Source source) => pathToUri[source.fullName];
+  Source? resolveAbsolute(Uri uri) {
+    return uriToFile[uri]?.createSource(uri);
+  }
 }
 
 @reflectiveTest
@@ -61,6 +63,12 @@
     expect(workspace.isBazel, isFalse);
   }
 
+  void test_pathToUri() {
+    var uri = toUri('/workspace/test.dart');
+    var source = resolver.resolveAbsolute(uri)!;
+    expect(resolver.pathToUri(source.fullName), uri);
+  }
+
   void test_resolveAbsolute_doesNotExist() {
     var source = _resolvePath('/workspace/foo.dart')!;
     expect(source, isNotNull);
@@ -98,6 +106,7 @@
     expect(source, isNull);
   }
 
+  @Deprecated('Use pathToUri() instead')
   void test_restoreAbsolute() {
     Uri uri =
         resourceProvider.pathContext.toUri(convertPath('/workspace/test.dart'));
@@ -207,13 +216,15 @@
   Source _assertResolveUri(Uri uri, String posixPath,
       {bool exists = true, bool restore = true}) {
     var source = resolver.resolveAbsolute(uri)!;
-    expect(source.fullName, convertPath(posixPath));
+    var path = source.fullName;
+    expect(path, convertPath(posixPath));
     expect(source.uri, uri);
     expect(source.exists(), exists);
     // If enabled, test also "restoreAbsolute".
     if (restore) {
-      var restoredUri = resolver.restoreAbsolute(source);
-      expect(restoredUri.toString(), uri.toString());
+      expect(resolver.pathToUri(path), uri);
+      // ignore: deprecated_member_use_from_same_package
+      expect(resolver.restoreAbsolute(source), uri);
     }
     return source;
   }
diff --git a/pkg/compiler/lib/src/ir/static_type.dart b/pkg/compiler/lib/src/ir/static_type.dart
index b122154..9696343 100644
--- a/pkg/compiler/lib/src/ir/static_type.dart
+++ b/pkg/compiler/lib/src/ir/static_type.dart
@@ -1556,31 +1556,32 @@
     // for-in [iterableType] is a subtype of `Stream`.
     ir.DartType iterableType = visitNode(node.iterable);
     ir.DartType iteratorType = const ir.DynamicType();
-    if (node.isAsync) {
-      ir.InterfaceType streamInterfaceType = getInterfaceTypeOf(iterableType);
-      ir.InterfaceType streamType = typeEnvironment.getTypeAsInstanceOf(
-          streamInterfaceType,
-          typeEnvironment.coreTypes.streamClass,
-          currentLibrary,
-          typeEnvironment.coreTypes);
-      if (streamType != null) {
-        iteratorType = ir.InterfaceType(
-            typeEnvironment.coreTypes.streamIteratorClass,
-            ir.Nullability.nonNullable,
-            streamType.typeArguments);
-      }
-    } else {
-      ir.InterfaceType iterableInterfaceType = getInterfaceTypeOf(iterableType);
-      ir.Member member = hierarchy.getInterfaceMember(
-          iterableInterfaceType.classNode, ir.Name(Identifiers.iterator));
-      if (member != null) {
-        iteratorType = ir.Substitution.fromInterfaceType(
-                typeEnvironment.getTypeAsInstanceOf(
-                    iterableInterfaceType,
-                    member.enclosingClass,
-                    currentLibrary,
-                    typeEnvironment.coreTypes))
-            .substituteType(member.getterType);
+    ir.InterfaceType iterableInterfaceType = getInterfaceTypeOf(iterableType);
+    if (iterableInterfaceType != null) {
+      if (node.isAsync) {
+        ir.InterfaceType streamType = typeEnvironment.getTypeAsInstanceOf(
+            iterableInterfaceType,
+            typeEnvironment.coreTypes.streamClass,
+            currentLibrary,
+            typeEnvironment.coreTypes);
+        if (streamType != null) {
+          iteratorType = ir.InterfaceType(
+              typeEnvironment.coreTypes.streamIteratorClass,
+              ir.Nullability.nonNullable,
+              streamType.typeArguments);
+        }
+      } else {
+        ir.Member member = hierarchy.getInterfaceMember(
+            iterableInterfaceType.classNode, ir.Name(Identifiers.iterator));
+        if (member != null) {
+          iteratorType = ir.Substitution.fromInterfaceType(
+                  typeEnvironment.getTypeAsInstanceOf(
+                      iterableInterfaceType,
+                      member.enclosingClass,
+                      currentLibrary,
+                      typeEnvironment.coreTypes))
+              .substituteType(member.getterType);
+        }
       }
     }
     _staticTypeCache._forInIteratorTypes[node] = iteratorType;
diff --git a/pkg/dds/CHANGELOG.md b/pkg/dds/CHANGELOG.md
index cccc7a8..55fbb5e 100644
--- a/pkg/dds/CHANGELOG.md
+++ b/pkg/dds/CHANGELOG.md
@@ -1,3 +1,7 @@
+# 2.1.5
+- Update to new CpuSamplesEvent format for CPU sample caching for improved
+  performance.
+
 # 2.1.4
 - A new library `package:dds/dap.dart` exposes classes required to build a custom DAP
   debug-adapter on top of the base Dart DAP functionality in DDS.
diff --git a/pkg/dds/lib/src/cpu_samples_manager.dart b/pkg/dds/lib/src/cpu_samples_manager.dart
index a0c413a..108da99 100644
--- a/pkg/dds/lib/src/cpu_samples_manager.dart
+++ b/pkg/dds/lib/src/cpu_samples_manager.dart
@@ -52,21 +52,31 @@
     int bufferSize = 1000000,
   ]) : super(bufferSize);
 
-  void cacheSamples(CpuSamples samples) {
-    String getFunctionId(ProfileFunction function) {
-      final functionObject = function.function;
-      if (functionObject is NativeFunction) {
-        return 'native/${functionObject.name}';
-      }
-      return functionObject.id!;
-    }
+  ProfileFunction _buildProfileFunction(dynamic function) {
+    // `kind` and `resolvedUrl` are populated in `populateFunctionDetails()`.
+    return ProfileFunction(
+      kind: '',
+      inclusiveTicks: -1,
+      exclusiveTicks: -1,
+      resolvedUrl: '',
+      function: function,
+    );
+  }
 
+  String _getFunctionId(dynamic function) {
+    if (function is NativeFunction) {
+      return 'native/${function.name}';
+    }
+    return function.id!;
+  }
+
+  void cacheSamples(CpuSamplesEvent samples) {
     // Initialize upon seeing our first samples.
     if (functions.isEmpty) {
       samplePeriod = samples.samplePeriod!;
       maxStackDepth = samples.maxStackDepth!;
       pid = samples.pid!;
-      functions.addAll(samples.functions!);
+      functions.addAll(samples.functions!.map(_buildProfileFunction));
 
       // Build the initial id to function index mapping. This allows for us to
       // lookup a ProfileFunction in the global function list stored in this
@@ -77,7 +87,7 @@
       // TODO(bkonyi): investigate creating some form of stable ID for
       // Functions tied to closures.
       for (int i = 0; i < functions.length; ++i) {
-        idToFunctionIndex[getFunctionId(functions[i])] = i;
+        idToFunctionIndex[_getFunctionId(functions[i].function)] = i;
       }
 
       // Clear tick information as we'll need to recalculate these values later
@@ -94,14 +104,14 @@
 
       // Check to see if we've got a function object we've never seen before.
       for (int i = 0; i < newFunctions.length; ++i) {
-        final key = getFunctionId(newFunctions[i]);
+        final key = _getFunctionId(newFunctions[i]);
         if (!idToFunctionIndex.containsKey(key)) {
           idToFunctionIndex[key] = functions.length;
           // Keep track of the original index and the location of the function
           // in the master function list so we can update the function indicies
           // for each sample in this batch.
           indexMapping[i] = functions.length;
-          functions.add(newFunctions[i]);
+          functions.add(_buildProfileFunction(newFunctions[i]));
 
           // Reset tick state as we'll recalculate later.
           functions.last.inclusiveTicks = 0;
@@ -159,6 +169,28 @@
     return evicted;
   }
 
+  Future<void> populateFunctionDetails(
+      DartDevelopmentServiceImpl dds, String isolateId) async {
+    final cpuSamples = await dds.vmServiceClient.sendRequest('getCpuSamples', {
+      'isolateId': isolateId,
+      'timeOriginMicros': 0,
+      'timeExtentMicros': 0,
+    });
+    final fullFunctions = cpuSamples['functions'];
+    for (final func in fullFunctions) {
+      final profileFunc = ProfileFunction.parse(func)!;
+      final id = _getFunctionId(profileFunc.function!);
+      final index = idToFunctionIndex[id];
+      if (index == null) {
+        continue;
+      }
+      final result = functions[index];
+      result.kind = profileFunc.kind;
+      result.resolvedUrl = profileFunc.resolvedUrl;
+      result.function = profileFunc.function;
+    }
+  }
+
   Map<String, dynamic> toJson() {
     return {
       'type': 'CachedCpuSamples',
diff --git a/pkg/dds/lib/src/isolate_manager.dart b/pkg/dds/lib/src/isolate_manager.dart
index 7522183..7808b2a 100644
--- a/pkg/dds/lib/src/isolate_manager.dart
+++ b/pkg/dds/lib/src/isolate_manager.dart
@@ -110,13 +110,14 @@
   /// Should always be called after an isolate is resumed.
   void clearResumeApprovals() => _resumeApprovalsByName.clear();
 
-  Map<String, dynamic> getCachedCpuSamples(String userTag) {
+  Future<Map<String, dynamic>> getCachedCpuSamples(String userTag) async {
     final repo = cpuSamplesManager.cpuSamplesCaches[userTag];
     if (repo == null) {
       throw json_rpc.RpcException.invalidParams(
         'CPU sample caching is not enabled for tag: "$userTag"',
       );
     }
+    await repo.populateFunctionDetails(isolateManager.dds, id);
     return repo.toJson();
   }
 
@@ -283,14 +284,15 @@
     );
   }
 
-  Map<String, dynamic> getCachedCpuSamples(json_rpc.Parameters parameters) {
+  Future<Map<String, dynamic>> getCachedCpuSamples(
+      json_rpc.Parameters parameters) async {
     final isolateId = parameters['isolateId'].asString;
     if (!isolates.containsKey(isolateId)) {
       return RPCResponses.collectedSentinel;
     }
     final isolate = isolates[isolateId]!;
     final userTag = parameters['userTag'].asString;
-    return isolate.getCachedCpuSamples(userTag);
+    return await isolate.getCachedCpuSamples(userTag);
   }
 
   /// Forwards a `resume` request to the VM service.
diff --git a/pkg/dds/pubspec.yaml b/pkg/dds/pubspec.yaml
index e575eae..e323748 100644
--- a/pkg/dds/pubspec.yaml
+++ b/pkg/dds/pubspec.yaml
@@ -3,7 +3,7 @@
   A library used to spawn the Dart Developer Service, used to communicate with
   a Dart VM Service instance.
 
-version: 2.1.4
+version: 2.1.5
 
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/dds
 
diff --git a/pkg/dds/test/get_cached_cpu_samples_test.dart b/pkg/dds/test/get_cached_cpu_samples_test.dart
index 77d2bdb..d8e900e 100644
--- a/pkg/dds/test/get_cached_cpu_samples_test.dart
+++ b/pkg/dds/test/get_cached_cpu_samples_test.dart
@@ -42,8 +42,15 @@
       final availableCaches = await service.getAvailableCachedCpuSamples();
       expect(availableCaches.cacheNames.length, 0);
 
-      final isolate = (await service.getVM()).isolates!.first;
-
+      IsolateRef isolate;
+      while (true) {
+        final vm = await service.getVM();
+        if (vm.isolates!.isNotEmpty) {
+          isolate = vm.isolates!.first;
+          break;
+        }
+        await Future.delayed(const Duration(seconds: 1));
+      }
       try {
         await service.getCachedCpuSamples(isolate.id!, 'Fake');
         fail('Invalid userTag did not cause an exception');
@@ -73,7 +80,18 @@
       expect(availableCaches.cacheNames.length, 1);
       expect(availableCaches.cacheNames.first, kUserTag);
 
-      final isolate = (await service.getVM()).isolates!.first;
+      IsolateRef isolate;
+      while (true) {
+        final vm = await service.getVM();
+        if (vm.isolates!.isNotEmpty) {
+          isolate = vm.isolates!.first;
+          isolate = await service.getIsolate(isolate.id!);
+          if ((isolate as Isolate).runnable!) {
+            break;
+          }
+        }
+        await Future.delayed(const Duration(seconds: 1));
+      }
 
       final completer = Completer<void>();
       int i = 0;
diff --git a/pkg/vm_service/CHANGELOG.md b/pkg/vm_service/CHANGELOG.md
index 760c9d9..16605c0 100644
--- a/pkg/vm_service/CHANGELOG.md
+++ b/pkg/vm_service/CHANGELOG.md
@@ -1,5 +1,10 @@
 # Changelog
 
+## 8.0.0
+- *breaking* Updated type of `Event.cpuSamples` from `CpuSamples` to
+  `CpuSamplesEvent`, which is less expensive to generate and serialize.
+- Added `CpuSamplesEvent` object.
+
 ## 7.5.0
 - Update to version `3.53` of the spec.
 - Added `setIsolatePauseMode` RPC.
diff --git a/pkg/vm_service/example/vm_service_assert.dart b/pkg/vm_service/example/vm_service_assert.dart
index f30e794..aa1b7c7 100644
--- a/pkg/vm_service/example/vm_service_assert.dart
+++ b/pkg/vm_service/example/vm_service_assert.dart
@@ -453,6 +453,20 @@
   return obj;
 }
 
+vms.CpuSamplesEvent assertCpuSamplesEvent(vms.CpuSamplesEvent obj) {
+  assertNotNull(obj);
+  assertInt(obj.samplePeriod!);
+  assertInt(obj.maxStackDepth!);
+  assertInt(obj.sampleCount!);
+  assertInt(obj.timeSpan!);
+  assertInt(obj.timeOriginMicros!);
+  assertInt(obj.timeExtentMicros!);
+  assertInt(obj.pid!);
+  assertListOfDynamic(obj.functions!);
+  assertListOfCpuSample(obj.samples!);
+  return obj;
+}
+
 vms.CpuSample assertCpuSample(vms.CpuSample obj) {
   assertNotNull(obj);
   assertInt(obj.tid!);
diff --git a/pkg/vm_service/java/.gitignore b/pkg/vm_service/java/.gitignore
index a54ba8c..5fe823e 100644
--- a/pkg/vm_service/java/.gitignore
+++ b/pkg/vm_service/java/.gitignore
@@ -63,6 +63,7 @@
 src/org/dartlang/vm/service/element/ContextRef.java
 src/org/dartlang/vm/service/element/CpuSample.java
 src/org/dartlang/vm/service/element/CpuSamples.java
+src/org/dartlang/vm/service/element/CpuSamplesEvent.java
 src/org/dartlang/vm/service/element/ErrorKind.java
 src/org/dartlang/vm/service/element/ErrorObj.java
 src/org/dartlang/vm/service/element/ErrorRef.java
diff --git a/pkg/vm_service/java/version.properties b/pkg/vm_service/java/version.properties
index b8e9261a..84daa3b 100644
--- a/pkg/vm_service/java/version.properties
+++ b/pkg/vm_service/java/version.properties
@@ -1 +1 @@
-version=3.53
+version=3.54
diff --git a/pkg/vm_service/lib/src/vm_service.dart b/pkg/vm_service/lib/src/vm_service.dart
index 278e6cf..fcdfe9b 100644
--- a/pkg/vm_service/lib/src/vm_service.dart
+++ b/pkg/vm_service/lib/src/vm_service.dart
@@ -26,7 +26,7 @@
         HeapSnapshotObjectNoData,
         HeapSnapshotObjectNullData;
 
-const String vmServiceVersion = '3.53.0';
+const String vmServiceVersion = '3.54.0';
 
 /// @optional
 const String optional = 'optional';
@@ -123,6 +123,7 @@
   'Context': Context.parse,
   'ContextElement': ContextElement.parse,
   'CpuSamples': CpuSamples.parse,
+  'CpuSamplesEvent': CpuSamplesEvent.parse,
   'CpuSample': CpuSample.parse,
   '@Error': ErrorRef.parse,
   'Error': Error.parse,
@@ -3658,6 +3659,92 @@
   String toString() => '[CpuSamples]';
 }
 
+class CpuSamplesEvent {
+  static CpuSamplesEvent? parse(Map<String, dynamic>? json) =>
+      json == null ? null : CpuSamplesEvent._fromJson(json);
+
+  /// The sampling rate for the profiler in microseconds.
+  int? samplePeriod;
+
+  /// The maximum possible stack depth for samples.
+  int? maxStackDepth;
+
+  /// The number of samples returned.
+  int? sampleCount;
+
+  /// The timespan the set of returned samples covers, in microseconds
+  /// (deprecated).
+  ///
+  /// Note: this property is deprecated and will always return -1. Use
+  /// `timeExtentMicros` instead.
+  int? timeSpan;
+
+  /// The start of the period of time in which the returned samples were
+  /// collected.
+  int? timeOriginMicros;
+
+  /// The duration of time covered by the returned samples.
+  int? timeExtentMicros;
+
+  /// The process ID for the VM.
+  int? pid;
+
+  /// A list of references to functions seen in the relevant samples. These
+  /// references can be looked up using the indicies provided in a `CpuSample`
+  /// `stack` to determine which function was on the stack.
+  List<dynamic>? functions;
+
+  /// A list of samples collected in the range `[timeOriginMicros,
+  /// timeOriginMicros + timeExtentMicros]`
+  List<CpuSample>? samples;
+
+  CpuSamplesEvent({
+    required this.samplePeriod,
+    required this.maxStackDepth,
+    required this.sampleCount,
+    required this.timeSpan,
+    required this.timeOriginMicros,
+    required this.timeExtentMicros,
+    required this.pid,
+    required this.functions,
+    required this.samples,
+  });
+
+  CpuSamplesEvent._fromJson(Map<String, dynamic> json) {
+    samplePeriod = json['samplePeriod'] ?? -1;
+    maxStackDepth = json['maxStackDepth'] ?? -1;
+    sampleCount = json['sampleCount'] ?? -1;
+    timeSpan = json['timeSpan'] ?? -1;
+    timeOriginMicros = json['timeOriginMicros'] ?? -1;
+    timeExtentMicros = json['timeExtentMicros'] ?? -1;
+    pid = json['pid'] ?? -1;
+    functions = List<dynamic>.from(
+        createServiceObject(json['functions'], const ['dynamic']) as List? ??
+            []);
+    samples = List<CpuSample>.from(
+        createServiceObject(json['samples'], const ['CpuSample']) as List? ??
+            []);
+  }
+
+  Map<String, dynamic> toJson() {
+    final json = <String, dynamic>{};
+    json.addAll({
+      'samplePeriod': samplePeriod,
+      'maxStackDepth': maxStackDepth,
+      'sampleCount': sampleCount,
+      'timeSpan': timeSpan,
+      'timeOriginMicros': timeOriginMicros,
+      'timeExtentMicros': timeExtentMicros,
+      'pid': pid,
+      'functions': functions?.map((f) => f.toJson()).toList(),
+      'samples': samples?.map((f) => f.toJson()).toList(),
+    });
+    return json;
+  }
+
+  String toString() => '[CpuSamplesEvent]';
+}
+
 /// See [getCpuSamples] and [CpuSamples].
 class CpuSample {
   static CpuSample? parse(Map<String, dynamic>? json) =>
@@ -4051,7 +4138,7 @@
 
   /// A CPU profile containing recent samples.
   @optional
-  CpuSamples? cpuSamples;
+  CpuSamplesEvent? cpuSamples;
 
   /// Binary data associated with the event.
   ///
@@ -4132,8 +4219,9 @@
     last = json['last'];
     updatedTag = json['updatedTag'];
     previousTag = json['previousTag'];
-    cpuSamples = createServiceObject(json['cpuSamples'], const ['CpuSamples'])
-        as CpuSamples?;
+    cpuSamples =
+        createServiceObject(json['cpuSamples'], const ['CpuSamplesEvent'])
+            as CpuSamplesEvent?;
     data = json['data'];
   }
 
diff --git a/pkg/vm_service/pubspec.yaml b/pkg/vm_service/pubspec.yaml
index 8bd5ed5..d613713 100644
--- a/pkg/vm_service/pubspec.yaml
+++ b/pkg/vm_service/pubspec.yaml
@@ -3,7 +3,7 @@
   A library to communicate with a service implementing the Dart VM
   service protocol.
 
-version: 7.5.0
+version: 8.0.0
 
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/vm_service
 
diff --git a/pkg/vm_service/test/common/test_helper.dart b/pkg/vm_service/test/common/test_helper.dart
index 8bf30b8..e16e307d 100644
--- a/pkg/vm_service/test/common/test_helper.dart
+++ b/pkg/vm_service/test/common/test_helper.dart
@@ -277,6 +277,7 @@
         vm = await vmServiceConnectUri(serviceWebsocketAddress);
         print('Done loading VM');
         isolate = await getFirstIsolate(vm);
+        print('Got first isolate');
       });
     });
 
@@ -342,6 +343,8 @@
     });
     await service.streamListen(EventStreams.kIsolate);
 
+    await service.streamListen(EventStreams.kIsolate);
+
     // The isolate may have started before we subscribed.
     vm = await service.getVM();
     if (vmIsolates.isNotEmpty) {
diff --git a/pkg/vm_service/tool/dart/generate_dart.dart b/pkg/vm_service/tool/dart/generate_dart.dart
index 4f2eb16..d59b1e7 100644
--- a/pkg/vm_service/tool/dart/generate_dart.dart
+++ b/pkg/vm_service/tool/dart/generate_dart.dart
@@ -935,6 +935,10 @@
   return obj;
 }
 
+List<dynamic> assertListOfDynamic(List<dynamic> list) {
+  return list;
+}
+
 List<int> assertListOfInt(List<int> list) {
   for (int elem in list) {
     assertInt(elem);
diff --git a/runtime/observatory/tests/service/get_object_rpc_test.dart b/runtime/observatory/tests/service/get_object_rpc_test.dart
index d553138..a1f0582 100644
--- a/runtime/observatory/tests/service/get_object_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_object_rpc_test.dart
@@ -14,8 +14,12 @@
 class _DummyClass {
   static var dummyVar = 11;
   final List<String> dummyList = new List<String>.filled(20, '');
+  static var dummyVarWithInit = foo();
+  late String dummyLateVarWithInit = 'bar';
+  late String dummyLateVar;
   void dummyFunction(int a, [bool b = false]) {}
   void dummyGenericFunction<K, V>(K a, {required V param}) {}
+  static List foo() => List<String>.filled(20, '');
 }
 
 class _DummySubClass extends _DummyClass {}
@@ -982,6 +986,83 @@
     expect(result['_guardLength'], isNotNull);
   },
 
+  // static field initializer
+  (Isolate isolate) async {
+    // Call eval to get a class id.
+    var evalResult = await invoke(isolate, 'getDummyClass');
+    var id = "${evalResult['class']['id']}/field_inits/dummyVarWithInit";
+    var params = {
+      'objectId': id,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Function'));
+    expect(result['id'], equals(id));
+    expect(result['name'], equals('dummyVarWithInit'));
+    expect(result['_kind'], equals('FieldInitializer'));
+    expect(result['static'], equals(true));
+    expect(result['const'], equals(false));
+    expect(result['implicit'], equals(false));
+    expect(result['signature']['typeParameters'], isNull);
+    expect(result['signature']['returnType'], isNotNull);
+    expect(result['signature']['parameters'].length, 0);
+    expect(result['location']['type'], equals('SourceLocation'));
+    expect(result['code']['type'], equals('@Code'));
+    expect(result['_optimizable'], equals(true));
+    expect(result['_inlinable'], equals(false));
+    expect(result['_usageCounter'], isZero);
+    expect(result['_optimizedCallSiteCount'], isZero);
+    expect(result['_deoptimizations'], isZero);
+  },
+
+  // late field initializer
+  (Isolate isolate) async {
+    // Call eval to get a class id.
+    var evalResult = await invoke(isolate, 'getDummyClass');
+    var id = "${evalResult['class']['id']}/field_inits/dummyLateVarWithInit";
+    var params = {
+      'objectId': id,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Function'));
+    expect(result['id'], equals(id));
+    expect(result['name'], equals('dummyLateVarWithInit'));
+    expect(result['_kind'], equals('FieldInitializer'));
+    expect(result['static'], equals(false));
+    expect(result['const'], equals(false));
+    expect(result['implicit'], equals(false));
+    expect(result['signature']['typeParameters'], isNull);
+    expect(result['signature']['returnType'], isNotNull);
+    expect(result['signature']['parameters'].length, 1);
+    expect(result['location']['type'], equals('SourceLocation'));
+    expect(result['code']['type'], equals('@Code'));
+    expect(result['_optimizable'], equals(true));
+    expect(result['_inlinable'], equals(false));
+    expect(result['_usageCounter'], isZero);
+    expect(result['_optimizedCallSiteCount'], isZero);
+    expect(result['_deoptimizations'], isZero);
+  },
+
+  // invalid late field initialize.
+  (Isolate isolate) async {
+    // Call eval to get a class id.
+    var evalResult = await invoke(isolate, 'getDummyClass');
+    var id = "${evalResult['class']['id']}/field_inits/dummyLateVar";
+    var params = {
+      'objectId': id,
+    };
+    bool caughtException = false;
+    try {
+      await isolate.invokeRpcNoUpgrade('getObject', params);
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kInvalidParams));
+      expect(
+          e.message, startsWith("getObject: invalid 'objectId' parameter: "));
+    }
+    expect(caughtException, isTrue);
+  },
+
   // field with guards
   (Isolate isolate) async {
     var result = await isolate.vm.invokeRpcNoUpgrade('getFlagList', {});
diff --git a/runtime/observatory/tests/service/get_version_rpc_test.dart b/runtime/observatory/tests/service/get_version_rpc_test.dart
index 7b1f275..59afe6d 100644
--- a/runtime/observatory/tests/service/get_version_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_version_rpc_test.dart
@@ -12,7 +12,7 @@
     final result = await vm.invokeRpcNoUpgrade('getVersion', {});
     expect(result['type'], 'Version');
     expect(result['major'], 3);
-    expect(result['minor'], 53);
+    expect(result['minor'], 54);
     expect(result['_privateMajor'], 0);
     expect(result['_privateMinor'], 0);
   },
diff --git a/runtime/observatory_2/tests/service_2/get_object_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_object_rpc_test.dart
index 44912b3..9c45a9e 100644
--- a/runtime/observatory_2/tests/service_2/get_object_rpc_test.dart
+++ b/runtime/observatory_2/tests/service_2/get_object_rpc_test.dart
@@ -14,8 +14,10 @@
 class _DummyClass {
   static var dummyVar = 11;
   final List<String> dummyList = new List<String>.filled(20, null);
+  static var dummyVarWithInit = foo();
   void dummyFunction(int a, [bool b = false]) {}
   void dummyGenericFunction<K, V>(K a, {V param}) {}
+  static List foo() => List<String>.filled(20, '');
 }
 
 class _DummySubClass extends _DummyClass {}
@@ -1014,6 +1016,34 @@
     expect(result['_guardLength'], equals('20'));
   },
 
+  // static field initializer
+  (Isolate isolate) async {
+    // Call eval to get a class id.
+    var evalResult = await invoke(isolate, 'getDummyClass');
+    var id = "${evalResult['class']['id']}/field_inits/dummyVarWithInit";
+    var params = {
+      'objectId': id,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Function'));
+    expect(result['id'], equals(id));
+    expect(result['name'], equals('dummyVarWithInit'));
+    expect(result['_kind'], equals('FieldInitializer'));
+    expect(result['static'], equals(true));
+    expect(result['const'], equals(false));
+    expect(result['implicit'], equals(false));
+    expect(result['signature']['typeParameters'], isNull);
+    expect(result['signature']['returnType'], isNotNull);
+    expect(result['signature']['parameters'].length, 0);
+    expect(result['location']['type'], equals('SourceLocation'));
+    expect(result['code']['type'], equals('@Code'));
+    expect(result['_optimizable'], equals(true));
+    expect(result['_inlinable'], equals(false));
+    expect(result['_usageCounter'], isZero);
+    expect(result['_optimizedCallSiteCount'], isZero);
+    expect(result['_deoptimizations'], isZero);
+  },
+
   // invalid field.
   (Isolate isolate) async {
     // Call eval to get a class id.
diff --git a/runtime/observatory_2/tests/service_2/get_version_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_version_rpc_test.dart
index e9c87c9..4daa57a 100644
--- a/runtime/observatory_2/tests/service_2/get_version_rpc_test.dart
+++ b/runtime/observatory_2/tests/service_2/get_version_rpc_test.dart
@@ -12,7 +12,7 @@
     final result = await vm.invokeRpcNoUpgrade('getVersion', {});
     expect(result['type'], equals('Version'));
     expect(result['major'], equals(3));
-    expect(result['minor'], equals(53));
+    expect(result['minor'], equals(54));
     expect(result['_privateMajor'], equals(0));
     expect(result['_privateMinor'], equals(0));
   },
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 71924f7..503be80 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -3702,6 +3702,9 @@
   //   element 2 * i + 1 is coverage hit (zero meaning code was not hit)
   ArrayPtr GetCoverageArray() const;
 
+  // Outputs this function's service ID to the provided JSON object.
+  void AddFunctionServiceId(const JSONObject& obj) const;
+
   // Sets deopt reason in all ICData-s with given deopt_id.
   void SetDeoptReasonForAll(intptr_t deopt_id, ICData::DeoptReasonId reason);
 
diff --git a/runtime/vm/object_service.cc b/runtime/vm/object_service.cc
index f7fe7fe..42ab457 100644
--- a/runtime/vm/object_service.cc
+++ b/runtime/vm/object_service.cc
@@ -248,22 +248,36 @@
   Object::PrintJSONImpl(stream, ref);
 }
 
-static void AddFunctionServiceId(const JSONObject& jsobj,
-                                 const Function& f,
-                                 const Class& cls) {
-  ASSERT(!cls.IsNull());
+void Function::AddFunctionServiceId(const JSONObject& jsobj) const {
+  Class& cls = Class::Handle(Owner());
   // Special kinds of functions use indices in their respective lists.
   intptr_t id = -1;
   const char* selector = NULL;
-  if (f.IsNonImplicitClosureFunction()) {
-    id = ClosureFunctionsCache::FindClosureIndex(f);
+  // Regular functions known to their owner use their name (percent-encoded).
+  String& name = String::Handle(this->name());
+
+  if (IsNonImplicitClosureFunction()) {
+    id = ClosureFunctionsCache::FindClosureIndex(*this);
     selector = "closures";
-  } else if (f.IsImplicitClosureFunction()) {
-    id = cls.FindImplicitClosureFunctionIndex(f);
+  } else if (IsImplicitClosureFunction()) {
+    id = cls.FindImplicitClosureFunctionIndex(*this);
     selector = "implicit_closures";
-  } else if (f.IsNoSuchMethodDispatcher() || f.IsInvokeFieldDispatcher()) {
-    id = cls.FindInvocationDispatcherFunctionIndex(f);
+  } else if (IsNoSuchMethodDispatcher() || IsInvokeFieldDispatcher()) {
+    id = cls.FindInvocationDispatcherFunctionIndex(*this);
     selector = "dispatchers";
+  } else if (IsFieldInitializer()) {
+    name ^= Field::NameFromInit(name);
+    const char* encoded_field_name = String::EncodeIRI(name);
+    if (cls.IsTopLevel()) {
+      const auto& library = Library::Handle(cls.library());
+      const auto& private_key = String::Handle(library.private_key());
+      jsobj.AddFixedServiceId("libraries/%s/field_inits/%s",
+                              private_key.ToCString(), encoded_field_name);
+    } else {
+      jsobj.AddFixedServiceId("classes/%" Pd "/field_inits/%s", cls.id(),
+                              encoded_field_name);
+    }
+    return;
   }
   if (id != -1) {
     ASSERT(selector != NULL);
@@ -278,10 +292,8 @@
     }
     return;
   }
-  // Regular functions known to their owner use their name (percent-encoded).
-  String& name = String::Handle(f.name());
   Thread* thread = Thread::Current();
-  if (Resolver::ResolveFunction(thread->zone(), cls, name) == f.ptr()) {
+  if (Resolver::ResolveFunction(thread->zone(), cls, name) == ptr()) {
     const char* encoded_name = String::EncodeIRI(name);
     if (cls.IsTopLevel()) {
       const auto& library = Library::Handle(cls.library());
@@ -297,7 +309,7 @@
   // Oddball functions (not known to their owner) fall back to use the object
   // id ring. Current known examples are signature functions of closures
   // and stubs like 'megamorphic_call_miss'.
-  jsobj.AddServiceId(f);
+  jsobj.AddServiceId(*this);
 }
 
 void Function::PrintJSONImpl(JSONStream* stream, bool ref) const {
@@ -308,7 +320,7 @@
   ASSERT(err.IsNull());
   JSONObject jsobj(stream);
   AddCommonObjectProperties(&jsobj, "Function", ref);
-  AddFunctionServiceId(jsobj, *this, cls);
+  AddFunctionServiceId(jsobj);
   const char* user_name = UserVisibleNameCString();
   const String& vm_name = String::Handle(name());
   AddNameProperties(&jsobj, user_name, vm_name.ToCString());
diff --git a/runtime/vm/profiler.cc b/runtime/vm/profiler.cc
index 58cab0e..f1f721b 100644
--- a/runtime/vm/profiler.cc
+++ b/runtime/vm/profiler.cc
@@ -250,7 +250,6 @@
 ProcessedSampleBuffer* SampleBlockListProcessor::BuildProcessedSampleBuffer(
     SampleFilter* filter,
     ProcessedSampleBuffer* buffer) {
-  ASSERT(filter != NULL);
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
 
@@ -267,7 +266,6 @@
 ProcessedSampleBuffer* SampleBlockBuffer::BuildProcessedSampleBuffer(
     SampleFilter* filter,
     ProcessedSampleBuffer* buffer) {
-  ASSERT(filter != NULL);
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
 
diff --git a/runtime/vm/profiler_service.cc b/runtime/vm/profiler_service.cc
index c82c0f9..1ae3a46 100644
--- a/runtime/vm/profiler_service.cc
+++ b/runtime/vm/profiler_service.cc
@@ -170,7 +170,19 @@
   func->AddProperty("_kind", KindToCString(kind()));
 }
 
-void ProfileFunction::PrintToJSONArray(JSONArray* functions) {
+void ProfileFunction::PrintToJSONArray(JSONArray* functions,
+                                       bool print_only_ids) {
+  if (print_only_ids) {
+    JSONObject obj(functions);
+    if (kind() == kDartFunction) {
+      ASSERT(!function_.IsNull());
+      obj.AddProperty("type", "@Object");
+      function_.AddFunctionServiceId(obj);
+    } else {
+      PrintToJSONObject(&obj);
+    }
+    return;
+  }
   JSONObject obj(functions);
   obj.AddProperty("type", "ProfileFunction");
   obj.AddProperty("kind", KindToCString(kind()));
@@ -1728,10 +1740,16 @@
   PrintProfileJSON(&obj, include_code_samples);
 }
 
-void Profile::PrintProfileJSON(JSONObject* obj, bool include_code_samples) {
+void Profile::PrintProfileJSON(JSONObject* obj,
+                               bool include_code_samples,
+                               bool is_event) {
   ScopeTimer sw("Profile::PrintProfileJSON", FLAG_trace_profiler);
   Thread* thread = Thread::Current();
-  obj->AddProperty("type", "CpuSamples");
+  if (is_event) {
+    obj->AddProperty("type", "CpuSamplesEvent");
+  } else {
+    obj->AddProperty("type", "CpuSamples");
+  }
   PrintHeaderJSON(obj);
   if (include_code_samples) {
     JSONArray codes(obj, "_codes");
@@ -1760,7 +1778,7 @@
     for (intptr_t i = 0; i < functions_->length(); i++) {
       ProfileFunction* function = functions_->At(i);
       ASSERT(function != NULL);
-      function->PrintToJSONArray(&functions);
+      function->PrintToJSONArray(&functions, is_event);
       thread->CheckForSafepoint();
     }
   }
diff --git a/runtime/vm/profiler_service.h b/runtime/vm/profiler_service.h
index a9df89b..13a29fa 100644
--- a/runtime/vm/profiler_service.h
+++ b/runtime/vm/profiler_service.h
@@ -171,7 +171,7 @@
 
   static const char* KindToCString(Kind kind);
 
-  void PrintToJSONArray(JSONArray* functions);
+  void PrintToJSONArray(JSONArray* functions, bool print_only_ids = false);
 
   // Returns true if the call was successful and |pfsp| is set.
   bool GetSinglePosition(ProfileFunctionSourcePosition* pfsp);
@@ -385,7 +385,9 @@
   ProfileCode* GetCodeFromPC(uword pc, int64_t timestamp);
 
   void PrintProfileJSON(JSONStream* stream, bool include_code_samples);
-  void PrintProfileJSON(JSONObject* obj, bool include_code_samples);
+  void PrintProfileJSON(JSONObject* obj,
+                        bool include_code_samples,
+                        bool is_event = false);
 
   ProfileFunction* FindFunction(const Function& function);
 
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index dba5444..209387d 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -1791,6 +1791,18 @@
     }
     return field.ptr();
   }
+  if (strcmp(parts[2], "field_inits") == 0) {
+    // Field initializer ids look like: "classes/17/field_inits/name"
+    const auto& field = Field::Handle(klass.LookupField(id));
+    if (field.IsNull() || (field.is_late() && !field.has_initializer())) {
+      return Object::sentinel().ptr();
+    }
+    const auto& function = Function::Handle(field.EnsureInitializerFunction());
+    if (function.IsNull()) {
+      return Object::sentinel().ptr();
+    }
+    return function.ptr();
+  }
   if (strcmp(parts[2], "functions") == 0) {
     // Function ids look like: "classes/17/functions/name"
 
@@ -1883,6 +1895,10 @@
     // Library field ids look like: "libraries/17/fields/name"
     return LookupClassMembers(Thread::Current(), klass, parts, num_parts);
   }
+  if (strcmp(parts[2], "field_inits") == 0) {
+    // Library field ids look like: "libraries/17/field_inits/name"
+    return LookupClassMembers(Thread::Current(), klass, parts, num_parts);
+  }
   if (strcmp(parts[2], "functions") == 0) {
     // Library function ids look like: "libraries/17/functions/name"
     return LookupClassMembers(Thread::Current(), klass, parts, num_parts);
@@ -1952,6 +1968,9 @@
   if (strcmp(parts[2], "closures") == 0) {
     // Closure ids look like: "classes/17/closures/11"
     return LookupClassMembers(thread, cls, parts, num_parts);
+  } else if (strcmp(parts[2], "field_inits") == 0) {
+    // Field initializer ids look like: "classes/17/field_inits/name"
+    return LookupClassMembers(thread, cls, parts, num_parts);
   } else if (strcmp(parts[2], "fields") == 0) {
     // Field ids look like: "classes/17/fields/name"
     return LookupClassMembers(thread, cls, parts, num_parts);
diff --git a/runtime/vm/service.h b/runtime/vm/service.h
index 59e9e24..c31b191 100644
--- a/runtime/vm/service.h
+++ b/runtime/vm/service.h
@@ -15,7 +15,7 @@
 namespace dart {
 
 #define SERVICE_PROTOCOL_MAJOR_VERSION 3
-#define SERVICE_PROTOCOL_MINOR_VERSION 53
+#define SERVICE_PROTOCOL_MINOR_VERSION 54
 
 class Array;
 class EmbedderServiceHandler;
diff --git a/runtime/vm/service/service.md b/runtime/vm/service/service.md
index 0865d15..6a04228 100644
--- a/runtime/vm/service/service.md
+++ b/runtime/vm/service/service.md
@@ -1,8 +1,8 @@
-# Dart VM Service Protocol 3.53
+# Dart VM Service Protocol 3.54
 
 > Please post feedback to the [observatory-discuss group][discuss-list]
 
-This document describes of _version 3.53_ of the Dart VM Service Protocol. This
+This document describes of _version 3.54_ of the Dart VM Service Protocol. This
 protocol is used to communicate with a running Dart Virtual Machine.
 
 To use the Service Protocol, start the VM with the *--observe* flag.
@@ -1987,6 +1987,46 @@
 
 See [getCpuSamples](#getcpusamples) and [CpuSample](#cpusample).
 
+### CpuSamplesEvent
+
+```
+class CpuSamplesEvent {
+  // The sampling rate for the profiler in microseconds.
+  int samplePeriod;
+
+  // The maximum possible stack depth for samples.
+  int maxStackDepth;
+
+  // The number of samples returned.
+  int sampleCount;
+
+  // The timespan the set of returned samples covers, in microseconds (deprecated).
+  //
+  // Note: this property is deprecated and will always return -1. Use `timeExtentMicros`
+  // instead.
+  int timeSpan;
+
+  // The start of the period of time in which the returned samples were
+  // collected.
+  int timeOriginMicros;
+
+  // The duration of time covered by the returned samples.
+  int timeExtentMicros;
+
+  // The process ID for the VM.
+  int pid;
+
+  // A list of references to functions seen in the relevant samples. These references can
+  // be looked up using the indicies provided in a `CpuSample` `stack` to determine
+  // which function was on the stack.
+  (@Object|NativeFunction)[] functions;
+
+  // A list of samples collected in the range
+  // `[timeOriginMicros, timeOriginMicros + timeExtentMicros]`
+  CpuSample[] samples;
+}
+```
+
 ### CpuSample
 
 ```
@@ -2251,7 +2291,7 @@
   string previousTag [optional];
 
   // A CPU profile containing recent samples.
-  CpuSamples cpuSamples [optional];
+  CpuSamplesEvent cpuSamples [optional];
 }
 ```
 
@@ -4263,4 +4303,6 @@
 3.51 | Added optional `reportLines` parameter to `getSourceReport` RPC.
 3.52 | Added `lookupResolvedPackageUris` and `lookupPackageUris` RPCs and `UriList` type.
 3.53 | Added `setIsolatePauseMode` RPC.
+3.54 | Added `CpuSamplesEvent`, updated `cpuSamples` property on `Event` to have type `CpuSamplesEvent`.
+
 [discuss-list]: https://groups.google.com/a/dartlang.org/forum/#!forum/observatory-discuss
diff --git a/runtime/vm/service_event.cc b/runtime/vm/service_event.cc
index 85740cf..6ef5b54 100644
--- a/runtime/vm/service_event.cc
+++ b/runtime/vm/service_event.cc
@@ -307,7 +307,8 @@
 
   if (kind() == kCpuSamples) {
     JSONObject cpu_profile(&jsobj, "cpuSamples");
-    cpu_profile_->PrintProfileJSON(&cpu_profile, false);
+    cpu_profile_->PrintProfileJSON(&cpu_profile, /*include_code_samples=*/false,
+                                   /*is_event=*/true);
   }
 }
 
diff --git a/tests/web/47691_test.dart b/tests/web/47691_test.dart
new file mode 100644
index 0000000..db8ae9d
--- /dev/null
+++ b/tests/web/47691_test.dart
@@ -0,0 +1,26 @@
+// Copyright (c) 2021, 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.
+
+import 'package:expect/expect.dart';
+
+class LoadElements {
+  List call() => [];
+}
+
+class ViewModel {
+  ViewModel(this._loadElements);
+
+  final LoadElements _loadElements;
+
+  void init() {
+    final elements = _loadElements();
+    for (final element in elements) {
+      Expect.identical(element, element);
+    }
+  }
+}
+
+void main() {
+  ViewModel(LoadElements()).init();
+}
diff --git a/tests/web_2/47691_test.dart b/tests/web_2/47691_test.dart
new file mode 100644
index 0000000..7de21ae
--- /dev/null
+++ b/tests/web_2/47691_test.dart
@@ -0,0 +1,28 @@
+// Copyright (c) 2021, 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.7
+
+import 'package:expect/expect.dart';
+
+class LoadElements {
+  List call() => [];
+}
+
+class ViewModel {
+  ViewModel(this._loadElements);
+
+  final LoadElements _loadElements;
+
+  void init() {
+    final elements = _loadElements();
+    for (final element in elements) {
+      Expect.identical(element, element);
+    }
+  }
+}
+
+void main() {
+  ViewModel(LoadElements()).init();
+}
diff --git a/tools/VERSION b/tools/VERSION
index 9cc28c9..6de95d5 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 16
 PATCH 0
-PRERELEASE 27
+PRERELEASE 28
 PRERELEASE_PATCH 0
\ No newline at end of file