Version 2.15.0-263.0.dev

Merge commit '44e35b1daa9785aab04f77fa9dff287e61915ce4' into 'dev'
diff --git a/pkg/_fe_analyzer_shared/lib/src/parser/forwarding_listener.dart b/pkg/_fe_analyzer_shared/lib/src/parser/forwarding_listener.dart
index 82f970c..042507d 100644
--- a/pkg/_fe_analyzer_shared/lib/src/parser/forwarding_listener.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/parser/forwarding_listener.dart
@@ -810,8 +810,8 @@
   }
 
   @override
-  void endImplicitCreationExpression(Token token) {
-    listener?.endImplicitCreationExpression(token);
+  void endImplicitCreationExpression(Token token, Token openAngleBracket) {
+    listener?.endImplicitCreationExpression(token, openAngleBracket);
   }
 
   @override
diff --git a/pkg/_fe_analyzer_shared/lib/src/parser/listener.dart b/pkg/_fe_analyzer_shared/lib/src/parser/listener.dart
index cd83709..63a247f 100644
--- a/pkg/_fe_analyzer_shared/lib/src/parser/listener.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/parser/listener.dart
@@ -743,7 +743,7 @@
 
   void beginImplicitCreationExpression(Token token) {}
 
-  void endImplicitCreationExpression(Token token) {
+  void endImplicitCreationExpression(Token token, Token openAngleBracket) {
     logEvent("ImplicitCreationExpression");
   }
 
diff --git a/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart b/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart
index 0f466bf..cb1ef21 100644
--- a/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart
@@ -5404,7 +5404,8 @@
               Token afterPeriod = afterTypeArguments.next!;
               if (_isNewOrIdentifier(afterPeriod) &&
                   optional('(', afterPeriod.next!)) {
-                return parseImplicitCreationExpression(token, typeArg);
+                return parseImplicitCreationExpression(
+                    token, identifier.next!, typeArg);
               }
             }
           }
@@ -6057,13 +6058,13 @@
   }
 
   Token parseImplicitCreationExpression(
-      Token token, TypeParamOrArgInfo typeArg) {
-    Token begin = token;
-    listener.beginImplicitCreationExpression(token);
+      Token token, Token openAngleBracket, TypeParamOrArgInfo typeArg) {
+    Token begin = token.next!; // This is the class name.
+    listener.beginImplicitCreationExpression(begin);
     token = parseConstructorReference(
         token, ConstructorReferenceContext.Implicit, typeArg);
     token = parseConstructorInvocationArguments(token);
-    listener.endImplicitCreationExpression(begin);
+    listener.endImplicitCreationExpression(begin, openAngleBracket);
     return token;
   }
 
diff --git a/pkg/analysis_server/benchmark/benchmarks.dart b/pkg/analysis_server/benchmark/benchmarks.dart
index a92b60e..c6b391f 100644
--- a/pkg/analysis_server/benchmark/benchmarks.dart
+++ b/pkg/analysis_server/benchmark/benchmarks.dart
@@ -154,6 +154,13 @@
   String toString() => '${toJson()}';
 }
 
+/// This interface is implemented by benchmarks that need to know the location
+/// of the Flutter repository.
+abstract class FlutterBenchmark {
+  /// Must be called exactly one time.
+  set flutterRepositoryPath(String path);
+}
+
 class ListCommand extends Command {
   final List<Benchmark> benchmarks;
 
@@ -190,6 +197,8 @@
   final List<Benchmark> benchmarks;
 
   RunCommand(this.benchmarks) {
+    argParser.addOption('flutter-repository',
+        help: 'The absolute normalized path of the Flutter repository.');
     argParser.addFlag('quick',
         negatable: false,
         help: 'Run a quick version of the benchmark. This is not useful for '
@@ -220,6 +229,7 @@
 
     var benchmarkId = argResults!.rest.first;
     var repeatCount = int.parse(argResults!['repeat'] as String);
+    var flutterRepository = argResults!['flutter-repository'] as String?;
     var quick = argResults!['quick'];
     var verbose = argResults!['verbose'];
 
@@ -229,6 +239,17 @@
       exit(1);
     });
 
+    if (benchmark is FlutterBenchmark) {
+      if (flutterRepository != null) {
+        (benchmark as FlutterBenchmark).flutterRepositoryPath =
+            flutterRepository;
+      } else {
+        print('The option --flutter-repository is required to '
+            "run '$benchmarkId'.");
+        exit(1);
+      }
+    }
+
     var actualIterations = repeatCount;
     if (benchmark.maxIterations > 0) {
       actualIterations = math.min(benchmark.maxIterations, repeatCount);
diff --git a/pkg/analysis_server/benchmark/perf/flutter_analyze_benchmark.dart b/pkg/analysis_server/benchmark/perf/flutter_analyze_benchmark.dart
index 048666b..f2d157b3 100644
--- a/pkg/analysis_server/benchmark/perf/flutter_analyze_benchmark.dart
+++ b/pkg/analysis_server/benchmark/perf/flutter_analyze_benchmark.dart
@@ -40,8 +40,8 @@
 
 /// benchmarks:
 ///   - analysis-flutter-analyze
-class FlutterAnalyzeBenchmark extends Benchmark {
-  late Directory flutterDir;
+class FlutterAnalyzeBenchmark extends Benchmark implements FlutterBenchmark {
+  late final String flutterRepositoryPath;
 
   FlutterAnalyzeBenchmark()
       : super(
@@ -56,43 +56,6 @@
   int get maxIterations => 3;
 
   @override
-  bool get needsSetup => true;
-
-  @override
-  Future oneTimeCleanup() {
-    try {
-      flutterDir.deleteSync(recursive: true);
-    } on FileSystemException catch (e) {
-      print(e);
-    }
-
-    return Future.value();
-  }
-
-  @override
-  Future oneTimeSetup() async {
-    flutterDir = Directory.systemTemp.createTempSync('flutter');
-
-    // git clone https://github.com/flutter/flutter $flutterDir
-    await _runProcess('git', [
-      'clone',
-      'https://github.com/flutter/flutter',
-      path.canonicalize(flutterDir.path)
-    ]);
-
-    var flutterTool = path.join(flutterDir.path, 'bin', 'flutter');
-
-    // flutter --version
-    await _runProcess(flutterTool, ['--version'], cwd: flutterDir.path);
-
-    // flutter precache
-    await _runProcess(flutterTool, ['precache'], cwd: flutterDir.path);
-
-    // flutter update-packages
-    await _runProcess(flutterTool, ['update-packages'], cwd: flutterDir.path);
-  }
-
-  @override
   Future<BenchMarkResult> run({
     bool quick = false,
     bool verbose = false,
@@ -114,7 +77,7 @@
         '--dart-sdk',
         dartSdkPath,
       ],
-      cwd: flutterDir.path,
+      cwd: flutterRepositoryPath,
       failOnError: false,
     );
 
diff --git a/pkg/analysis_server/benchmark/perf/flutter_completion_benchmark.dart b/pkg/analysis_server/benchmark/perf/flutter_completion_benchmark.dart
index 8515385..e2bac08 100644
--- a/pkg/analysis_server/benchmark/perf/flutter_completion_benchmark.dart
+++ b/pkg/analysis_server/benchmark/perf/flutter_completion_benchmark.dart
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:async';
-import 'dart:convert';
 import 'dart:io';
 
 import 'package:path/path.dart' as path;
@@ -11,36 +10,7 @@
 import '../benchmarks.dart';
 import 'memory_tests.dart';
 
-Future<int> _runProcess(
-  String command,
-  List<String> args, {
-  String? cwd,
-  bool failOnError = true,
-}) async {
-  print('\n$command ${args.join(' ')}');
-
-  var process = await Process.start(command, args, workingDirectory: cwd);
-
-  process.stdout
-      .transform(utf8.decoder)
-      .transform(LineSplitter())
-      .listen((line) {
-    print('  $line');
-  });
-  process.stderr
-      .transform(utf8.decoder)
-      .transform(LineSplitter())
-      .listen((line) => print('  $line'));
-
-  var exitCode = await process.exitCode;
-  if (exitCode != 0 && failOnError) {
-    throw '$command exited with $exitCode';
-  }
-
-  return exitCode;
-}
-
-class FlutterCompletionBenchmark extends Benchmark {
+class FlutterCompletionBenchmark extends Benchmark implements FlutterBenchmark {
   static final das = FlutterCompletionBenchmark(
     'das',
     () => AnalysisServerBenchmarkTest(),
@@ -53,7 +23,7 @@
 
   final AbstractBenchmarkTest Function() testConstructor;
 
-  late String flutterPath;
+  late final String flutterRepositoryPath;
 
   FlutterCompletionBenchmark(String protocolName, this.testConstructor)
       : super(
@@ -66,44 +36,6 @@
   int get maxIterations => 2;
 
   @override
-  bool get needsSetup => true;
-
-  @override
-  Future oneTimeCleanup() {
-    try {
-      Directory(flutterPath).deleteSync(recursive: true);
-    } on FileSystemException catch (e) {
-      print(e);
-    }
-
-    return Future.value();
-  }
-
-  @override
-  Future oneTimeSetup() async {
-    flutterPath = Directory.systemTemp.createTempSync('flutter').path;
-
-    // git clone https://github.com/flutter/flutter $flutterPath
-    await _runProcess('git', [
-      'clone',
-      'https://github.com/flutter/flutter',
-      path.canonicalize(flutterPath)
-    ]);
-
-    var flutterTool = path.join(flutterPath, 'bin', 'flutter');
-
-    // flutter --version
-    await _runProcess(flutterTool, ['--version'], cwd: flutterPath);
-
-    // flutter update-packages
-    await _runProcess(
-      flutterTool,
-      ['pub', 'get'],
-      cwd: path.join(flutterPath, 'packages', 'flutter'),
-    );
-  }
-
-  @override
   Future<BenchMarkResult> run({
     bool quick = false,
     bool verbose = false,
@@ -117,7 +49,8 @@
       test.debugStdio();
     }
 
-    final flutterPkgPath = path.join(flutterPath, 'packages', 'flutter');
+    final flutterPkgPath =
+        path.join(flutterRepositoryPath, 'packages', 'flutter');
 
     // Open a small directory, but with the package config that allows us
     // to analyze any file in `package:flutter`, including tests.
@@ -300,7 +233,7 @@
     // Perform warm-up.
     // The cold start does not matter.
     // The sustained performance is much more important.
-    const kWarmUpCount = 50;
+    const kWarmUpCount = 20;
     for (var i = 0; i < kWarmUpCount; i++) {
       await perform();
     }
diff --git a/pkg/analysis_server/lib/src/cider/completion.dart b/pkg/analysis_server/lib/src/cider/completion.dart
index b9c27ec..17f00da 100644
--- a/pkg/analysis_server/lib/src/cider/completion.dart
+++ b/pkg/analysis_server/lib/src/cider/completion.dart
@@ -85,6 +85,7 @@
                 <IncludedSuggestionRelevanceTag>[];
 
             var manager = DartCompletionManager(
+              budget: CompletionBudget(CompletionBudget.defaultDuration),
               includedElementKinds: includedElementKinds,
               includedElementNames: includedElementNames,
               includedSuggestionRelevanceTags: includedSuggestionRelevanceTags,
diff --git a/pkg/analysis_server/lib/src/domain_completion.dart b/pkg/analysis_server/lib/src/domain_completion.dart
index 015acf4..85c3ac9 100644
--- a/pkg/analysis_server/lib/src/domain_completion.dart
+++ b/pkg/analysis_server/lib/src/domain_completion.dart
@@ -33,12 +33,12 @@
 /// Instances of the class [CompletionDomainHandler] implement a
 /// [RequestHandler] that handles requests in the completion domain.
 class CompletionDomainHandler extends AbstractRequestHandler {
-  /// The time budget for a completion request.
-  static const Duration _budgetDuration = Duration(milliseconds: 100);
-
   /// The maximum number of performance measurements to keep.
   static const int performanceListMaxLength = 50;
 
+  /// The time budget for a completion request.
+  Duration budgetDuration = CompletionBudget.defaultDuration;
+
   /// The completion services that the client is currently subscribed.
   final Set<CompletionService> subscriptions = <CompletionService>{};
 
@@ -90,6 +90,7 @@
     var suggestions = <CompletionSuggestion>[];
     await performance.runAsync('computeSuggestions', (performance) async {
       var manager = DartCompletionManager(
+        budget: budget,
         includedElementKinds: includedElementKinds,
         includedElementNames: includedElementNames,
         includedSuggestionRelevanceTags: includedSuggestionRelevanceTags,
@@ -213,7 +214,7 @@
 
   /// Implement the 'completion.getSuggestions2' request.
   void getSuggestions2(Request request) async {
-    var budget = CompletionBudget(_budgetDuration);
+    var budget = CompletionBudget(budgetDuration);
 
     var params = CompletionGetSuggestions2Params.fromRequest(request);
     var file = params.file;
@@ -337,7 +338,7 @@
 
   /// Process a `completion.getSuggestions` request.
   Future<void> processRequest(Request request) async {
-    var budget = CompletionBudget(_budgetDuration);
+    var budget = CompletionBudget(budgetDuration);
 
     final performance = this.performance = CompletionPerformance();
 
diff --git a/pkg/analysis_server/lib/src/domains/execution/completion.dart b/pkg/analysis_server/lib/src/domains/execution/completion.dart
index c130a8a..a2f6ea6 100644
--- a/pkg/analysis_server/lib/src/domains/execution/completion.dart
+++ b/pkg/analysis_server/lib/src/domains/execution/completion.dart
@@ -74,7 +74,9 @@
       offset: targetOffset,
     );
 
-    var suggestions = await DartCompletionManager().computeSuggestions(
+    var suggestions = await DartCompletionManager(
+      budget: CompletionBudget(CompletionBudget.defaultDuration),
+    ).computeSuggestions(
       dartRequest,
       OperationPerformanceImpl('<root>'),
     );
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
index 74ca47f..85f3765 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
@@ -233,6 +233,7 @@
 
       try {
         var contributor = DartCompletionManager(
+          budget: CompletionBudget(CompletionBudget.defaultDuration),
           includedElementKinds: includedElementKinds,
           includedElementNames: includedElementNames,
           includedSuggestionRelevanceTags: includedSuggestionRelevanceTags,
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart b/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
index 1b62943..4a18e4b 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
@@ -47,11 +47,17 @@
 
 /// Class that tracks how much time budget we have left.
 class CompletionBudget {
+  static const Duration defaultDuration = Duration(milliseconds: 100);
+
   final Duration _budget;
   final Stopwatch _timer = Stopwatch()..start();
 
   CompletionBudget(this._budget);
 
+  bool get isEmpty {
+    return _timer.elapsed > _budget;
+  }
+
   Duration get left {
     var result = _budget - _timer.elapsed;
     return result.isNegative ? Duration.zero : result;
@@ -61,6 +67,9 @@
 /// [DartCompletionManager] determines if a completion request is Dart specific
 /// and forwards those requests to all [DartCompletionContributor]s.
 class DartCompletionManager {
+  /// Time budget to computing suggestions.
+  final CompletionBudget budget;
+
   /// If not `null`, then instead of using [ImportedReferenceContributor],
   /// fill this set with kinds of elements that are applicable at the
   /// completion location, so should be suggested from available suggestion
@@ -91,6 +100,7 @@
   /// [includedSuggestionRelevanceTags] must either all be `null` or must all be
   /// non-`null`.
   DartCompletionManager({
+    required this.budget,
     this.includedElementKinds,
     this.includedElementNames,
     this.includedSuggestionRelevanceTags,
@@ -156,7 +166,7 @@
     final librariesToImport = this.librariesToImport;
     if (librariesToImport != null) {
       contributors.add(
-        NotImportedContributor(request, builder, librariesToImport),
+        NotImportedContributor(request, builder, budget, librariesToImport),
       );
     }
 
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/not_imported_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/not_imported_contributor.dart
index e6415d0..4fba896 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/not_imported_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/not_imported_contributor.dart
@@ -2,6 +2,8 @@
 // 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 'dart:async';
+
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
 import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
 import 'package:analysis_server/src/services/completion/dart/local_library_contributor.dart';
@@ -17,11 +19,13 @@
 
 /// A contributor of suggestions from not yet imported libraries.
 class NotImportedContributor extends DartCompletionContributor {
+  final CompletionBudget budget;
   final List<Uri> librariesToImport;
 
   NotImportedContributor(
     DartCompletionRequest request,
     SuggestionBuilder builder,
+    this.budget,
     this.librariesToImport,
   ) : super(request, builder);
 
@@ -37,10 +41,18 @@
     var fsState = analysisDriver.fsState;
     var filter = _buildFilter(fsState);
 
-    await analysisDriver.discoverAvailableFiles();
+    try {
+      await analysisDriver.discoverAvailableFiles().timeout(budget.left);
+    } on TimeoutException {
+      return;
+    }
 
     var knownFiles = fsState.knownFiles.toList();
     for (var file in knownFiles) {
+      if (budget.isEmpty) {
+        return;
+      }
+
       if (!filter.shouldInclude(file)) {
         continue;
       }
diff --git a/pkg/analysis_server/test/domain_completion_test.dart b/pkg/analysis_server/test/domain_completion_test.dart
index 4536f8b..ca7daef 100644
--- a/pkg/analysis_server/test/domain_completion_test.dart
+++ b/pkg/analysis_server/test/domain_completion_test.dart
@@ -91,6 +91,31 @@
       CrashReportingAttachmentsBuilder.empty,
       InstrumentationService.NULL_SERVICE,
     );
+
+    completionDomain.budgetDuration = const Duration(seconds: 30);
+  }
+
+  Future<void> test_notImported_emptyBudget() async {
+    await _configureWithWorkspaceRoot();
+
+    // Empty budget, so no not yet imported libraries.
+    completionDomain.budgetDuration = const Duration(milliseconds: 0);
+
+    var responseValidator = await _getTestCodeSuggestions('''
+void f() {
+  Rand^
+}
+''');
+
+    responseValidator
+      ..assertComplete()
+      ..assertReplacementBack(4)
+      ..assertLibrariesToImport(includes: [], excludes: [
+        'dart:core',
+        'dart:math',
+      ]);
+
+    responseValidator.suggestions.withElementClass().assertEmpty();
   }
 
   Future<void> test_notImported_pub_dependencies_inLib() async {
@@ -185,7 +210,7 @@
 
     await _configureWithWorkspaceRoot();
 
-    var test_path = '$testPackageTestPath/test.dart';
+    var test_path = convertPath('$testPackageTestPath/test.dart');
     var responseValidator = await _getCodeSuggestions(
       path: test_path,
       content: '''
@@ -450,7 +475,7 @@
 
     await _configureWithWorkspaceRoot();
 
-    var test_path = '$testPackageTestPath/test.dart';
+    var test_path = convertPath('$testPackageTestPath/test.dart');
     var responseValidator = await _getCodeSuggestions(
       path: test_path,
       content: '''
@@ -497,7 +522,7 @@
 
     await _configureWithWorkspaceRoot();
 
-    var test_path = '$testPackageTestPath/test.dart';
+    var test_path = convertPath('$testPackageTestPath/test.dart');
     var responseValidator = await _getCodeSuggestions(
       path: test_path,
       content: '''
@@ -981,7 +1006,7 @@
     int maxResults = 1 << 10,
   }) async {
     return _getCodeSuggestions(
-      path: testFilePath,
+      path: convertPath(testFilePath),
       content: content,
       maxResults: maxResults,
     );
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_argument_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_argument_test.dart
index 87759cd..736da8c 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_argument_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_argument_test.dart
@@ -21,7 +21,7 @@
   @override
   String get lintCode => LintNames.avoid_redundant_argument_values;
 
-  Future<void> test_independentInvocations() async {
+  Future<void> test_singleFile() async {
     await resolveTestCode('''
 void f({bool valWithDefault = true, bool val}) {}
 void f2({bool valWithDefault = true, bool val}) {}
@@ -41,23 +41,6 @@
 }
 ''');
   }
-
-  Future<void> test_multipleInSingleInvocation() async {
-    await resolveTestCode('''
-void f() {
-  g(a: 0, b: 1, c: 2);
-}
-
-void g({int a = 0, int b = 1, int c = 2}) {}
-''');
-    await assertHasFix('''
-void f() {
-  g();
-}
-
-void g({int a = 0, int b = 1, int c = 2}) {}
-''');
-  }
 }
 
 @reflectiveTest
diff --git a/pkg/analysis_server/test/stress/completion/completion_runner.dart b/pkg/analysis_server/test/stress/completion/completion_runner.dart
index f54299e..136684a 100644
--- a/pkg/analysis_server/test/stress/completion/completion_runner.dart
+++ b/pkg/analysis_server/test/stress/completion/completion_runner.dart
@@ -57,7 +57,9 @@
     var collection = AnalysisContextCollection(
         includedPaths: <String>[analysisRoot],
         resourceProvider: resourceProvider);
-    var contributor = DartCompletionManager();
+    var contributor = DartCompletionManager(
+      budget: CompletionBudget(const Duration(seconds: 30)),
+    );
     var statistics = CompletionPerformance();
     var stamp = 1;
 
diff --git a/pkg/analysis_server/tool/code_completion/completion_metrics.dart b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
index 7284e5d..c7a90fc 100644
--- a/pkg/analysis_server/tool/code_completion/completion_metrics.dart
+++ b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
@@ -1205,9 +1205,11 @@
           availableSuggestionsParams]) async {
     List<protocol.CompletionSuggestion> suggestions;
 
+    var budget = CompletionBudget(Duration(seconds: 30));
     if (declarationsTracker == null) {
       // available suggestions == false
       suggestions = await DartCompletionManager(
+        budget: budget,
         listener: listener,
       ).computeSuggestions(dartRequest, performance);
     } else {
@@ -1218,6 +1220,7 @@
           <protocol.IncludedSuggestionRelevanceTag>[];
       var includedSuggestionSetList = <protocol.IncludedSuggestionSet>[];
       suggestions = await DartCompletionManager(
+        budget: budget,
         includedElementKinds: includedElementKinds,
         includedElementNames: includedElementNames,
         includedSuggestionRelevanceTags: includedSuggestionRelevanceTagList,
diff --git a/pkg/analyzer/lib/src/fasta/ast_builder.dart b/pkg/analyzer/lib/src/fasta/ast_builder.dart
index e88c858..4a50a3c 100644
--- a/pkg/analyzer/lib/src/fasta/ast_builder.dart
+++ b/pkg/analyzer/lib/src/fasta/ast_builder.dart
@@ -1664,7 +1664,7 @@
   }
 
   @override
-  void endImplicitCreationExpression(Token token) {
+  void endImplicitCreationExpression(Token token, Token openAngleBracket) {
     debugEvent("ImplicitCreationExpression");
 
     _handleInstanceCreation(null);
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index 6f08b8b..c19ef88 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -4948,6 +4948,14 @@
     if (enableConstructorTearOffsInLibrary && inImplicitCreationContext) {
       Expression receiver = receiverFunction();
       if (typeArguments != null) {
+        if (receiver is StaticTearOff &&
+                (receiver.target.isFactory ||
+                    isTearOffLowering(receiver.target)) ||
+            receiver is ConstructorTearOff ||
+            receiver is RedirectingFactoryTearOff) {
+          return buildProblem(fasta.messageConstructorTearOffWithTypeArguments,
+              instantiationOffset, noLength);
+        }
         receiver = forest.createInstantiation(
             instantiationOffset,
             receiver,
@@ -4974,10 +4982,10 @@
   }
 
   @override
-  void endImplicitCreationExpression(Token token) {
+  void endImplicitCreationExpression(Token token, Token openAngleBracket) {
     debugEvent("ImplicitCreationExpression");
     _buildConstructorReferenceInvocation(
-        token.next!, token.offset, Constness.implicit,
+        token, openAngleBracket.offset, Constness.implicit,
         inMetadata: false, inImplicitCreationContext: true);
   }
 
diff --git a/pkg/front_end/lib/src/fasta/util/direct_parser_ast_helper.dart b/pkg/front_end/lib/src/fasta/util/direct_parser_ast_helper.dart
index ffb7439..4fedb0b 100644
--- a/pkg/front_end/lib/src/fasta/util/direct_parser_ast_helper.dart
+++ b/pkg/front_end/lib/src/fasta/util/direct_parser_ast_helper.dart
@@ -1172,11 +1172,12 @@
   }
 
   @override
-  void endImplicitCreationExpression(Token token) {
+  void endImplicitCreationExpression(Token token, Token openAngleBracket) {
     DirectParserASTContentImplicitCreationExpressionEnd data =
         new DirectParserASTContentImplicitCreationExpressionEnd(
             DirectParserASTType.END,
-            token: token);
+            token: token,
+            openAngleBracket: openAngleBracket);
     seen(data);
   }
 
@@ -4782,14 +4783,16 @@
 class DirectParserASTContentImplicitCreationExpressionEnd
     extends DirectParserASTContent {
   final Token token;
+  final Token openAngleBracket;
 
   DirectParserASTContentImplicitCreationExpressionEnd(DirectParserASTType type,
-      {required this.token})
+      {required this.token, required this.openAngleBracket})
       : super("ImplicitCreationExpression", type);
 
   @override
   Map<String, Object?> get deprecatedArguments => {
         "token": token,
+        "openAngleBracket": openAngleBracket,
       };
 }
 
diff --git a/pkg/front_end/parser_testcases/general/function_reference_following_token.dart.expect b/pkg/front_end/parser_testcases/general/function_reference_following_token.dart.expect
index 2fca01a..8b8e356 100644
--- a/pkg/front_end/parser_testcases/general/function_reference_following_token.dart.expect
+++ b/pkg/front_end/parser_testcases/general/function_reference_following_token.dart.expect
@@ -293,7 +293,7 @@
       handleNoType(var)
       handleIdentifier(typeArgs_period_methodInvocation, topLevelVariableDeclaration)
       beginFieldInitializer(=)
-        beginImplicitCreationExpression(=)
+        beginImplicitCreationExpression(f)
           handleIdentifier(f, constructorReference)
           beginConstructorReference(f)
             beginTypeArguments(<)
@@ -308,7 +308,7 @@
           endConstructorReference(f, ., (, ConstructorReferenceContext.Implicit)
           beginArguments(()
           endArguments(0, (, ))
-        endImplicitCreationExpression(=)
+        endImplicitCreationExpression(f, <)
       endFieldInitializer(=, ;)
     endTopLevelFields(null, null, null, null, var, 1, var, ;)
   endTopLevelDeclaration(var)
diff --git a/pkg/front_end/parser_testcases/general/function_reference_following_token.dart.intertwined.expect b/pkg/front_end/parser_testcases/general/function_reference_following_token.dart.intertwined.expect
index 32d6f7d..11e6ef5 100644
--- a/pkg/front_end/parser_testcases/general/function_reference_following_token.dart.intertwined.expect
+++ b/pkg/front_end/parser_testcases/general/function_reference_following_token.dart.intertwined.expect
@@ -396,8 +396,8 @@
           parseExpression(=)
             parsePrecedenceExpression(=, 1, true)
               parseUnaryExpression(=, true)
-                parseImplicitCreationExpression(=, Instance of 'ComplexTypeParamOrArgInfo')
-                  listener: beginImplicitCreationExpression(=)
+                parseImplicitCreationExpression(=, <, Instance of 'ComplexTypeParamOrArgInfo')
+                  listener: beginImplicitCreationExpression(f)
                   parseConstructorReference(=, ConstructorReferenceContext.Implicit, Instance of 'ComplexTypeParamOrArgInfo')
                     ensureIdentifier(=, constructorReference)
                       listener: handleIdentifier(f, constructorReference)
@@ -418,7 +418,7 @@
                     parseArgumentsRest(()
                       listener: beginArguments(()
                       listener: endArguments(0, (, ))
-                  listener: endImplicitCreationExpression(=)
+                  listener: endImplicitCreationExpression(f, <)
           listener: endFieldInitializer(=, ;)
         listener: endTopLevelFields(null, null, null, null, var, 1, var, ;)
   listener: endTopLevelDeclaration(var)
diff --git a/pkg/front_end/parser_testcases/general/new_as_identifier.dart.expect b/pkg/front_end/parser_testcases/general/new_as_identifier.dart.expect
index 89b9611..97bcac4 100644
--- a/pkg/front_end/parser_testcases/general/new_as_identifier.dart.expect
+++ b/pkg/front_end/parser_testcases/general/new_as_identifier.dart.expect
@@ -545,7 +545,7 @@
       handleNoType(var)
       handleIdentifier(constructor_invocation_implicit_generic, topLevelVariableDeclaration)
       beginFieldInitializer(=)
-        beginImplicitCreationExpression(=)
+        beginImplicitCreationExpression(C)
           handleIdentifier(C, constructorReference)
           beginConstructorReference(C)
             beginTypeArguments(<)
@@ -558,7 +558,7 @@
           endConstructorReference(C, ., (, ConstructorReferenceContext.Implicit)
           beginArguments(()
           endArguments(0, (, ))
-        endImplicitCreationExpression(=)
+        endImplicitCreationExpression(C, <)
       endFieldInitializer(=, ;)
     endTopLevelFields(null, null, null, null, var, 1, var, ;)
   endTopLevelDeclaration(var)
@@ -595,7 +595,7 @@
       handleNoType(var)
       handleIdentifier(constructor_invocation_implicit_prefixed_generic, topLevelVariableDeclaration)
       beginFieldInitializer(=)
-        beginImplicitCreationExpression(=)
+        beginImplicitCreationExpression(prefix)
           handleIdentifier(prefix, constructorReference)
           beginConstructorReference(prefix)
             handleIdentifier(C, constructorReferenceContinuation)
@@ -610,7 +610,7 @@
           endConstructorReference(prefix, ., (, ConstructorReferenceContext.Implicit)
           beginArguments(()
           endArguments(0, (, ))
-        endImplicitCreationExpression(=)
+        endImplicitCreationExpression(prefix, <)
       endFieldInitializer(=, ;)
     endTopLevelFields(null, null, null, null, var, 1, var, ;)
   endTopLevelDeclaration(var)
diff --git a/pkg/front_end/parser_testcases/general/new_as_identifier.dart.intertwined.expect b/pkg/front_end/parser_testcases/general/new_as_identifier.dart.intertwined.expect
index f1c6c1c..7af61b0 100644
--- a/pkg/front_end/parser_testcases/general/new_as_identifier.dart.intertwined.expect
+++ b/pkg/front_end/parser_testcases/general/new_as_identifier.dart.intertwined.expect
@@ -1028,8 +1028,8 @@
           parseExpression(=)
             parsePrecedenceExpression(=, 1, true)
               parseUnaryExpression(=, true)
-                parseImplicitCreationExpression(=, Instance of 'SimpleTypeArgument1')
-                  listener: beginImplicitCreationExpression(=)
+                parseImplicitCreationExpression(=, <, Instance of 'SimpleTypeArgument1')
+                  listener: beginImplicitCreationExpression(C)
                   parseConstructorReference(=, ConstructorReferenceContext.Implicit, Instance of 'SimpleTypeArgument1')
                     ensureIdentifier(=, constructorReference)
                       listener: handleIdentifier(C, constructorReference)
@@ -1049,7 +1049,7 @@
                     parseArgumentsRest(()
                       listener: beginArguments(()
                       listener: endArguments(0, (, ))
-                  listener: endImplicitCreationExpression(=)
+                  listener: endImplicitCreationExpression(C, <)
           listener: endFieldInitializer(=, ;)
         listener: endTopLevelFields(null, null, null, null, var, 1, var, ;)
   listener: endTopLevelDeclaration(var)
@@ -1126,8 +1126,8 @@
           parseExpression(=)
             parsePrecedenceExpression(=, 1, true)
               parseUnaryExpression(=, true)
-                parseImplicitCreationExpression(=, Instance of 'SimpleTypeArgument1')
-                  listener: beginImplicitCreationExpression(=)
+                parseImplicitCreationExpression(=, <, Instance of 'SimpleTypeArgument1')
+                  listener: beginImplicitCreationExpression(prefix)
                   parseConstructorReference(=, ConstructorReferenceContext.Implicit, Instance of 'SimpleTypeArgument1')
                     ensureIdentifier(=, constructorReference)
                       listener: handleIdentifier(prefix, constructorReference)
@@ -1151,7 +1151,7 @@
                     parseArgumentsRest(()
                       listener: beginArguments(()
                       listener: endArguments(0, (, ))
-                  listener: endImplicitCreationExpression(=)
+                  listener: endImplicitCreationExpression(prefix, <)
           listener: endFieldInitializer(=, ;)
         listener: endTopLevelFields(null, null, null, null, var, 1, var, ;)
   listener: endTopLevelDeclaration(var)
diff --git a/pkg/front_end/test/parser_test_listener.dart b/pkg/front_end/test/parser_test_listener.dart
index 7fcb1c2..f82cd95 100644
--- a/pkg/front_end/test/parser_test_listener.dart
+++ b/pkg/front_end/test/parser_test_listener.dart
@@ -1086,10 +1086,11 @@
   }
 
   @override
-  void endImplicitCreationExpression(Token token) {
+  void endImplicitCreationExpression(Token token, Token openAngleBracket) {
     indent--;
     seen(token);
-    doPrint('endImplicitCreationExpression(' '$token)');
+    seen(openAngleBracket);
+    doPrint('endImplicitCreationExpression(' '$token, ' '$openAngleBracket)');
   }
 
   @override
diff --git a/pkg/front_end/test/parser_test_parser.dart b/pkg/front_end/test/parser_test_parser.dart
index 3ea2b0c..a6ba2ca 100644
--- a/pkg/front_end/test/parser_test_parser.dart
+++ b/pkg/front_end/test/parser_test_parser.dart
@@ -1651,10 +1651,14 @@
 
   @override
   Token parseImplicitCreationExpression(
-      Token token, TypeParamOrArgInfo typeArg) {
-    doPrint('parseImplicitCreationExpression(' '$token, ' '$typeArg)');
+      Token token, Token openAngleBracket, TypeParamOrArgInfo typeArg) {
+    doPrint('parseImplicitCreationExpression('
+        '$token, '
+        '$openAngleBracket, '
+        '$typeArg)');
     indent++;
-    var result = super.parseImplicitCreationExpression(token, typeArg);
+    var result =
+        super.parseImplicitCreationExpression(token, openAngleBracket, typeArg);
     indent--;
     return result;
   }
diff --git a/pkg/front_end/testcases/constructor_tearoffs/issue46719.dart b/pkg/front_end/testcases/constructor_tearoffs/issue46719.dart
index efe748f..4232eea 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/issue46719.dart
+++ b/pkg/front_end/testcases/constructor_tearoffs/issue46719.dart
@@ -21,7 +21,7 @@
 }
 
 test() {
-  A.named<int>.toString();
+  A.named<int>.toString(); // error
 }
 
 void main() {
diff --git a/pkg/front_end/testcases/constructor_tearoffs/issue46719.dart.strong.expect b/pkg/front_end/testcases/constructor_tearoffs/issue46719.dart.strong.expect
index 99500cc..a62bd0c 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/issue46719.dart.strong.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/issue46719.dart.strong.expect
@@ -1,4 +1,12 @@
 library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/constructor_tearoffs/issue46719.dart:24:10: Error: A constructor tear-off can't have type arguments after the constructor name.
+// Try removing the type arguments or placing them after the class name.
+//   A.named<int>.toString(); // error
+//          ^
+//
 import self as self;
 import "dart:core" as core;
 
@@ -27,30 +35,33 @@
 static method FunctionApplier|get#applyAndPrint(lowered final core::Function #this) → (core::List<core::Object?>) → void
   return (core::List<core::Object?> positionalArguments) → void => self::FunctionApplier|applyAndPrint(#this, positionalArguments);
 static method test() → dynamic {
-  #C3.{core::Object::toString}(){() → core::String};
+  invalid-expression "pkg/front_end/testcases/constructor_tearoffs/issue46719.dart:24:10: Error: A constructor tear-off can't have type arguments after the constructor name.
+Try removing the type arguments or placing them after the class name.
+  A.named<int>.toString(); // error
+         ^";
 }
 static method main() → void {
   self::A<dynamic> a = new self::A::•<dynamic>();
   self::FunctionApplier|applyAndPrint(a.{self::A::m}{<X extends core::Object? = dynamic>(X%) → core::List<X%>}<core::int>, <core::Object?>[2]);
   self::FunctionApplier|applyAndPrint(a.{self::A::m}{<X extends core::Object? = dynamic>(X%) → core::List<X%>}<core::String>, <core::Object?>["three"]);
-  self::FunctionApplier|applyAndPrint(#C5, <core::Object?>[2]);
-  self::FunctionApplier|applyAndPrint(#C6, <core::Object?>["three"]);
-  self::FunctionApplier|applyAndPrint(#C8, <core::Object?>[2]);
-  self::FunctionApplier|applyAndPrint(#C9, <core::Object?>["three"]);
-  self::FunctionApplier|applyAndPrint(#C5, <core::Object?>[2]);
-  self::FunctionApplier|applyAndPrint(#C6, <core::Object?>["three"]);
-  #C2.{core::Object::toString}(){() → core::String};
-  #C3.{core::Object::toString}(){() → core::String};
+  self::FunctionApplier|applyAndPrint(#C3, <core::Object?>[2]);
+  self::FunctionApplier|applyAndPrint(#C4, <core::Object?>["three"]);
+  self::FunctionApplier|applyAndPrint(#C6, <core::Object?>[2]);
+  self::FunctionApplier|applyAndPrint(#C7, <core::Object?>["three"]);
+  self::FunctionApplier|applyAndPrint(#C3, <core::Object?>[2]);
+  self::FunctionApplier|applyAndPrint(#C4, <core::Object?>["three"]);
+  #C8.{core::Object::toString}(){() → core::String};
+  #C9.{core::Object::toString}(){() → core::String};
 }
 
 constants  {
   #C1 = <core::Symbol, dynamic>{)
-  #C2 = constructor-tearoff self::A::named
+  #C2 = static-tearoff self::A::n
   #C3 = instantiation #C2 <core::int>
-  #C4 = static-tearoff self::A::n
-  #C5 = instantiation #C4 <core::int>
-  #C6 = instantiation #C4 <core::String>
-  #C7 = static-tearoff self::m
-  #C8 = instantiation #C7 <core::int>
-  #C9 = instantiation #C7 <core::String>
+  #C4 = instantiation #C2 <core::String>
+  #C5 = static-tearoff self::m
+  #C6 = instantiation #C5 <core::int>
+  #C7 = instantiation #C5 <core::String>
+  #C8 = constructor-tearoff self::A::named
+  #C9 = instantiation #C8 <core::int>
 }
diff --git a/pkg/front_end/testcases/constructor_tearoffs/issue46719.dart.strong.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/issue46719.dart.strong.transformed.expect
index 4e41cb6..7124a3a 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/issue46719.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/issue46719.dart.strong.transformed.expect
@@ -1,4 +1,12 @@
 library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/constructor_tearoffs/issue46719.dart:24:10: Error: A constructor tear-off can't have type arguments after the constructor name.
+// Try removing the type arguments or placing them after the class name.
+//   A.named<int>.toString(); // error
+//          ^
+//
 import self as self;
 import "dart:core" as core;
 
@@ -27,30 +35,33 @@
 static method FunctionApplier|get#applyAndPrint(lowered final core::Function #this) → (core::List<core::Object?>) → void
   return (core::List<core::Object?> positionalArguments) → void => self::FunctionApplier|applyAndPrint(#this, positionalArguments);
 static method test() → dynamic {
-  #C3.{core::Object::toString}(){() → core::String};
+  invalid-expression "pkg/front_end/testcases/constructor_tearoffs/issue46719.dart:24:10: Error: A constructor tear-off can't have type arguments after the constructor name.
+Try removing the type arguments or placing them after the class name.
+  A.named<int>.toString(); // error
+         ^";
 }
 static method main() → void {
   self::A<dynamic> a = new self::A::•<dynamic>();
   self::FunctionApplier|applyAndPrint(a.{self::A::m}{<X extends core::Object? = dynamic>(X%) → core::List<X%>}<core::int>, core::_GrowableList::_literal1<core::Object?>(2));
   self::FunctionApplier|applyAndPrint(a.{self::A::m}{<X extends core::Object? = dynamic>(X%) → core::List<X%>}<core::String>, core::_GrowableList::_literal1<core::Object?>("three"));
-  self::FunctionApplier|applyAndPrint(#C5, core::_GrowableList::_literal1<core::Object?>(2));
-  self::FunctionApplier|applyAndPrint(#C6, core::_GrowableList::_literal1<core::Object?>("three"));
-  self::FunctionApplier|applyAndPrint(#C8, core::_GrowableList::_literal1<core::Object?>(2));
-  self::FunctionApplier|applyAndPrint(#C9, core::_GrowableList::_literal1<core::Object?>("three"));
-  self::FunctionApplier|applyAndPrint(#C5, core::_GrowableList::_literal1<core::Object?>(2));
-  self::FunctionApplier|applyAndPrint(#C6, core::_GrowableList::_literal1<core::Object?>("three"));
-  #C2.{core::Object::toString}(){() → core::String};
-  #C3.{core::Object::toString}(){() → core::String};
+  self::FunctionApplier|applyAndPrint(#C3, core::_GrowableList::_literal1<core::Object?>(2));
+  self::FunctionApplier|applyAndPrint(#C4, core::_GrowableList::_literal1<core::Object?>("three"));
+  self::FunctionApplier|applyAndPrint(#C6, core::_GrowableList::_literal1<core::Object?>(2));
+  self::FunctionApplier|applyAndPrint(#C7, core::_GrowableList::_literal1<core::Object?>("three"));
+  self::FunctionApplier|applyAndPrint(#C3, core::_GrowableList::_literal1<core::Object?>(2));
+  self::FunctionApplier|applyAndPrint(#C4, core::_GrowableList::_literal1<core::Object?>("three"));
+  #C8.{core::Object::toString}(){() → core::String};
+  #C9.{core::Object::toString}(){() → core::String};
 }
 
 constants  {
   #C1 = <core::Symbol, dynamic>{)
-  #C2 = constructor-tearoff self::A::named
+  #C2 = static-tearoff self::A::n
   #C3 = instantiation #C2 <core::int>
-  #C4 = static-tearoff self::A::n
-  #C5 = instantiation #C4 <core::int>
-  #C6 = instantiation #C4 <core::String>
-  #C7 = static-tearoff self::m
-  #C8 = instantiation #C7 <core::int>
-  #C9 = instantiation #C7 <core::String>
+  #C4 = instantiation #C2 <core::String>
+  #C5 = static-tearoff self::m
+  #C6 = instantiation #C5 <core::int>
+  #C7 = instantiation #C5 <core::String>
+  #C8 = constructor-tearoff self::A::named
+  #C9 = instantiation #C8 <core::int>
 }
diff --git a/pkg/front_end/testcases/constructor_tearoffs/issue46719.dart.weak.expect b/pkg/front_end/testcases/constructor_tearoffs/issue46719.dart.weak.expect
index ce83852..6add537 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/issue46719.dart.weak.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/issue46719.dart.weak.expect
@@ -1,4 +1,12 @@
 library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/constructor_tearoffs/issue46719.dart:24:10: Error: A constructor tear-off can't have type arguments after the constructor name.
+// Try removing the type arguments or placing them after the class name.
+//   A.named<int>.toString(); // error
+//          ^
+//
 import self as self;
 import "dart:core" as core;
 
@@ -27,30 +35,33 @@
 static method FunctionApplier|get#applyAndPrint(lowered final core::Function #this) → (core::List<core::Object?>) → void
   return (core::List<core::Object?> positionalArguments) → void => self::FunctionApplier|applyAndPrint(#this, positionalArguments);
 static method test() → dynamic {
-  #C3.{core::Object::toString}(){() → core::String};
+  invalid-expression "pkg/front_end/testcases/constructor_tearoffs/issue46719.dart:24:10: Error: A constructor tear-off can't have type arguments after the constructor name.
+Try removing the type arguments or placing them after the class name.
+  A.named<int>.toString(); // error
+         ^";
 }
 static method main() → void {
   self::A<dynamic> a = new self::A::•<dynamic>();
   self::FunctionApplier|applyAndPrint(a.{self::A::m}{<X extends core::Object? = dynamic>(X%) → core::List<X%>}<core::int>, <core::Object?>[2]);
   self::FunctionApplier|applyAndPrint(a.{self::A::m}{<X extends core::Object? = dynamic>(X%) → core::List<X%>}<core::String>, <core::Object?>["three"]);
-  self::FunctionApplier|applyAndPrint(#C5, <core::Object?>[2]);
-  self::FunctionApplier|applyAndPrint(#C6, <core::Object?>["three"]);
-  self::FunctionApplier|applyAndPrint(#C8, <core::Object?>[2]);
-  self::FunctionApplier|applyAndPrint(#C9, <core::Object?>["three"]);
-  self::FunctionApplier|applyAndPrint(#C5, <core::Object?>[2]);
-  self::FunctionApplier|applyAndPrint(#C6, <core::Object?>["three"]);
-  #C2.{core::Object::toString}(){() → core::String};
-  #C3.{core::Object::toString}(){() → core::String};
+  self::FunctionApplier|applyAndPrint(#C3, <core::Object?>[2]);
+  self::FunctionApplier|applyAndPrint(#C4, <core::Object?>["three"]);
+  self::FunctionApplier|applyAndPrint(#C6, <core::Object?>[2]);
+  self::FunctionApplier|applyAndPrint(#C7, <core::Object?>["three"]);
+  self::FunctionApplier|applyAndPrint(#C3, <core::Object?>[2]);
+  self::FunctionApplier|applyAndPrint(#C4, <core::Object?>["three"]);
+  #C8.{core::Object::toString}(){() → core::String};
+  #C9.{core::Object::toString}(){() → core::String};
 }
 
 constants  {
   #C1 = <core::Symbol*, dynamic>{)
-  #C2 = constructor-tearoff self::A::named
+  #C2 = static-tearoff self::A::n
   #C3 = instantiation #C2 <core::int*>
-  #C4 = static-tearoff self::A::n
-  #C5 = instantiation #C4 <core::int*>
-  #C6 = instantiation #C4 <core::String*>
-  #C7 = static-tearoff self::m
-  #C8 = instantiation #C7 <core::int*>
-  #C9 = instantiation #C7 <core::String*>
+  #C4 = instantiation #C2 <core::String*>
+  #C5 = static-tearoff self::m
+  #C6 = instantiation #C5 <core::int*>
+  #C7 = instantiation #C5 <core::String*>
+  #C8 = constructor-tearoff self::A::named
+  #C9 = instantiation #C8 <core::int*>
 }
diff --git a/pkg/front_end/testcases/constructor_tearoffs/issue46719.dart.weak.transformed.expect b/pkg/front_end/testcases/constructor_tearoffs/issue46719.dart.weak.transformed.expect
index cd0afe2..328de14 100644
--- a/pkg/front_end/testcases/constructor_tearoffs/issue46719.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/constructor_tearoffs/issue46719.dart.weak.transformed.expect
@@ -1,4 +1,12 @@
 library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/constructor_tearoffs/issue46719.dart:24:10: Error: A constructor tear-off can't have type arguments after the constructor name.
+// Try removing the type arguments or placing them after the class name.
+//   A.named<int>.toString(); // error
+//          ^
+//
 import self as self;
 import "dart:core" as core;
 
@@ -27,30 +35,33 @@
 static method FunctionApplier|get#applyAndPrint(lowered final core::Function #this) → (core::List<core::Object?>) → void
   return (core::List<core::Object?> positionalArguments) → void => self::FunctionApplier|applyAndPrint(#this, positionalArguments);
 static method test() → dynamic {
-  #C3.{core::Object::toString}(){() → core::String};
+  invalid-expression "pkg/front_end/testcases/constructor_tearoffs/issue46719.dart:24:10: Error: A constructor tear-off can't have type arguments after the constructor name.
+Try removing the type arguments or placing them after the class name.
+  A.named<int>.toString(); // error
+         ^";
 }
 static method main() → void {
   self::A<dynamic> a = new self::A::•<dynamic>();
   self::FunctionApplier|applyAndPrint(a.{self::A::m}{<X extends core::Object? = dynamic>(X%) → core::List<X%>}<core::int>, core::_GrowableList::_literal1<core::Object?>(2));
   self::FunctionApplier|applyAndPrint(a.{self::A::m}{<X extends core::Object? = dynamic>(X%) → core::List<X%>}<core::String>, core::_GrowableList::_literal1<core::Object?>("three"));
-  self::FunctionApplier|applyAndPrint(#C5, core::_GrowableList::_literal1<core::Object?>(2));
-  self::FunctionApplier|applyAndPrint(#C6, core::_GrowableList::_literal1<core::Object?>("three"));
-  self::FunctionApplier|applyAndPrint(#C8, core::_GrowableList::_literal1<core::Object?>(2));
-  self::FunctionApplier|applyAndPrint(#C9, core::_GrowableList::_literal1<core::Object?>("three"));
-  self::FunctionApplier|applyAndPrint(#C5, core::_GrowableList::_literal1<core::Object?>(2));
-  self::FunctionApplier|applyAndPrint(#C6, core::_GrowableList::_literal1<core::Object?>("three"));
-  #C2.{core::Object::toString}(){() → core::String};
-  #C3.{core::Object::toString}(){() → core::String};
+  self::FunctionApplier|applyAndPrint(#C3, core::_GrowableList::_literal1<core::Object?>(2));
+  self::FunctionApplier|applyAndPrint(#C4, core::_GrowableList::_literal1<core::Object?>("three"));
+  self::FunctionApplier|applyAndPrint(#C6, core::_GrowableList::_literal1<core::Object?>(2));
+  self::FunctionApplier|applyAndPrint(#C7, core::_GrowableList::_literal1<core::Object?>("three"));
+  self::FunctionApplier|applyAndPrint(#C3, core::_GrowableList::_literal1<core::Object?>(2));
+  self::FunctionApplier|applyAndPrint(#C4, core::_GrowableList::_literal1<core::Object?>("three"));
+  #C8.{core::Object::toString}(){() → core::String};
+  #C9.{core::Object::toString}(){() → core::String};
 }
 
 constants  {
   #C1 = <core::Symbol*, dynamic>{)
-  #C2 = constructor-tearoff self::A::named
+  #C2 = static-tearoff self::A::n
   #C3 = instantiation #C2 <core::int*>
-  #C4 = static-tearoff self::A::n
-  #C5 = instantiation #C4 <core::int*>
-  #C6 = instantiation #C4 <core::String*>
-  #C7 = static-tearoff self::m
-  #C8 = instantiation #C7 <core::int*>
-  #C9 = instantiation #C7 <core::String*>
+  #C4 = instantiation #C2 <core::String*>
+  #C5 = static-tearoff self::m
+  #C6 = instantiation #C5 <core::int*>
+  #C7 = instantiation #C5 <core::String*>
+  #C8 = constructor-tearoff self::A::named
+  #C9 = instantiation #C8 <core::int*>
 }
diff --git a/pkg/vm_service/example/vm_service_assert.dart b/pkg/vm_service/example/vm_service_assert.dart
index 0af318b..f30e794 100644
--- a/pkg/vm_service/example/vm_service_assert.dart
+++ b/pkg/vm_service/example/vm_service_assert.dart
@@ -30,6 +30,10 @@
   return obj;
 }
 
+List<dynamic> assertListOfDynamic(List<dynamic> list) {
+  return list;
+}
+
 List<int> assertListOfInt(List<int> list) {
   for (int elem in list) {
     assertInt(elem);
@@ -1146,6 +1150,12 @@
   return obj;
 }
 
+vms.UriList assertUriList(vms.UriList obj) {
+  assertNotNull(obj);
+  assertListOfDynamic(obj.uris!);
+  return obj;
+}
+
 vms.Version assertVersion(vms.Version obj) {
   assertNotNull(obj);
   assertInt(obj.major!);
diff --git a/pkg/vm_service/java/.gitignore b/pkg/vm_service/java/.gitignore
index cc8a3e7..817e983 100644
--- a/pkg/vm_service/java/.gitignore
+++ b/pkg/vm_service/java/.gitignore
@@ -43,6 +43,7 @@
 src/org/dartlang/vm/service/consumer/TimelineConsumer.java
 src/org/dartlang/vm/service/consumer/TimelineFlagsConsumer.java
 src/org/dartlang/vm/service/consumer/TimestampConsumer.java
+src/org/dartlang/vm/service/consumer/UriListConsumer.java
 src/org/dartlang/vm/service/consumer/VMConsumer.java
 src/org/dartlang/vm/service/consumer/VersionConsumer.java
 src/org/dartlang/vm/service/element/AllocationProfile.java
@@ -131,6 +132,7 @@
 src/org/dartlang/vm/service/element/TypeArgumentsRef.java
 src/org/dartlang/vm/service/element/TypeParameters.java
 src/org/dartlang/vm/service/element/UnresolvedSourceLocation.java
+src/org/dartlang/vm/service/element/UriList.java
 src/org/dartlang/vm/service/element/VM.java
 src/org/dartlang/vm/service/element/VMRef.java
 src/org/dartlang/vm/service/element/Version.java
diff --git a/pkg/vm_service/java/src/org/dartlang/vm/service/consumer/ResolvePackageUriConsumer.java b/pkg/vm_service/java/src/org/dartlang/vm/service/consumer/ResolvePackageUriConsumer.java
new file mode 100644
index 0000000..cb35d14
--- /dev/null
+++ b/pkg/vm_service/java/src/org/dartlang/vm/service/consumer/ResolvePackageUriConsumer.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2015, the Dart project authors.
+ *
+ * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.dartlang.vm.service.consumer;
+
+// This is a generated file.
+
+import org.dartlang.vm.service.element.ResolvedPackageUri;
+import org.dartlang.vm.service.element.Sentinel;
+
+@SuppressWarnings({"WeakerAccess", "unused"})
+public interface ResolvePackageUriConsumer extends Consumer {
+    void received(ResolvedPackageUri response);
+
+    void received(Sentinel response);
+}
diff --git a/pkg/vm_service/java/src/org/dartlang/vm/service/element/ResolvedPackageUri.java b/pkg/vm_service/java/src/org/dartlang/vm/service/element/ResolvedPackageUri.java
new file mode 100644
index 0000000..376b4f0
--- /dev/null
+++ b/pkg/vm_service/java/src/org/dartlang/vm/service/element/ResolvedPackageUri.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015, the Dart project authors.
+ *
+ * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.dartlang.vm.service.element;
+
+// This is a generated file.
+
+import com.google.gson.JsonObject;
+
+@SuppressWarnings({"WeakerAccess", "unused"})
+public class ResolvedPackageUri extends Response {
+    public ResolvedPackageUri(JsonObject json) {
+        super(json);
+    }
+
+    /**
+   * The file URI for a given package.
+   */
+    public String getResolvedUri() {
+        return getAsString("resolvedUri");
+    }
+}
diff --git a/pkg/vm_service/java/version.properties b/pkg/vm_service/java/version.properties
index 383674e..8ae6998 100644
--- a/pkg/vm_service/java/version.properties
+++ b/pkg/vm_service/java/version.properties
@@ -1 +1 @@
-version=3.51
+version=3.52
diff --git a/pkg/vm_service/lib/src/vm_service.dart b/pkg/vm_service/lib/src/vm_service.dart
index 277b463..51958bd 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.51.0';
+const String vmServiceVersion = '3.52.0';
 
 /// @optional
 const String optional = 'optional';
@@ -186,6 +186,7 @@
   'TypeArguments': TypeArguments.parse,
   'TypeParameters': TypeParameters.parse,
   'UnresolvedSourceLocation': UnresolvedSourceLocation.parse,
+  'UriList': UriList.parse,
   'Version': Version.parse,
   '@VM': VMRef.parse,
   'VM': VM.parse,
@@ -226,6 +227,8 @@
   'getVMTimelineMicros': const ['Timestamp'],
   'pause': const ['Success'],
   'kill': const ['Success'],
+  'lookupResolvedPackageUris': const ['UriList'],
+  'lookupPackageUris': const ['UriList'],
   'registerService': const ['Success'],
   'reloadSources': const ['ReloadReport'],
   'removeBreakpoint': const ['Success'],
@@ -920,6 +923,37 @@
   /// returned.
   Future<Success> kill(String isolateId);
 
+  /// The `lookupResolvedPackageUris` RPC is used to convert a list of URIs to
+  /// their resolved (or absolute) paths. For example, URIs passed to this RPC
+  /// are mapped in the following ways:
+  ///
+  /// - `dart:io` -&gt; `org-dartlang-sdk:///sdk/lib/io/io.dart`
+  /// - `package:test/test.dart` -&gt;
+  /// `file:///$PACKAGE_INSTALLATION_DIR/lib/test.dart`
+  /// - `file:///foo/bar/bazz.dart` -&gt; `file:///foo/bar/bazz.dart`
+  ///
+  /// If a URI is not known, the corresponding entry in the [UriList] response
+  /// will be `null`.
+  ///
+  /// See [UriList].
+  Future<UriList> lookupResolvedPackageUris(
+      String isolateId, List<String> uris);
+
+  /// The `lookupPackageUris` RPC is used to convert a list of URIs to their
+  /// unresolved paths. For example, URIs passed to this RPC are mapped in the
+  /// following ways:
+  ///
+  /// - `org-dartlang-sdk:///sdk/lib/io/io.dart` -&gt; `dart:io`
+  /// - `file:///$PACKAGE_INSTALLATION_DIR/lib/test.dart` -&gt;
+  /// `package:test/test.dart`
+  /// - `file:///foo/bar/bazz.dart` -&gt; `file:///foo/bar/bazz.dart`
+  ///
+  /// If a URI is not known, the corresponding entry in the [UriList] response
+  /// will be `null`.
+  ///
+  /// See [UriList].
+  Future<UriList> lookupPackageUris(String isolateId, List<String> uris);
+
   /// Registers a service that can be invoked by other VM service clients, where
   /// `service` is the name of the service to advertise and `alias` is an
   /// alternative name for the registered service.
@@ -1468,6 +1502,18 @@
             params!['isolateId'],
           );
           break;
+        case 'lookupResolvedPackageUris':
+          response = await _serviceImplementation.lookupResolvedPackageUris(
+            params!['isolateId'],
+            List<String>.from(params['uris'] ?? []),
+          );
+          break;
+        case 'lookupPackageUris':
+          response = await _serviceImplementation.lookupPackageUris(
+            params!['isolateId'],
+            List<String>.from(params['uris'] ?? []),
+          );
+          break;
         case 'reloadSources':
           response = await _serviceImplementation.reloadSources(
             params!['isolateId'],
@@ -1979,6 +2025,16 @@
       _call('kill', {'isolateId': isolateId});
 
   @override
+  Future<UriList> lookupResolvedPackageUris(
+          String isolateId, List<String> uris) =>
+      _call(
+          'lookupResolvedPackageUris', {'isolateId': isolateId, 'uris': uris});
+
+  @override
+  Future<UriList> lookupPackageUris(String isolateId, List<String> uris) =>
+      _call('lookupPackageUris', {'isolateId': isolateId, 'uris': uris});
+
+  @override
   Future<Success> registerService(String service, String alias) =>
       _call('registerService', {'service': service, 'alias': alias});
 
@@ -6112,9 +6168,8 @@
   /// example, memory associated with Dart objects through APIs such as
   /// Dart_NewFinalizableHandle, Dart_NewWeakPersistentHandle and
   /// Dart_NewExternalTypedData.  This usage is only as accurate as the values
-  /// supplied to these APIs from the VM embedder or native extensions. This
-  /// external memory applies GC pressure, but is separate from heapUsage and
-  /// heapCapacity.
+  /// supplied to these APIs from the VM embedder. This external memory applies
+  /// GC pressure, but is separate from heapUsage and heapCapacity.
   int? externalUsage;
 
   /// The total capacity of the heap in bytes. This is the amount of memory used
@@ -7876,6 +7931,38 @@
   String toString() => '[UnresolvedSourceLocation]';
 }
 
+class UriList extends Response {
+  static UriList? parse(Map<String, dynamic>? json) =>
+      json == null ? null : UriList._fromJson(json);
+
+  /// A list of URIs.
+  List<dynamic>? uris;
+
+  UriList({
+    required this.uris,
+  });
+
+  UriList._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
+    uris = List<dynamic>.from(
+        createServiceObject(json['uris'], const ['dynamic']) as List? ?? []);
+  }
+
+  @override
+  String get type => 'UriList';
+
+  @override
+  Map<String, dynamic> toJson() {
+    final json = <String, dynamic>{};
+    json['type'] = type;
+    json.addAll({
+      'uris': uris?.map((f) => f.toJson()).toList(),
+    });
+    return json;
+  }
+
+  String toString() => '[UriList uris: ${uris}]';
+}
+
 /// See [Versioning].
 class Version extends Response {
   static Version? parse(Map<String, dynamic>? json) =>
diff --git a/pkg/vm_service/test/uri_mappings_lookup_test.dart b/pkg/vm_service/test/uri_mappings_lookup_test.dart
new file mode 100644
index 0000000..0155833
--- /dev/null
+++ b/pkg/vm_service/test/uri_mappings_lookup_test.dart
@@ -0,0 +1,56 @@
+// 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 'dart:io';
+
+import 'package:test/test.dart';
+import 'package:vm_service/vm_service.dart';
+
+import 'common/test_helper.dart';
+
+final tests = <IsolateTest>[
+  (VmService service, IsolateRef isolate) async {
+    final isolateId = isolate.id!;
+    final unresolvedUris = <String>[
+      'dart:io', // dart:io -> org-dartlang-sdk:///sdk/lib/io/io.dart
+      Platform.script.toString(), // file:///abc.dart -> file:///abc.dart
+      'package:test/test.dart', // package:test/test.dart -> file:///some_dir/test/lib/test.dart
+      'package:does_not_exist/does_not_exist.dart', // invalid URI -> null
+    ];
+
+    var result = await service.lookupResolvedPackageUris(
+      isolateId,
+      unresolvedUris,
+    );
+    expect(result.uris, isNotNull);
+    var uris = result.uris!;
+    expect(uris.length, 4);
+    expect(uris[0], 'org-dartlang-sdk:///sdk/lib/io/io.dart');
+    expect(uris[1], Platform.script.toString());
+    expect(uris[2], startsWith('file:///'));
+    expect(uris[2], endsWith('third_party/pkg/test/pkgs/test/lib/test.dart'));
+    expect(uris[3], isNull);
+
+    result = await service.lookupPackageUris(
+      isolateId,
+      [
+        ...uris.sublist(0, 3),
+        'does_not_exist.dart',
+      ],
+    );
+    expect(result.uris, isNotNull);
+    uris = result.uris!;
+    expect(uris.length, 4);
+    expect(uris[0], unresolvedUris[0]);
+    expect(uris[1], unresolvedUris[1]);
+    expect(uris[2], unresolvedUris[2]);
+    expect(uris[3], isNull);
+  },
+];
+
+void main(args) => runIsolateTests(
+      args,
+      tests,
+      'uri_mappings_lookup_test.dart',
+    );
diff --git a/runtime/observatory/tests/service/get_version_rpc_test.dart b/runtime/observatory/tests/service/get_version_rpc_test.dart
index 340b046..4ad1d93 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'], 51);
+    expect(result['minor'], 52);
     expect(result['_privateMajor'], 0);
     expect(result['_privateMinor'], 0);
   },
diff --git a/runtime/observatory/tests/service/service.status b/runtime/observatory/tests/service/service.status
index c7e818c..ee2c8dc 100644
--- a/runtime/observatory/tests/service/service.status
+++ b/runtime/observatory/tests/service/service.status
@@ -29,12 +29,14 @@
 
 [ $system == android ]
 string_escaping_test: Skip # Issue http://dartbug.com/42094
+uri_mappings_lookup_test: SkipByDesign # Relies on file paths in the SDK.
 
 [ $system == windows ]
 *: Slow
 async_generator_breakpoint_test: Skip # Issue 29145
 dev_fs_http_put_weird_char_test: Skip # Windows disallows carriage returns in paths
 dev_fs_weird_char_test: Skip # Windows disallows question mark in paths
+uri_mappings_lookup_test: SkipByDesign # Relies on file paths in the SDK.
 
 [ $compiler == none && $runtime == vm ]
 evaluate_activation_test/instance: RuntimeError # http://dartbug.com/20047
diff --git a/runtime/observatory/tests/service/uri_mappings_lookup_test.dart b/runtime/observatory/tests/service/uri_mappings_lookup_test.dart
new file mode 100644
index 0000000..dd19f6a
--- /dev/null
+++ b/runtime/observatory/tests/service/uri_mappings_lookup_test.dart
@@ -0,0 +1,53 @@
+// 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 'dart:io';
+
+import 'package:observatory/service_io.dart';
+// Already a dependency of package:test.
+import 'package:pool/pool.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+final tests = <IsolateTest>[
+  (Isolate isolate) async {
+    final scriptUri =
+        'file://${Directory.current.path}/runtime/observatory/tests/service/uri_mappings_lookup_test.dart';
+    final unresolvedUris = <String>[
+      'package:does_not_exist/does_not_exist.dart', // invalid URI -> null
+      'dart:io', // dart:io -> org-dartlang-sdk:///sdk/lib/io/io.dart
+      'package:pool/pool.dart', // package:pool/pool.dart -> file:///some_dir/pool/lib/pool.dart
+      scriptUri, // file:///abc.dart -> file:///abc.dart
+    ];
+
+    var result = await isolate.invokeRpcNoUpgrade('lookupResolvedPackageUris', {
+      'uris': unresolvedUris,
+    });
+    expect(result['uris'], isNotNull);
+    var uris = result['uris'].cast<String?>();
+    expect(uris.length, 4);
+    expect(uris[0], isNull);
+    expect(uris[1], 'org-dartlang-sdk:///sdk/lib/io/io.dart');
+    expect(uris[2], startsWith('file:///'));
+    expect(uris[2], endsWith('third_party/pkg/pool/lib/pool.dart'));
+    expect(uris[3], scriptUri);
+
+    result = await isolate.invokeRpcNoUpgrade('lookupPackageUris', {
+      'uris': [
+        'does_not_exist.dart',
+        ...uris.sublist(1, 4),
+      ]
+    });
+    expect(result['uris'], isNotNull);
+    uris = result['uris'].cast<String?>();
+    expect(uris.length, 4);
+    expect(uris[0], isNull);
+    expect(uris[1], unresolvedUris[1]);
+    expect(uris[2], unresolvedUris[2]);
+    expect(uris[3], unresolvedUris[3]);
+  },
+];
+
+void main(args) => runIsolateTests(args, tests);
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 9b7cdf5..12ffc1c 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(51));
+    expect(result['minor'], equals(52));
     expect(result['_privateMajor'], equals(0));
     expect(result['_privateMinor'], equals(0));
   },
diff --git a/runtime/observatory_2/tests/service_2/service_2.status b/runtime/observatory_2/tests/service_2/service_2.status
index 8e5b462..c5a50bb 100644
--- a/runtime/observatory_2/tests/service_2/service_2.status
+++ b/runtime/observatory_2/tests/service_2/service_2.status
@@ -15,10 +15,8 @@
 [ $arch == arm ]
 process_service_test: Pass, Fail # Issue 24344
 
-# Test uses service API and relies on correct class names
 [ $builder_tag == obfuscated ]
-dominator_tree_vm_test: SkipByDesign
-dominator_tree_vm_with_double_field_test: SkipByDesign
+*: SkipByDesign # Responses full of obfuscated names
 
 # Tests with known analyzer issues
 [ $compiler == dart2analyzer ]
@@ -34,12 +32,14 @@
 
 [ $system == android ]
 string_escaping_test: Skip # Issue http://dartbug.com/42094
+uri_mappings_lookup_test: SkipByDesign # Relies on file paths in the SDK.
 
 [ $system == windows ]
 *: Slow
 async_generator_breakpoint_test: Skip # Issue 29145
 dev_fs_http_put_weird_char_test: Skip # Windows disallows carriage returns in paths
 dev_fs_weird_char_test: Skip # Windows disallows question mark in paths
+uri_mappings_lookup_test: SkipByDesign # Relies on file paths in the SDK.
 
 [ $compiler == none && $runtime == vm ]
 evaluate_activation_test/instance: RuntimeError # http://dartbug.com/20047
diff --git a/runtime/observatory_2/tests/service_2/uri_mappings_lookup_test.dart b/runtime/observatory_2/tests/service_2/uri_mappings_lookup_test.dart
new file mode 100644
index 0000000..832dcee
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/uri_mappings_lookup_test.dart
@@ -0,0 +1,53 @@
+// 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 'dart:io';
+
+import 'package:observatory_2/service_io.dart';
+// Already a dependency of package:test.
+import 'package:pool/pool.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+final tests = <IsolateTest>[
+  (Isolate isolate) async {
+    final scriptUri =
+        'file://${Directory.current.path}/runtime/observatory_2/tests/service_2/uri_mappings_lookup_test.dart';
+    final unresolvedUris = <String>[
+      'package:does_not_exist/does_not_exist.dart', // invalid URI -> null
+      'dart:io', // dart:io -> org-dartlang-sdk:///sdk/lib/io/io.dart
+      'package:pool/pool.dart', // package:pool/pool.dart -> file:///some_dir/pool/lib/pool.dart
+      scriptUri, // file:///abc.dart -> file:///abc.dart
+    ];
+
+    var result = await isolate.invokeRpcNoUpgrade('lookupResolvedPackageUris', {
+      'uris': unresolvedUris,
+    });
+    expect(result['uris'], isNotNull);
+    var uris = result['uris'].cast<String>();
+    expect(uris.length, 4);
+    expect(uris[0], isNull);
+    expect(uris[1], 'org-dartlang-sdk:///sdk/lib/io/io.dart');
+    expect(uris[2], startsWith('file:///'));
+    expect(uris[2], endsWith('third_party/pkg/pool/lib/pool.dart'));
+    expect(uris[3], scriptUri);
+
+    result = await isolate.invokeRpcNoUpgrade('lookupPackageUris', {
+      'uris': [
+        'does_not_exist.dart',
+        ...uris.sublist(1, 4),
+      ]
+    });
+    expect(result['uris'], isNotNull);
+    uris = result['uris'].cast<String>();
+    expect(uris.length, 4);
+    expect(uris[0], isNull);
+    expect(uris[1], unresolvedUris[1]);
+    expect(uris[2], unresolvedUris[2]);
+    expect(uris[3], unresolvedUris[3]);
+  },
+];
+
+void main(args) => runIsolateTests(args, tests);
diff --git a/runtime/platform/globals.h b/runtime/platform/globals.h
index 7d69548..8e8c737 100644
--- a/runtime/platform/globals.h
+++ b/runtime/platform/globals.h
@@ -558,6 +558,9 @@
 constexpr intptr_t RoundWordsToGB(intptr_t size_in_words) {
   return (size_in_words + (GBInWords >> 1)) >> GBInWordsLog2;
 }
+constexpr double WordsToMB(intptr_t size_in_words) {
+  return static_cast<double>(size_in_words) / MBInWords;
+}
 
 constexpr intptr_t kIntptrOne = 1;
 constexpr intptr_t kIntptrMin = (kIntptrOne << (kBitsPerWord - 1));
diff --git a/runtime/vm/compiler/jit/compiler.cc b/runtime/vm/compiler/jit/compiler.cc
index b85bfec..acdb7c6 100644
--- a/runtime/vm/compiler/jit/compiler.cc
+++ b/runtime/vm/compiler/jit/compiler.cc
@@ -1109,9 +1109,8 @@
 
 BackgroundCompiler::BackgroundCompiler(IsolateGroup* isolate_group)
     : isolate_group_(isolate_group),
-      queue_monitor_(),
+      monitor_(),
       function_queue_(new BackgroundCompilationQueue()),
-      done_monitor_(),
       running_(false),
       done_(true),
       disabled_depth_(0) {}
@@ -1122,71 +1121,56 @@
 }
 
 void BackgroundCompiler::Run() {
-  while (true) {
-    // Maybe something is already in the queue, check first before waiting
-    // to be notified.
-    bool result = Thread::EnterIsolateGroupAsHelper(
-        isolate_group_, Thread::kCompilerTask, /*bypass_safepoint=*/false);
-    ASSERT(result);
+  bool result = Thread::EnterIsolateGroupAsHelper(
+      isolate_group_, Thread::kCompilerTask, /*bypass_safepoint=*/false);
+  ASSERT(result);
+  {
+    Thread* thread = Thread::Current();
+    StackZone stack_zone(thread);
+    Zone* zone = stack_zone.GetZone();
+    HANDLESCOPE(thread);
+    Function& function = Function::Handle(zone);
+    QueueElement* element = nullptr;
     {
-      Thread* thread = Thread::Current();
-      StackZone stack_zone(thread);
-      Zone* zone = stack_zone.GetZone();
-      Function& function = Function::Handle(zone);
-      {
-        SafepointMonitorLocker ml(&queue_monitor_);
-        if (running_) {
-          function = function_queue()->PeekFunction();
-        }
+      SafepointMonitorLocker ml(&monitor_);
+      if (running_ && !function_queue()->IsEmpty()) {
+        element = function_queue()->Remove();
+        function ^= element->function();
       }
-      while (!function.IsNull()) {
-        Compiler::CompileOptimizedFunction(thread, function,
-                                           Compiler::kNoOSRDeoptId);
+    }
+    if (element != nullptr) {
+      delete element;
+      Compiler::CompileOptimizedFunction(thread, function,
+                                         Compiler::kNoOSRDeoptId);
 
-        QueueElement* qelem = NULL;
-        {
-          SafepointMonitorLocker ml(&queue_monitor_);
-          if (!running_ || function_queue()->IsEmpty()) {
-            // We are shutting down, queue was cleared.
-            function = Function::null();
-          } else {
-            qelem = function_queue()->Remove();
-            const Function& old = Function::Handle(qelem->Function());
-            // If an optimizable method is not optimized, put it back on
-            // the background queue (unless it was passed to foreground).
-            if ((!old.HasOptimizedCode() && old.IsOptimizable()) ||
-                FLAG_stress_test_background_compilation) {
-              if (Compiler::CanOptimizeFunction(thread, old)) {
-                QueueElement* repeat_qelem = new QueueElement(old);
-                function_queue()->Add(repeat_qelem);
-              }
-            }
-            function = function_queue()->PeekFunction();
+      // If an optimizable method is not optimized, put it back on
+      // the background queue (unless it was passed to foreground).
+      if ((!function.HasOptimizedCode() && function.IsOptimizable()) ||
+          FLAG_stress_test_background_compilation) {
+        if (Compiler::CanOptimizeFunction(thread, function)) {
+          SafepointMonitorLocker ml(&monitor_);
+          if (running_) {
+            QueueElement* repeat_qelem = new QueueElement(function);
+            function_queue()->Add(repeat_qelem);
           }
         }
-        if (qelem != NULL) {
-          delete qelem;
-        }
       }
     }
-    Thread::ExitIsolateGroupAsHelper(/*bypass_safepoint=*/false);
-    {
-      // Wait to be notified when the work queue is not empty.
-      MonitorLocker ml(&queue_monitor_);
-      while (function_queue()->IsEmpty() && running_) {
-        ml.Wait();
-      }
-      if (!running_) {
-        break;
-      }
-    }
-  }  // while running
-
+  }
+  Thread::ExitIsolateGroupAsHelper(/*bypass_safepoint=*/false);
   {
-    // Notify that the thread is done.
-    MonitorLocker ml_done(&done_monitor_);
-    done_ = true;
-    ml_done.NotifyAll();
+    MonitorLocker ml(&monitor_);
+    if (running_ && !function_queue()->IsEmpty() &&
+        Dart::thread_pool()->Run<BackgroundCompilerTask>(this)) {
+      // Successfully scheduled a new task.
+    } else {
+      // Background compiler done. This notification must happen after the
+      // thread leaves to group to avoid a shutdown race with the thread
+      // registry.
+      running_ = false;
+      done_ = true;
+      ml.NotifyAll();
+    }
   }
 }
 
@@ -1195,7 +1179,7 @@
   ASSERT(thread->IsMutatorThread());
   ASSERT(!thread->IsAtSafepoint(SafepointLevel::kGCAndDeopt));
 
-  SafepointMonitorLocker ml_done(&done_monitor_);
+  SafepointMonitorLocker ml(&monitor_);
   if (disabled_depth_ > 0) return false;
   if (!running_ && done_) {
     running_ = true;
@@ -1211,7 +1195,6 @@
     }
   }
 
-  SafepointMonitorLocker ml(&queue_monitor_);
   ASSERT(running_);
   if (function_queue()->ContainsObj(function)) {
     return true;
@@ -1231,21 +1214,16 @@
   ASSERT(thread->isolate() == nullptr || thread->IsMutatorThread());
   ASSERT(!thread->IsAtSafepoint(SafepointLevel::kGCAndDeopt));
 
-  SafepointMonitorLocker ml_done(&done_monitor_);
-  StopLocked(thread, &ml_done);
+  SafepointMonitorLocker ml(&monitor_);
+  StopLocked(thread, &ml);
 }
 
 void BackgroundCompiler::StopLocked(Thread* thread,
-                                    SafepointMonitorLocker* done_locker) {
-  {
-    SafepointMonitorLocker ml(&queue_monitor_);
-    running_ = false;
-    function_queue_->Clear();
-    ml.NotifyAll();  // Stop waiting for the queue.
-  }
-
+                                    SafepointMonitorLocker* locker) {
+  running_ = false;
+  function_queue_->Clear();
   while (!done_) {
-    done_locker->Wait();
+    locker->Wait();
   }
 }
 
@@ -1254,7 +1232,7 @@
   ASSERT(thread->IsMutatorThread());
   ASSERT(!thread->IsAtSafepoint(SafepointLevel::kGCAndDeopt));
 
-  SafepointMonitorLocker ml_done(&done_monitor_);
+  SafepointMonitorLocker ml(&monitor_);
   disabled_depth_--;
   if (disabled_depth_ < 0) {
     FATAL("Mismatched number of calls to BackgroundCompiler::Enable/Disable.");
@@ -1266,10 +1244,10 @@
   ASSERT(thread->IsMutatorThread());
   ASSERT(!thread->IsAtSafepoint(SafepointLevel::kGCAndDeopt));
 
-  SafepointMonitorLocker ml_done(&done_monitor_);
+  SafepointMonitorLocker ml(&monitor_);
   disabled_depth_++;
   if (done_) return;
-  StopLocked(thread, &ml_done);
+  StopLocked(thread, &ml);
 }
 
 #else  // DART_PRECOMPILED_RUNTIME
diff --git a/runtime/vm/compiler/jit/compiler.h b/runtime/vm/compiler/jit/compiler.h
index 528a964..caa0903 100644
--- a/runtime/vm/compiler/jit/compiler.h
+++ b/runtime/vm/compiler/jit/compiler.h
@@ -158,13 +158,10 @@
 
   IsolateGroup* isolate_group_;
 
-  Monitor queue_monitor_;  // Controls access to the queue.
+  Monitor monitor_;  // Controls access to the queue and running state.
   BackgroundCompilationQueue* function_queue_;
-
-  Monitor done_monitor_;    // Notify/wait that the thread is done.
   bool running_;            // While true, will try to read queue and compile.
   bool done_;               // True if the thread is done.
-
   int16_t disabled_depth_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(BackgroundCompiler);
diff --git a/runtime/vm/heap/heap.cc b/runtime/vm/heap/heap.cc
index 7d4c1a4..9c8269b 100644
--- a/runtime/vm/heap/heap.cc
+++ b/runtime/vm/heap/heap.cc
@@ -409,7 +409,7 @@
       if (phase == PageSpace::kAwaitingFinalization) {
         CollectOldSpaceGarbage(thread, GCType::kMarkSweep, GCReason::kFinalize);
       } else if (phase == PageSpace::kDone) {
-        StartConcurrentMarking(thread);
+        StartConcurrentMarking(thread, GCReason::kIdle);
       }
     }
   }
@@ -617,13 +617,23 @@
       CollectNewSpaceGarbage(thread, GCReason::kFull);
     }
 
-    StartConcurrentMarking(thread);
+    StartConcurrentMarking(thread, reason);
   }
 }
 
-void Heap::StartConcurrentMarking(Thread* thread) {
+void Heap::StartConcurrentMarking(Thread* thread, GCReason reason) {
+  GcSafepointOperationScope safepoint_operation(thread);
+  RecordBeforeGC(GCType::kStartConcurrentMark, reason);
+  VMTagScope tagScope(thread, reason == GCReason::kIdle
+                                  ? VMTag::kGCIdleTagId
+                                  : VMTag::kGCOldSpaceTagId);
   TIMELINE_FUNCTION_GC_DURATION(thread, "StartConcurrentMarking");
   old_space_.CollectGarbage(/*compact=*/false, /*finalize=*/false);
+  RecordAfterGC(GCType::kStartConcurrentMark);
+  PrintStats();
+#if defined(SUPPORT_TIMELINE)
+  PrintStatsToTimeline(&tbes, reason);
+#endif
 }
 
 void Heap::CheckFinishConcurrentMarking(Thread* thread) {
@@ -852,6 +862,8 @@
   switch (type) {
     case GCType::kScavenge:
       return "Scavenge";
+    case GCType::kStartConcurrentMark:
+      return "StartCMark";
     case GCType::kMarkSweep:
       return "MarkSweep";
     case GCType::kMarkCompact:
@@ -1018,8 +1030,6 @@
   stats_.before_.old_ = old_space_.GetCurrentUsage();
   for (int i = 0; i < GCStats::kTimeEntries; i++)
     stats_.times_[i] = 0;
-  for (int i = 0; i < GCStats::kDataEntries; i++)
-    stats_.data_[i] = 0;
 }
 
 static double AvgCollectionPeriod(int64_t run_time, intptr_t collections) {
@@ -1119,34 +1129,33 @@
   if ((FLAG_verbose_gc_hdr != 0) &&
       (((stats_.num_ - 1) % FLAG_verbose_gc_hdr) == 0)) {
     OS::PrintErr(
-        "[              |                      |     |       |      "
+        "[              |                          |     |       |      "
         "| new gen     | new gen     | new gen "
         "| old gen       | old gen       | old gen     "
-        "| sweep | safe- | roots/| stbuf/| tospc/| weaks/|               ]\n"
-        "[ GC isolate   | space (reason)       | GC# | start | time "
-        "| used (kB)   | capacity kB | external"
-        "| used (kB)     | capacity (kB) | external kB "
-        "| thread| point |marking| reset | sweep |swplrge| data          ]\n"
-        "[              |                      |     |  (s)  | (ms) "
+        "| sweep | safe- | roots/| stbuf/| tospc/| weaks/  ]\n"
+        "[ GC isolate   | space (reason)           | GC# | start | time "
+        "| used (MB)   | capacity MB | external"
+        "| used (MB)     | capacity (MB) | external MB "
+        "| thread| point |marking| reset | sweep |swplrge  ]\n"
+        "[              |                          |     |  (s)  | (ms) "
         "|before| after|before| after| b4 |aftr"
         "| before| after | before| after |before| after"
-        "| (ms)  | (ms)  | (ms)  | (ms)  | (ms)  | (ms)  |               ]\n");
+        "| (ms)  | (ms)  | (ms)  | (ms)  | (ms)  | (ms)    ]\n");
   }
 
   // clang-format off
   OS::PrintErr(
-    "[ %-13.13s, %10s(%9s), "  // GC(isolate-group), type(reason)
+    "[ %-13.13s, %11s(%12s), "  // GC(isolate-group), type(reason)
     "%4" Pd ", "  // count
     "%6.2f, "  // start time
     "%5.1f, "  // total time
-    "%5" Pd ", %5" Pd ", "  // new gen: in use before/after
-    "%5" Pd ", %5" Pd ", "  // new gen: capacity before/after
-    "%3" Pd ", %3" Pd ", "  // new gen: external before/after
-    "%6" Pd ", %6" Pd ", "  // old gen: in use before/after
-    "%6" Pd ", %6" Pd ", "  // old gen: capacity before/after
-    "%5" Pd ", %5" Pd ", "  // old gen: external before/after
+    "%5.1f, %5.1f, "  // new gen: in use before/after
+    "%5.1f, %5.1f, "   // new gen: capacity before/after
+    "%3.1f, %3.1f, "   // new gen: external before/after
+    "%6.1f, %6.1f, "   // old gen: in use before/after
+    "%6.1f, %6.1f, "   // old gen: capacity before/after
+    "%5.1f, %5.1f, "   // old gen: external before/after
     "%6.2f, %6.2f, %6.2f, %6.2f, %6.2f, %6.2f, "  // times
-    "%" Pd ", %" Pd ", %" Pd ", %" Pd ", "  // data
     "]\n",  // End with a comma to make it easier to import in spreadsheets.
     isolate_group()->source()->name,
     GCTypeToString(stats_.type_),
@@ -1155,28 +1164,24 @@
     MicrosecondsToSeconds(isolate_group_->UptimeMicros()),
     MicrosecondsToMilliseconds(stats_.after_.micros_ -
                                stats_.before_.micros_),
-    RoundWordsToKB(stats_.before_.new_.used_in_words),
-    RoundWordsToKB(stats_.after_.new_.used_in_words),
-    RoundWordsToKB(stats_.before_.new_.capacity_in_words),
-    RoundWordsToKB(stats_.after_.new_.capacity_in_words),
-    RoundWordsToKB(stats_.before_.new_.external_in_words),
-    RoundWordsToKB(stats_.after_.new_.external_in_words),
-    RoundWordsToKB(stats_.before_.old_.used_in_words),
-    RoundWordsToKB(stats_.after_.old_.used_in_words),
-    RoundWordsToKB(stats_.before_.old_.capacity_in_words),
-    RoundWordsToKB(stats_.after_.old_.capacity_in_words),
-    RoundWordsToKB(stats_.before_.old_.external_in_words),
-    RoundWordsToKB(stats_.after_.old_.external_in_words),
+    WordsToMB(stats_.before_.new_.used_in_words),
+    WordsToMB(stats_.after_.new_.used_in_words),
+    WordsToMB(stats_.before_.new_.capacity_in_words),
+    WordsToMB(stats_.after_.new_.capacity_in_words),
+    WordsToMB(stats_.before_.new_.external_in_words),
+    WordsToMB(stats_.after_.new_.external_in_words),
+    WordsToMB(stats_.before_.old_.used_in_words),
+    WordsToMB(stats_.after_.old_.used_in_words),
+    WordsToMB(stats_.before_.old_.capacity_in_words),
+    WordsToMB(stats_.after_.old_.capacity_in_words),
+    WordsToMB(stats_.before_.old_.external_in_words),
+    WordsToMB(stats_.after_.old_.external_in_words),
     MicrosecondsToMilliseconds(stats_.times_[0]),
     MicrosecondsToMilliseconds(stats_.times_[1]),
     MicrosecondsToMilliseconds(stats_.times_[2]),
     MicrosecondsToMilliseconds(stats_.times_[3]),
     MicrosecondsToMilliseconds(stats_.times_[4]),
-    MicrosecondsToMilliseconds(stats_.times_[5]),
-    stats_.data_[0],
-    stats_.data_[1],
-    stats_.data_[2],
-    stats_.data_[3]);
+    MicrosecondsToMilliseconds(stats_.times_[5]));
   // clang-format on
 #endif  // !defined(PRODUCT)
 }
diff --git a/runtime/vm/heap/heap.h b/runtime/vm/heap/heap.h
index a3973c1..04e8a62 100644
--- a/runtime/vm/heap/heap.h
+++ b/runtime/vm/heap/heap.h
@@ -126,7 +126,7 @@
   void CollectAllGarbage(GCReason reason = GCReason::kFull);
 
   void CheckStartConcurrentMarking(Thread* thread, GCReason reason);
-  void StartConcurrentMarking(Thread* thread);
+  void StartConcurrentMarking(Thread* thread, GCReason reason);
   void CheckFinishConcurrentMarking(Thread* thread);
   void WaitForMarkerTasks(Thread* thread);
   void WaitForSweeperTasks(Thread* thread);
@@ -261,11 +261,6 @@
     stats_.times_[id] = micros;
   }
 
-  void RecordData(int id, intptr_t value) {
-    ASSERT((id >= 0) && (id < GCStats::kDataEntries));
-    stats_.data_[id] = value;
-  }
-
   void UpdateGlobalMaxUsed();
 
   static bool IsAllocatableInNewSpace(intptr_t size) {
@@ -327,12 +322,10 @@
     };
 
     enum { kTimeEntries = 6 };
-    enum { kDataEntries = 4 };
 
     Data before_;
     Data after_;
     int64_t times_[kTimeEntries];
-    intptr_t data_[kDataEntries];
 
    private:
     DISALLOW_COPY_AND_ASSIGN(GCStats);
diff --git a/runtime/vm/heap/pages.cc b/runtime/vm/heap/pages.cc
index db1541a..f17e8e6 100644
--- a/runtime/vm/heap/pages.cc
+++ b/runtime/vm/heap/pages.cc
@@ -1526,7 +1526,6 @@
   ASSERT(end >= start);
   history_.AddGarbageCollectionTime(start, end);
   const int gc_time_fraction = history_.GarbageCollectionTimeFraction();
-  heap_->RecordData(PageSpace::kGCTimeFraction, gc_time_fraction);
 
   // Assume garbage increases linearly with allocation:
   // G = kA, and estimate k from the previous cycle.
@@ -1544,7 +1543,6 @@
         1.0, garbage / static_cast<double>(allocated_since_previous_gc));
 
     const int garbage_ratio = static_cast<int>(k * 100);
-    heap_->RecordData(PageSpace::kGarbageRatio, garbage_ratio);
 
     // Define GC to be 'worthwhile' iff at least fraction t of heap is garbage.
     double t = 1.0 - desired_utilization_;
@@ -1595,10 +1593,8 @@
       }
     }
   } else {
-    heap_->RecordData(PageSpace::kGarbageRatio, 100);
     grow_heap = 0;
   }
-  heap_->RecordData(PageSpace::kPageGrowth, grow_heap);
   last_usage_ = after;
 
   intptr_t max_capacity_in_words = heap_->old_space()->max_capacity_in_words_;
@@ -1688,7 +1684,7 @@
   }
 #endif
 
-  if (FLAG_log_growth) {
+  if (FLAG_log_growth || FLAG_verbose_gc) {
     THR_Print("%s: threshold=%" Pd "kB, idle_threshold=%" Pd "kB, reason=%s\n",
               heap_->isolate_group()->source()->name,
               hard_gc_threshold_in_words_ / KBInWords,
diff --git a/runtime/vm/heap/pages.h b/runtime/vm/heap/pages.h
index 8a91944..9d039e1 100644
--- a/runtime/vm/heap/pages.h
+++ b/runtime/vm/heap/pages.h
@@ -536,11 +536,6 @@
     kResetFreeLists = 3,
     kSweepPages = 4,
     kSweepLargePages = 5,
-    // Data
-    kGarbageRatio = 0,
-    kGCTimeFraction = 1,
-    kPageGrowth = 2,
-    kAllowedGrowth = 3
   };
 
   uword TryAllocateInternal(intptr_t size,
diff --git a/runtime/vm/heap/scavenger.cc b/runtime/vm/heap/scavenger.cc
index 7cc5b8c..592627b 100644
--- a/runtime/vm/heap/scavenger.cc
+++ b/runtime/vm/heap/scavenger.cc
@@ -1148,13 +1148,10 @@
   // Grab the deduplication sets out of the isolate's consolidated store buffer.
   StoreBuffer* store_buffer = heap_->isolate_group()->store_buffer();
   StoreBufferBlock* pending = blocks_;
-  intptr_t total_count = 0;
   while (pending != nullptr) {
     StoreBufferBlock* next = pending->next();
     // Generated code appends to store buffers; tell MemorySanitizer.
     MSAN_UNPOISON(pending, sizeof(*pending));
-    intptr_t count = pending->Count();
-    total_count += count;
     while (!pending->IsEmpty()) {
       ObjectPtr raw_object = pending->Pop();
       ASSERT(!raw_object->IsForwardingCorpse());
@@ -1172,10 +1169,6 @@
   }
   // Done iterating through old objects remembered in the store buffers.
   visitor->VisitingOldObject(nullptr);
-
-  heap_->RecordData(kStoreBufferEntries, total_count);
-  heap_->RecordData(kDataUnused1, 0);
-  heap_->RecordData(kDataUnused2, 0);
 }
 
 template <bool parallel>
diff --git a/runtime/vm/heap/scavenger.h b/runtime/vm/heap/scavenger.h
index f538f3a..ca75d9a 100644
--- a/runtime/vm/heap/scavenger.h
+++ b/runtime/vm/heap/scavenger.h
@@ -366,11 +366,6 @@
     kIterateStoreBuffers = 3,
     kProcessToSpace = 4,
     kIterateWeaks = 5,
-    // Data
-    kStoreBufferEntries = 0,
-    kDataUnused1 = 1,
-    kDataUnused2 = 2,
-    kToKBAfterStoreBuffer = 3
   };
 
   uword TryAllocateFromTLAB(Thread* thread, intptr_t size) {
diff --git a/runtime/vm/heap/spaces.h b/runtime/vm/heap/spaces.h
index 6b60e6c..f6827aa 100644
--- a/runtime/vm/heap/spaces.h
+++ b/runtime/vm/heap/spaces.h
@@ -31,6 +31,7 @@
 
 enum class GCType {
   kScavenge,
+  kStartConcurrentMark,
   kMarkSweep,
   kMarkCompact,
 };
diff --git a/runtime/vm/isolate_reload.cc b/runtime/vm/isolate_reload.cc
index 18a7bb6..11d116a 100644
--- a/runtime/vm/isolate_reload.cc
+++ b/runtime/vm/isolate_reload.cc
@@ -830,6 +830,11 @@
     success = false;
   }
 
+  Array& null_array = Array::Handle(Z);
+  // Invalidate the URI mapping caches.
+  IG->object_store()->set_uri_to_resolved_uri_map(null_array);
+  IG->object_store()->set_resolved_uri_to_uri_map(null_array);
+
   // Re-queue any shutdown requests so they can inform each isolate's own thread
   // to shut down.
   if (result.IsUnwindError()) {
diff --git a/runtime/vm/object_store.h b/runtime/vm/object_store.h
index ebb2d60..3b308c6 100644
--- a/runtime/vm/object_store.h
+++ b/runtime/vm/object_store.h
@@ -143,6 +143,9 @@
   RW(Library, _vmservice_library)                                              \
   RW(GrowableObjectArray, libraries)                                           \
   RW(Array, libraries_map)                                                     \
+  RW(Array, uri_to_resolved_uri_map)                                           \
+  RW(Array, resolved_uri_to_uri_map)                                           \
+  RW(Smi, last_libraries_count)                                                \
   RW(Array, loading_units)                                                     \
   RW(GrowableObjectArray, closure_functions)                                   \
   RW(GrowableObjectArray, pending_classes)                                     \
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index a0a5b9c..a75737e 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -4948,6 +4948,159 @@
   Service::PrintJSONForVM(js, false);
 }
 
+static intptr_t ParseJSONArray(Thread* thread,
+                               const char* str,
+                               const GrowableObjectArray& elements) {
+  ASSERT(str != nullptr);
+  ASSERT(thread != nullptr);
+  Zone* zone = thread->zone();
+  intptr_t n = strlen(str);
+  if (n < 2) {
+    return -1;
+  }
+  intptr_t start = 1;
+  while (start < n) {
+    intptr_t end = start;
+    while ((str[end + 1] != ',') && (str[end + 1] != ']')) {
+      end++;
+    }
+    if (end == start) {
+      // Empty element
+      break;
+    }
+    String& element = String::Handle(
+        zone, String::FromUTF8(reinterpret_cast<const uint8_t*>(&str[start]),
+                               end - start + 1));
+    elements.Add(element);
+    start = end + 3;
+  }
+  return 0;
+}
+
+class UriMappingTraits {
+ public:
+  static const char* Name() { return "UriMappingTraits"; }
+  static bool ReportStats() { return false; }
+
+  static bool IsMatch(const Object& a, const Object& b) {
+    const String& a_str = String::Cast(a);
+    const String& b_str = String::Cast(b);
+
+    ASSERT(a_str.HasHash() && b_str.HasHash());
+    return a_str.Equals(b_str);
+  }
+
+  static uword Hash(const Object& key) { return String::Cast(key).Hash(); }
+
+  static ObjectPtr NewKey(const String& str) { return str.ptr(); }
+};
+
+typedef UnorderedHashMap<UriMappingTraits> UriMapping;
+
+static void PopulateUriMappings(Thread* thread) {
+  Zone* zone = thread->zone();
+  auto object_store = thread->isolate_group()->object_store();
+  UriMapping uri_to_resolved_uri(HashTables::New<UriMapping>(16, Heap::kOld));
+  UriMapping resolved_uri_to_uri(HashTables::New<UriMapping>(16, Heap::kOld));
+
+  const auto& libs =
+      GrowableObjectArray::Handle(zone, object_store->libraries());
+  intptr_t num_libs = libs.Length();
+
+  Library& lib = Library::Handle(zone);
+  Script& script = Script::Handle(zone);
+  Array& scripts = Array::Handle(zone);
+  String& uri = String::Handle(zone);
+  String& resolved_uri = String::Handle(zone);
+
+  for (intptr_t i = 0; i < num_libs; ++i) {
+    lib ^= libs.At(i);
+    scripts ^= lib.LoadedScripts();
+    intptr_t num_scripts = scripts.Length();
+    for (intptr_t j = 0; j < num_scripts; ++j) {
+      script ^= scripts.At(j);
+      uri ^= script.url();
+      resolved_uri ^= script.resolved_url();
+      uri_to_resolved_uri.UpdateOrInsert(uri, resolved_uri);
+      resolved_uri_to_uri.UpdateOrInsert(resolved_uri, uri);
+    }
+  }
+
+  object_store->set_uri_to_resolved_uri_map(uri_to_resolved_uri.Release());
+  object_store->set_resolved_uri_to_uri_map(resolved_uri_to_uri.Release());
+  Smi& count = Smi::Handle(zone, Smi::New(num_libs));
+  object_store->set_last_libraries_count(count);
+}
+
+static void LookupScriptUrisImpl(Thread* thread,
+                                 JSONStream* js,
+                                 bool lookup_resolved) {
+  Zone* zone = thread->zone();
+  auto object_store = thread->isolate_group()->object_store();
+
+  const auto& libs =
+      GrowableObjectArray::Handle(zone, object_store->libraries());
+  Smi& last_libraries_count =
+      Smi::Handle(zone, object_store->last_libraries_count());
+  if ((object_store->uri_to_resolved_uri_map() == Array::null()) ||
+      (object_store->resolved_uri_to_uri_map() == Array::null()) ||
+      (last_libraries_count.Value() != libs.Length())) {
+    PopulateUriMappings(thread);
+  }
+  const char* uris_arg = js->LookupParam("uris");
+  if (uris_arg == nullptr) {
+    PrintMissingParamError(js, "uris");
+    return;
+  }
+
+  const GrowableObjectArray& uris =
+      GrowableObjectArray::Handle(zone, GrowableObjectArray::New());
+  intptr_t uris_length = ParseJSONArray(thread, uris_arg, uris);
+  if (uris_length < 0) {
+    PrintInvalidParamError(js, "uris");
+    return;
+  }
+
+  UriMapping map(lookup_resolved ? object_store->uri_to_resolved_uri_map()
+                                 : object_store->resolved_uri_to_uri_map());
+  JSONObject jsobj(js);
+  jsobj.AddProperty("type", "UriList");
+
+  {
+    JSONArray uris_array(&jsobj, "uris");
+    String& uri = String::Handle(zone);
+    String& res = String::Handle(zone);
+    for (intptr_t i = 0; i < uris.Length(); ++i) {
+      uri ^= uris.At(i);
+      res ^= map.GetOrNull(uri);
+      if (res.IsNull()) {
+        uris_array.AddValueNull();
+      } else {
+        uris_array.AddValue(res.ToCString());
+      }
+    }
+  }
+  map.Release();
+}
+
+static const MethodParameter* const lookup_resolved_package_uris_params[] = {
+    ISOLATE_PARAMETER,
+    nullptr,
+};
+
+static void LookupResolvedPackageUris(Thread* thread, JSONStream* js) {
+  LookupScriptUrisImpl(thread, js, true);
+}
+
+static const MethodParameter* const lookup_package_uris_params[] = {
+    ISOLATE_PARAMETER,
+    nullptr,
+};
+
+static void LookupPackageUris(Thread* thread, JSONStream* js) {
+  LookupScriptUrisImpl(thread, js, false);
+}
+
 static const char* const exception_pause_mode_names[] = {
     "All",
     "None",
@@ -5359,6 +5512,10 @@
     get_reachable_size_params },
   { "_getRetainedSize", GetRetainedSize,
     get_retained_size_params },
+  { "lookupResolvedPackageUris", LookupResolvedPackageUris,
+    lookup_resolved_package_uris_params },
+  { "lookupPackageUris", LookupPackageUris,
+    lookup_package_uris_params },
   { "getRetainingPath", GetRetainingPath,
     get_retaining_path_params },
   { "getScripts", GetScripts,
diff --git a/runtime/vm/service.h b/runtime/vm/service.h
index 6d32257..c3fe1f2 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 51
+#define SERVICE_PROTOCOL_MINOR_VERSION 52
 
 class Array;
 class EmbedderServiceHandler;
diff --git a/runtime/vm/service/service.md b/runtime/vm/service/service.md
index ad7d3bc..ba4add1 100644
--- a/runtime/vm/service/service.md
+++ b/runtime/vm/service/service.md
@@ -1,8 +1,8 @@
-# Dart VM Service Protocol 3.51
+# Dart VM Service Protocol 3.52
 
 > Please post feedback to the [observatory-discuss group][discuss-list]
 
-This document describes of _version 3.51_ of the Dart VM Service Protocol. This
+This document describes of _version 3.52_ 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.
@@ -62,6 +62,8 @@
   - [getVMTimelineFlags](#getvmtimelineflags)
   - [getVMTimelineMicros](#getvmtimelinemicros)
   - [invoke](#invoke)
+  - [lookupResolvedPackageUris](#lookupresolvedpackageuris)
+  - [lookupPackageUris](#lookuppackageuris)
   - [pause](#pause)
   - [kill](#kill)
   - [registerService](#registerService)
@@ -142,7 +144,8 @@
   - [Timestamp](#timestamp)
   - [TypeArguments](#typearguments)
   - [TypeParameters](#typeparameters)[
-  - [UresolvedSourceLocation](#unresolvedsourcelocation)
+  - [UnresolvedSourceLocation](#unresolvedsourcelocation)
+  - [UriList](#urilist)
   - [Version](#version)
   - [VM](#vm)
   - [WebSocketTarget](#websockettarget)
@@ -1207,6 +1210,44 @@
 
 See [Success](#success).
 
+### lookupResolvedPackageUris
+
+```
+UriList lookupResolvedPackageUris(string isolateId, string[] uris)
+```
+
+The _lookupResolvedPackageUris_ RPC is used to convert a list of URIs to their
+resolved (or absolute) paths. For example, URIs passed to this RPC are mapped in
+the following ways:
+
+- `dart:io` -> `org-dartlang-sdk:///sdk/lib/io/io.dart`
+- `package:test/test.dart` -> `file:///$PACKAGE_INSTALLATION_DIR/lib/test.dart`
+- `file:///foo/bar/bazz.dart` -> `file:///foo/bar/bazz.dart`
+
+If a URI is not known, the corresponding entry in the [UriList] response will be
+`null`.
+
+See [UriList](#urilist).
+
+### lookupPackageUris
+
+```
+UriList lookupPackageUris(string isolateId, string[] uris)
+```
+
+The _lookupPackageUris_ RPC is used to convert a list of URIs to their
+unresolved paths. For example, URIs passed to this RPC are mapped in the
+following ways:
+
+- `org-dartlang-sdk:///sdk/lib/io/io.dart` -> `dart:io`
+- `file:///$PACKAGE_INSTALLATION_DIR/lib/test.dart` -> `package:test/test.dart`
+- `file:///foo/bar/bazz.dart` -> `file:///foo/bar/bazz.dart`
+
+If a URI is not known, the corresponding entry in the [UriList] response will be
+`null`.
+
+See [UriList](#urilist).
+
 ### registerService
 
 ```
@@ -4057,6 +4098,15 @@
 The _column_ field will only be present when the breakpoint was
 specified with a specific column number.
 
+### UriList
+
+```
+class UriList extends Response {
+  // A list of URIs.
+  (string|Null)[] uris;
+}
+```
+
 ### Version
 
 ```
@@ -4184,5 +4234,5 @@
 3.49 | Added `CpuSamples` event kind, and `cpuSamples` property to `Event`.
 3.50 | Added `returnType`, `parameters`, and `typeParameters` to `@Instance`, and `implicit` to `@Function`. Added `Parameter` type.
 3.51 | Added optional `reportLines` parameter to `getSourceReport` RPC.
-
+3.52 | Added `lookupResolvedPackageUris` and `lookupPackageUris` RPCs and `UriList` type.
 [discuss-list]: https://groups.google.com/a/dartlang.org/forum/#!forum/observatory-discuss
diff --git a/tests/language/constructor/reference_test.dart b/tests/language/constructor/reference_test.dart
index 6828160..06755d1 100644
--- a/tests/language/constructor/reference_test.dart
+++ b/tests/language/constructor/reference_test.dart
@@ -97,8 +97,7 @@
   Foo.bar<int>.baz();
 //       ^^^^^
 // [analyzer] COMPILE_TIME_ERROR.WRONG_NUMBER_OF_TYPE_ARGUMENTS_CONSTRUCTOR
-//             ^
-// [cfe] The method 'baz' isn't defined for the class 'Foo<int> Function()'.
+// [cfe] A constructor tear-off can't have type arguments after the constructor name.
   Foo.bar.baz<int>();
 //        ^^^
 // [analyzer] COMPILE_TIME_ERROR.UNDEFINED_METHOD
diff --git a/tools/VERSION b/tools/VERSION
index c49f227..54f7e08 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 262
+PRERELEASE 263
 PRERELEASE_PATCH 0
\ No newline at end of file