Version 2.14.0-115.0.dev

Merge commit 'b8a1cd485090a60ac49668dcbe3a7af311e5983c' into 'dev'
diff --git a/pkg/analysis_server/bin/server.dart b/pkg/analysis_server/bin/server.dart
index a873eb1..7810101 100644
--- a/pkg/analysis_server/bin/server.dart
+++ b/pkg/analysis_server/bin/server.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/starter.dart';
 
 /// Create and run an analysis server.
diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart
index a9dde60..56ca0de 100644
--- a/pkg/analysis_server/lib/src/analysis_server.dart
+++ b/pkg/analysis_server/lib/src/analysis_server.dart
@@ -685,6 +685,7 @@
         _notificationManager.recordAnalysisErrors(NotificationManager.serverId,
             path, server.doAnalysisError_listFromEngine(result));
       }
+      analysisServer.getDocumentationCacheFor(result)?.cacheFromResult(result);
       var unit = result.unit;
       if (unit != null) {
         if (analysisServer._hasAnalysisServiceSubscription(
diff --git a/pkg/analysis_server/lib/src/analysis_server_abstract.dart b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
index 09a9fbe..448bc17 100644
--- a/pkg/analysis_server/lib/src/analysis_server_abstract.dart
+++ b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
@@ -15,6 +15,7 @@
 import 'package:analysis_server/src/plugin/plugin_watcher.dart';
 import 'package:analysis_server/src/server/crash_reporting_attachments.dart';
 import 'package:analysis_server/src/server/diagnostic_server.dart';
+import 'package:analysis_server/src/services/completion/dart/documentation_cache.dart';
 import 'package:analysis_server/src/services/correction/namespace.dart';
 import 'package:analysis_server/src/services/pub/pub_api.dart';
 import 'package:analysis_server/src/services/pub/pub_package_service.dart';
@@ -25,6 +26,7 @@
 import 'package:analysis_server/src/utilities/null_string_sink.dart';
 import 'package:analysis_server/src/utilities/request_statistics.dart';
 import 'package:analysis_server/src/utilities/tee_string_sink.dart';
+import 'package:analyzer/dart/analysis/analysis_context.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/analysis/session.dart';
 import 'package:analyzer/dart/ast/ast.dart';
@@ -83,6 +85,10 @@
   DeclarationsTracker? declarationsTracker;
   DeclarationsTrackerData? declarationsTrackerData;
 
+  /// A map from analysis contexts to the documentation cache associated with
+  /// each context.
+  Map<AnalysisContext, DocumentationCache> documentationForContext = {};
+
   /// The DiagnosticServer for this AnalysisServer. If available, it can be used
   /// to start an http diagnostics server or return the port for an existing
   /// server.
@@ -217,6 +223,7 @@
 
   void addContextsToDeclarationsTracker() {
     declarationsTracker?.discardContexts();
+    documentationForContext.clear();
     for (var driver in driverMap.values) {
       declarationsTracker?.addContext(driver.analysisContext!);
       driver.resetUriResolution();
@@ -275,6 +282,19 @@
         DartdocDirectiveInfo();
   }
 
+  /// Return the object used to cache the documentation for elements in the
+  /// context that produced the [result], or `null` if there is no cache for the
+  /// context.
+  DocumentationCache? getDocumentationCacheFor(ResolvedUnitResult result) {
+    var context = result.session.analysisContext;
+    var tracker = declarationsTracker?.getContext(context);
+    if (tracker == null) {
+      return null;
+    }
+    return documentationForContext.putIfAbsent(
+        context, () => DocumentationCache(tracker.dartdocDirectiveInfo));
+  }
+
   /// Return a [Future] that completes with the [Element] at the given
   /// [offset] of the given [file], or with `null` if there is no node at the
   /// [offset] or the node does not have an element.
diff --git a/pkg/analysis_server/lib/src/domain_completion.dart b/pkg/analysis_server/lib/src/domain_completion.dart
index 4fe65b8..78db63a 100644
--- a/pkg/analysis_server/lib/src/domain_completion.dart
+++ b/pkg/analysis_server/lib/src/domain_completion.dart
@@ -108,7 +108,9 @@
       await perf.runAsync(contributorTag, (performance) async {
         try {
           suggestions.addAll(
-            await manager.computeSuggestions(performance, request),
+            await manager.computeSuggestions(performance, request,
+                documentationCache:
+                    server.getDocumentationCacheFor(request.result)),
           );
         } on AbortCompletion {
           suggestions.clear();
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 1ea7a5a..aea0553 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
@@ -9,6 +9,7 @@
 import 'package:analysis_server/src/services/completion/completion_performance.dart';
 import 'package:analysis_server/src/services/completion/dart/arglist_contributor.dart';
 import 'package:analysis_server/src/services/completion/dart/combinator_contributor.dart';
+import 'package:analysis_server/src/services/completion/dart/documentation_cache.dart';
 import 'package:analysis_server/src/services/completion/dart/extension_member_contributor.dart';
 import 'package:analysis_server/src/services/completion/dart/feature_computer.dart';
 import 'package:analysis_server/src/services/completion/dart/field_formal_contributor.dart';
@@ -93,6 +94,7 @@
     bool enableOverrideContributor = true,
     bool enableUriContributor = true,
     CompletionPreference? completionPreference,
+    DocumentationCache? documentationCache,
   }) async {
     request.checkAborted();
     var pathContext = request.resourceProvider.pathContext;
@@ -105,6 +107,7 @@
       request,
       dartdocDirectiveInfo,
       completionPreference: completionPreference,
+      documentationCache: documentationCache,
     );
 
     // Don't suggest in comments.
@@ -297,6 +300,8 @@
   @override
   final CompletionPreference completionPreference;
 
+  final DocumentationCache? documentationCache;
+
   DartCompletionRequestImpl._(
       this.result,
       this.resourceProvider,
@@ -308,7 +313,8 @@
       this.dartdocDirectiveInfo,
       this._originalRequest,
       this.performance,
-      {CompletionPreference? completionPreference})
+      {CompletionPreference? completionPreference,
+      this.documentationCache})
       : featureComputer =
             FeatureComputer(result.typeSystem, result.typeProvider),
         completionPreference =
@@ -431,7 +437,8 @@
       OperationPerformanceImpl performance,
       CompletionRequest request,
       DartdocDirectiveInfo? dartdocDirectiveInfo,
-      {CompletionPreference? completionPreference}) async {
+      {CompletionPreference? completionPreference,
+      DocumentationCache? documentationCache}) async {
     request.checkAborted();
 
     return performance.run(
@@ -453,6 +460,7 @@
           request,
           (request as CompletionRequestImpl).performance,
           completionPreference: completionPreference,
+          documentationCache: documentationCache,
         );
       },
     );
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/documentation_cache.dart b/pkg/analysis_server/lib/src/services/completion/dart/documentation_cache.dart
new file mode 100644
index 0000000..5724484
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/completion/dart/documentation_cache.dart
@@ -0,0 +1,196 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/computer/computer_hover.dart';
+import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/src/dartdoc/dartdoc_directive_info.dart';
+
+/// Cached data about the documentation associated with the elements declared in
+/// a single analysis context.
+class DocumentationCache {
+  /// A shared instance for elements that have no documentation.
+  static final DocumentationWithSummary _emptyDocs =
+      DocumentationWithSummary(full: '', summary: '');
+
+  /// The object used to compute the documentation associated with a single
+  /// element.
+  final DartdocDirectiveInfo dartdocDirectiveInfo;
+
+  /// The documentation associated with the elements that have been cached. The
+  /// cache is keyed by the path of the file containing the declaration of the
+  /// element and the qualified name of the element.
+  final Map<String, Map<String, DocumentationWithSummary>> documentationCache =
+      {};
+
+  /// Initialize a newly created cache.
+  DocumentationCache(this.dartdocDirectiveInfo);
+
+  /// Fill the cache with data from the [result].
+  void cacheFromResult(ResolvedUnitResult result) {
+    var element = result.unit?.declaredElement;
+    if (element != null) {
+      _cacheFromElement(element);
+      for (var library in result.libraryElement.importedLibraries) {
+        _cacheLibrary(library);
+      }
+    }
+  }
+
+  /// Return the data cached for the given [element], or `null` if there is no
+  /// cached data.
+  DocumentationWithSummary? dataFor(Element element) {
+    var parent = element.enclosingElement;
+    if (parent == null) {
+      return null;
+    }
+    var key = element.name;
+    if (key == null) {
+      return null;
+    }
+    if (parent is! CompilationUnitElement) {
+      var parentName = parent.name;
+      if (parentName == null) {
+        return null;
+      }
+      key = '$parentName.$key';
+      parent = parent.enclosingElement;
+    }
+    if (parent is CompilationUnitElement) {
+      var elementMap = documentationCache[_keyForUnit(parent)];
+      return elementMap?[key];
+    }
+    return null;
+  }
+
+  /// Fill the cache with data from the [compilationUnit].
+  void _cacheFromElement(CompilationUnitElement compilationUnit) {
+    var elementMap =
+        documentationCache.putIfAbsent(_keyForUnit(compilationUnit), () => {});
+    for (var element in compilationUnit.accessors) {
+      if (!element.isSynthetic) {
+        elementMap.cacheTopLevelElement(dartdocDirectiveInfo, element);
+      }
+    }
+    for (var element in compilationUnit.enums) {
+      var parentKey =
+          elementMap.cacheTopLevelElement(dartdocDirectiveInfo, element);
+      if (parentKey != null) {
+        for (var member in element.fields) {
+          elementMap.cacheMember(dartdocDirectiveInfo, parentKey, member);
+        }
+      }
+    }
+    for (var element in compilationUnit.extensions) {
+      var parentKey =
+          elementMap.cacheTopLevelElement(dartdocDirectiveInfo, element);
+      if (parentKey != null) {
+        for (var member in element.accessors) {
+          if (!element.isSynthetic) {
+            elementMap.cacheMember(dartdocDirectiveInfo, parentKey, member);
+          }
+        }
+        for (var member in element.fields) {
+          if (!element.isSynthetic) {
+            elementMap.cacheMember(dartdocDirectiveInfo, parentKey, member);
+          }
+        }
+        for (var member in element.methods) {
+          elementMap.cacheMember(dartdocDirectiveInfo, parentKey, member);
+        }
+      }
+    }
+    for (var element in compilationUnit.functions) {
+      elementMap.cacheTopLevelElement(dartdocDirectiveInfo, element);
+    }
+    for (var element in [...compilationUnit.mixins, ...compilationUnit.types]) {
+      var parentKey =
+          elementMap.cacheTopLevelElement(dartdocDirectiveInfo, element);
+      if (parentKey != null) {
+        for (var member in element.accessors) {
+          if (!element.isSynthetic) {
+            elementMap.cacheMember(dartdocDirectiveInfo, parentKey, member);
+          }
+        }
+        for (var member in element.fields) {
+          if (!element.isSynthetic) {
+            elementMap.cacheMember(dartdocDirectiveInfo, parentKey, member);
+          }
+        }
+        for (var member in element.methods) {
+          elementMap.cacheMember(dartdocDirectiveInfo, parentKey, member);
+        }
+      }
+    }
+    for (var element in compilationUnit.topLevelVariables) {
+      if (!element.isSynthetic) {
+        elementMap.cacheTopLevelElement(dartdocDirectiveInfo, element);
+      }
+    }
+    for (var element in compilationUnit.typeAliases) {
+      elementMap.cacheTopLevelElement(dartdocDirectiveInfo, element);
+    }
+  }
+
+  /// Cache the data for the given [library] and every library exported from it
+  /// if it hasn't already been cached.
+  void _cacheLibrary(LibraryElement library) {
+    if (_hasDataFor(library.definingCompilationUnit)) {
+      return;
+    }
+    for (var unit in library.units) {
+      _cacheFromElement(unit);
+    }
+    for (var exported in library.exportedLibraries) {
+      _cacheLibrary(exported);
+    }
+  }
+
+  /// Return `true` if the cache contains data for the [compilationUnit].
+  bool _hasDataFor(CompilationUnitElement compilationUnit) {
+    return documentationCache.containsKey(_keyForUnit(compilationUnit));
+  }
+
+  /// Return the key used in the [documentationCache] for the [compilationUnit].
+  String _keyForUnit(CompilationUnitElement compilationUnit) =>
+      compilationUnit.source.fullName;
+}
+
+extension on Map<String, DocumentationWithSummary> {
+  /// Cache the data associated with the top-level [element], and return the
+  /// [key] used for the element. This does not cache any data associated with
+  /// any other elements, including children of the [element].
+  String? cacheTopLevelElement(
+      DartdocDirectiveInfo dartdocDirectiveInfo, Element element) {
+    var key = element.name;
+    if (key == null) {
+      return null;
+    }
+    cacheElement(dartdocDirectiveInfo, key, element);
+    return key;
+  }
+
+  /// Cache the data associated with the [member] element given that the key
+  /// associated with the member's parent is [parentKey].
+  void cacheMember(DartdocDirectiveInfo dartdocDirectiveInfo, String parentKey,
+      Element member) {
+    var name = member.name;
+    if (name == null) {
+      return null;
+    }
+    cacheElement(dartdocDirectiveInfo, '$parentKey.$name', member);
+  }
+
+  /// Cache the data associated with the [element], using the given [key].
+  DocumentationWithSummary? cacheElement(
+      DartdocDirectiveInfo dartdocDirectiveInfo, String key, Element element) {
+    var documentation = DartUnitHoverComputer.computeDocumentation(
+        dartdocDirectiveInfo, element,
+        includeSummary: true);
+    if (documentation is DocumentationWithSummary) {
+      return this[key] = documentation;
+    }
+    return this[key] = DocumentationCache._emptyDocs;
+  }
+}
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart b/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
index 179e807..467c954 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
@@ -10,6 +10,7 @@
 import 'package:analysis_server/src/protocol_server.dart'
     hide Element, ElementKind;
 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/feature_computer.dart';
 import 'package:analysis_server/src/services/completion/dart/utilities.dart';
 import 'package:analysis_server/src/utilities/extensions/ast.dart';
@@ -1110,6 +1111,16 @@
   /// If the [element] has a documentation comment, fill the [suggestion]'s
   /// documentation fields.
   void _setDocumentation(CompletionSuggestion suggestion, Element element) {
+    final request = this.request;
+    if (request is DartCompletionRequestImpl) {
+      var documentationCache = request.documentationCache;
+      var data = documentationCache?.dataFor(element);
+      if (data != null) {
+        suggestion.docComplete = data.full;
+        suggestion.docSummary = data.summary;
+        return;
+      }
+    }
     var doc = DartUnitHoverComputer.computeDocumentation(
         request.dartdocDirectiveInfo, element,
         includeSummary: true);
diff --git a/pkg/analysis_server/tool/code_completion/completion_metrics.dart b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
index 434a3c1..069482a 100644
--- a/pkg/analysis_server/tool/code_completion/completion_metrics.dart
+++ b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
@@ -13,6 +13,7 @@
 import 'package:analysis_server/src/services/completion/completion_core.dart';
 import 'package:analysis_server/src/services/completion/completion_performance.dart';
 import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
+import 'package:analysis_server/src/services/completion/dart/documentation_cache.dart';
 import 'package:analysis_server/src/services/completion/dart/feature_computer.dart';
 import 'package:analysis_server/src/services/completion/dart/probability_range.dart';
 import 'package:analysis_server/src/services/completion/dart/relevance_tables.g.dart';
@@ -1034,6 +1035,8 @@
       MetricsSuggestionListener listener,
       OperationPerformanceImpl performance,
       CompletionRequestImpl request,
+      DartdocDirectiveInfo dartdocDirectiveInfo,
+      DocumentationCache? documentationCache,
       [DeclarationsTracker? declarationsTracker,
       protocol.CompletionAvailableSuggestionsParams?
           availableSuggestionsParams]) async {
@@ -1042,9 +1045,10 @@
     if (declarationsTracker == null) {
       // available suggestions == false
       suggestions = await DartCompletionManager(
-        dartdocDirectiveInfo: DartdocDirectiveInfo(),
+        dartdocDirectiveInfo: dartdocDirectiveInfo,
         listener: listener,
-      ).computeSuggestions(performance, request);
+      ).computeSuggestions(performance, request,
+          documentationCache: documentationCache);
     } else {
       // available suggestions == true
       var includedElementKinds = <protocol.ElementKind>{};
@@ -1053,12 +1057,13 @@
           <protocol.IncludedSuggestionRelevanceTag>[];
       var includedSuggestionSetList = <protocol.IncludedSuggestionSet>[];
       suggestions = await DartCompletionManager(
-        dartdocDirectiveInfo: DartdocDirectiveInfo(),
+        dartdocDirectiveInfo: dartdocDirectiveInfo,
         includedElementKinds: includedElementKinds,
         includedElementNames: includedElementNames,
         includedSuggestionRelevanceTags: includedSuggestionRelevanceTagList,
         listener: listener,
-      ).computeSuggestions(performance, request);
+      ).computeSuggestions(performance, request,
+          documentationCache: documentationCache);
 
       computeIncludedSetList(declarationsTracker, request.result,
           includedSuggestionSetList, includedElementNames);
@@ -1141,118 +1146,26 @@
 
     // Loop through each file, resolve the file and call
     // forEachExpectedCompletion
+
+    var dartdocDirectiveInfo = DartdocDirectiveInfo();
+    var documentationCache = DocumentationCache(dartdocDirectiveInfo);
+    var results = <ResolvedUnitResult>[];
     var pathContext = context.contextRoot.resourceProvider.pathContext;
     for (var filePath in context.contextRoot.analyzedFiles()) {
       if (file_paths.isDart(pathContext, filePath)) {
         try {
-          _resolvedUnitResult = await context.currentSession
-              .getResolvedUnit2(filePath) as ResolvedUnitResult;
+          var result = await context.currentSession.getResolvedUnit2(filePath)
+              as ResolvedUnitResult;
 
-          var analysisError = getFirstErrorOrNull(_resolvedUnitResult);
+          var analysisError = getFirstErrorOrNull(result);
           if (analysisError != null) {
             print('File $filePath skipped due to errors such as:');
             print('  ${analysisError.toString()}');
             print('');
             continue;
-          }
-
-          // Use the ExpectedCompletionsVisitor to compute the set of expected
-          // completions for this CompilationUnit.
-          final visitor = ExpectedCompletionsVisitor(filePath);
-          _resolvedUnitResult.unit!.accept(visitor);
-
-          for (var expectedCompletion in visitor.expectedCompletions) {
-            var resolvedUnitResult = _resolvedUnitResult;
-
-            // If an overlay option is being used, compute the overlay file, and
-            // have the context reanalyze the file
-            if (options.overlay != CompletionMetricsOptions.OVERLAY_NONE) {
-              var overlayContents = _getOverlayContents(
-                  _resolvedUnitResult.content!, expectedCompletion);
-
-              _provider.setOverlay(filePath,
-                  content: overlayContents,
-                  modificationStamp: overlayModificationStamp++);
-              context.driver.changeFile(filePath);
-              resolvedUnitResult = await context.currentSession
-                  .getResolvedUnit2(filePath) as ResolvedUnitResult;
-            }
-
-            // As this point the completion suggestions are computed,
-            // and results are collected with varying settings for
-            // comparison:
-
-            Future<int> handleExpectedCompletion(
-                {required MetricsSuggestionListener listener,
-                required CompletionMetrics metrics}) async {
-              var stopwatch = Stopwatch()..start();
-              var request = CompletionRequestImpl(
-                resolvedUnitResult,
-                expectedCompletion.offset,
-                CompletionPerformance(),
-              );
-              var directiveInfo = DartdocDirectiveInfo();
-
-              late OpType opType;
-              late List<protocol.CompletionSuggestion> suggestions;
-              await request.performance.runRequestOperation(
-                (performance) async {
-                  var dartRequest = await DartCompletionRequestImpl.from(
-                      performance, request, directiveInfo);
-                  opType =
-                      OpType.forCompletion(dartRequest.target, request.offset);
-                  suggestions = await _computeCompletionSuggestions(
-                    listener,
-                    performance,
-                    request,
-                    metrics.availableSuggestions ? declarationsTracker : null,
-                    metrics.availableSuggestions
-                        ? availableSuggestionsParams
-                        : null,
-                  );
-                },
-              );
-              stopwatch.stop();
-
-              return forEachExpectedCompletion(
-                  request,
-                  listener,
-                  expectedCompletion,
-                  opType.completionLocation,
-                  suggestions,
-                  metrics,
-                  stopwatch.elapsedMilliseconds);
-            }
-
-            var bestRank = -1;
-            var bestName = '';
-            var defaultTag = getCurrentTag();
-            for (var metrics in targetMetrics) {
-              // Compute the completions.
-              metrics.enable();
-              metrics.userTag.makeCurrent();
-              // if (FeatureComputer.noDisabledFeatures) {
-              //   var line = expectedCompletion.lineNumber;
-              //   var column = expectedCompletion.columnNumber;
-              //   print('$filePath:$line:$column');
-              // }
-              var listener = MetricsSuggestionListener();
-              var rank = await handleExpectedCompletion(
-                  listener: listener, metrics: metrics);
-              if (bestRank < 0 || rank < bestRank) {
-                bestRank = rank;
-                bestName = metrics.name;
-              }
-              defaultTag.makeCurrent();
-              metrics.disable();
-            }
-            rankComparison.count(bestName);
-
-            // If an overlay option is being used, remove the overlay applied
-            // earlier
-            if (options.overlay != CompletionMetricsOptions.OVERLAY_NONE) {
-              _provider.removeOverlay(filePath);
-            }
+          } else {
+            results.add(result);
+            documentationCache.cacheFromResult(result);
           }
         } catch (exception, stackTrace) {
           print('Exception caught analyzing: $filePath');
@@ -1261,6 +1174,104 @@
         }
       }
     }
+    for (var result in results) {
+      _resolvedUnitResult = result;
+      var filePath = result.path!;
+      // Use the ExpectedCompletionsVisitor to compute the set of expected
+      // completions for this CompilationUnit.
+      final visitor = ExpectedCompletionsVisitor(filePath);
+      _resolvedUnitResult.unit!.accept(visitor);
+
+      for (var expectedCompletion in visitor.expectedCompletions) {
+        var resolvedUnitResult = _resolvedUnitResult;
+
+        // If an overlay option is being used, compute the overlay file, and
+        // have the context reanalyze the file
+        if (options.overlay != CompletionMetricsOptions.OVERLAY_NONE) {
+          var overlayContents = _getOverlayContents(
+              _resolvedUnitResult.content!, expectedCompletion);
+
+          _provider.setOverlay(filePath,
+              content: overlayContents,
+              modificationStamp: overlayModificationStamp++);
+          context.driver.changeFile(filePath);
+          resolvedUnitResult = await context.currentSession
+              .getResolvedUnit2(filePath) as ResolvedUnitResult;
+        }
+
+        // As this point the completion suggestions are computed,
+        // and results are collected with varying settings for
+        // comparison:
+
+        Future<int> handleExpectedCompletion(
+            {required MetricsSuggestionListener listener,
+            required CompletionMetrics metrics}) async {
+          var stopwatch = Stopwatch()..start();
+          var request = CompletionRequestImpl(
+            resolvedUnitResult,
+            expectedCompletion.offset,
+            CompletionPerformance(),
+          );
+          var directiveInfo = DartdocDirectiveInfo();
+
+          late OpType opType;
+          late List<protocol.CompletionSuggestion> suggestions;
+          await request.performance.runRequestOperation(
+            (performance) async {
+              var dartRequest = await DartCompletionRequestImpl.from(
+                  performance, request, directiveInfo);
+              opType = OpType.forCompletion(dartRequest.target, request.offset);
+              suggestions = await _computeCompletionSuggestions(
+                listener,
+                performance,
+                request,
+                dartdocDirectiveInfo,
+                documentationCache,
+                metrics.availableSuggestions ? declarationsTracker : null,
+                metrics.availableSuggestions
+                    ? availableSuggestionsParams
+                    : null,
+              );
+            },
+          );
+          stopwatch.stop();
+
+          return forEachExpectedCompletion(
+              request,
+              listener,
+              expectedCompletion,
+              opType.completionLocation,
+              suggestions,
+              metrics,
+              stopwatch.elapsedMilliseconds);
+        }
+
+        var bestRank = -1;
+        var bestName = '';
+        var defaultTag = getCurrentTag();
+        for (var metrics in targetMetrics) {
+          // Compute the completions.
+          metrics.enable();
+          metrics.userTag.makeCurrent();
+          var listener = MetricsSuggestionListener();
+          var rank = await handleExpectedCompletion(
+              listener: listener, metrics: metrics);
+          if (bestRank < 0 || rank < bestRank) {
+            bestRank = rank;
+            bestName = metrics.name;
+          }
+          defaultTag.makeCurrent();
+          metrics.disable();
+        }
+        rankComparison.count(bestName);
+
+        // If an overlay option is being used, remove the overlay applied
+        // earlier.
+        if (options.overlay != CompletionMetricsOptions.OVERLAY_NONE) {
+          _provider.removeOverlay(filePath);
+        }
+      }
+    }
   }
 
   List<protocol.CompletionSuggestion> _filterSuggestions(
diff --git a/pkg/analyzer/lib/src/dartdoc/dartdoc_directive_info.dart b/pkg/analyzer/lib/src/dartdoc/dartdoc_directive_info.dart
index 540d866..cfcb732 100644
--- a/pkg/analyzer/lib/src/dartdoc/dartdoc_directive_info.dart
+++ b/pkg/analyzer/lib/src/dartdoc/dartdoc_directive_info.dart
@@ -177,7 +177,7 @@
       }
       start = eolIndex + 1;
     }
-    if (lastNonEmpty < firstNonEmpty) {
+    if (firstNonEmpty < 0 || lastNonEmpty < firstNonEmpty) {
       // All of the lines are empty.
       return const <String>[];
     }
diff --git a/pkg/compiler/lib/src/js_backend/deferred_holder_expression.dart b/pkg/compiler/lib/src/js_backend/deferred_holder_expression.dart
index f7e5ff1..d51e50f 100644
--- a/pkg/compiler/lib/src/js_backend/deferred_holder_expression.dart
+++ b/pkg/compiler/lib/src/js_backend/deferred_holder_expression.dart
@@ -284,83 +284,6 @@
   }
 }
 
-enum DeferredHolderResourceExpressionKind {
-  constantHolderReference,
-}
-
-/// Similar to [DeferredHolderExpression], [DeferredHolderResourceExpression]
-/// is used by resources which want to insert a DeferredExpression into the
-/// ast. This class does not support serialization.
-class DeferredHolderResourceExpression extends js.DeferredExpression
-    implements js.AstContainer {
-  final DeferredHolderResourceExpressionKind kind;
-  final Entity entity;
-  js.Expression _value;
-
-  @override
-  final js.JavaScriptNodeSourceInformation sourceInformation;
-
-  DeferredHolderResourceExpression(this.kind, this.entity)
-      : sourceInformation = null;
-  DeferredHolderResourceExpression._(
-      this.kind, this.entity, this._value, this.sourceInformation);
-
-  factory DeferredHolderResourceExpression.constantHolderReference() {
-    return DeferredHolderResourceExpression(
-        DeferredHolderResourceExpressionKind.constantHolderReference, null);
-  }
-
-  set value(js.Expression value) {
-    assert(!isFinalized && value != null);
-    _value = value;
-  }
-
-  @override
-  js.Expression get value {
-    assert(isFinalized, '$this is unassigned');
-    return _value;
-  }
-
-  @override
-  bool get isFinalized => _value != null;
-
-  @override
-  DeferredHolderResourceExpression withSourceInformation(
-      js.JavaScriptNodeSourceInformation newSourceInformation) {
-    if (newSourceInformation == sourceInformation) return this;
-    if (newSourceInformation == null) return this;
-    return DeferredHolderResourceExpression._(
-        kind, entity, _value, newSourceInformation);
-  }
-
-  @override
-  int get precedenceLevel => _value?.precedenceLevel ?? js.PRIMARY;
-
-  @override
-  int get hashCode {
-    return Hashing.objectsHash(kind, entity);
-  }
-
-  @override
-  bool operator ==(Object other) {
-    if (identical(this, other)) return true;
-    return other is DeferredHolderExpression &&
-        kind == other.kind &&
-        entity == other.entity;
-  }
-
-  @override
-  String toString() {
-    StringBuffer sb = new StringBuffer();
-    sb.write('DeferredHolderResourceExpression(kind=$kind,entity=$entity,');
-    sb.write('value=$_value)');
-    return sb.toString();
-  }
-
-  @override
-  Iterable<js.Node> get containedNodes => isFinalized ? [_value] : const [];
-}
-
 abstract class DeferredHolderExpressionFinalizer {
   /// Collects DeferredHolderExpressions from the JavaScript
   /// AST [code];
@@ -380,7 +303,6 @@
   final List<DeferredHolderExpression> holderReferences = [];
   final List<DeferredHolderParameter> holderParameters = [];
   final List<DeferredHolderResource> holderResources = [];
-  final List<DeferredHolderResourceExpression> holderResourceExpressions = [];
   final Set<String> _uniqueHolders = {};
   final List<String> _holders = [];
   final Map<Entity, String> _entityMap = {};
@@ -627,17 +549,6 @@
     resource.statement = js.Block(statements);
   }
 
-  /// Creates a reference to the constant holder.
-  void allocateConstantHolderReference(
-      DeferredHolderResourceExpression resource) {
-    String constantHolder = _holders.firstWhere(
-        (holder) => holder == globalObjectForConstants(),
-        orElse: () => null);
-    resource.value = constantHolder == null
-        ? js.LiteralNull()
-        : js.VariableUse(constantHolder);
-  }
-
   /// Allocates all [DeferredHolderResource]s and
   /// [DeferredHolderResourceExpression]s.
   void allocateResources() {
@@ -660,15 +571,6 @@
           break;
       }
     }
-
-    // Finally, finalize any [DeferredHolderResourceExpression]s.
-    for (var resource in holderResourceExpressions) {
-      switch (resource.kind) {
-        case DeferredHolderResourceExpressionKind.constantHolderReference:
-          allocateConstantHolderReference(resource);
-          break;
-      }
-    }
   }
 
   @override
@@ -685,11 +587,6 @@
     holderResources.add(node);
   }
 
-  void _registerDeferredHolderResourceExpression(
-      DeferredHolderResourceExpression node) {
-    holderResourceExpressions.add(node);
-  }
-
   void _registerDeferredHolderParameter(DeferredHolderParameter node) {
     holderParameters.add(node);
   }
@@ -722,8 +619,6 @@
   void visitDeferredExpression(js.DeferredExpression node) {
     if (node is DeferredHolderExpression) {
       _finalizer._registerDeferredHolderExpression(node);
-    } else if (node is DeferredHolderResourceExpression) {
-      _finalizer._registerDeferredHolderResourceExpression(node);
     } else {
       visitNode(node);
     }
diff --git a/pkg/compiler/lib/src/js_emitter/model.dart b/pkg/compiler/lib/src/js_emitter/model.dart
index 8a6db71..f2c81cf 100644
--- a/pkg/compiler/lib/src/js_emitter/model.dart
+++ b/pkg/compiler/lib/src/js_emitter/model.dart
@@ -165,11 +165,7 @@
   }
 }
 
-abstract class FieldContainer {
-  List<Field> get staticFieldsForReflection;
-}
-
-class Library implements FieldContainer {
+class Library {
   /// The element should only be used during the transition to the new model.
   /// Uses indicate missing information in the model.
   final LibraryEntity element;
@@ -179,11 +175,8 @@
   final List<Class> classes;
   final List<ClassTypeData> classTypeData;
 
-  @override
-  final List<Field> staticFieldsForReflection;
-
-  Library(this.element, this.uri, this.statics, this.classes,
-      this.classTypeData, this.staticFieldsForReflection);
+  Library(
+      this.element, this.uri, this.statics, this.classes, this.classTypeData);
 
   @override
   String toString() {
@@ -233,7 +226,7 @@
           check.cls == commonElements.objectClass || check.cls == element);
 }
 
-class Class implements FieldContainer {
+class Class {
   /// The element should only be used during the transition to the new model.
   /// Uses indicate missing information in the model.
   final ClassEntity element;
@@ -256,8 +249,7 @@
 
   /// noSuchMethod stubs in the special case that the class is Object.
   final List<StubMethod> noSuchMethodStubs;
-  @override
-  final List<Field> staticFieldsForReflection;
+
   final bool hasRtiField; // Per-instance runtime type information pseudo-field.
   final bool onlyForRti;
   final bool onlyForConstructor;
@@ -289,7 +281,6 @@
       this.name,
       this.methods,
       this.fields,
-      this.staticFieldsForReflection,
       this.callStubs,
       this.noSuchMethodStubs,
       this.checkedSetters,
@@ -335,7 +326,6 @@
       ClassTypeData typeData,
       js.Name name,
       List<Field> instanceFields,
-      List<Field> staticFieldsForReflection,
       List<StubMethod> callStubs,
       List<StubMethod> checkedSetters,
       List<StubMethod> isChecks,
@@ -350,7 +340,6 @@
             name,
             const <Method>[],
             instanceFields,
-            staticFieldsForReflection,
             callStubs,
             const <StubMethod>[],
             checkedSetters,
diff --git a/pkg/compiler/lib/src/js_emitter/program_builder/field_visitor.dart b/pkg/compiler/lib/src/js_emitter/program_builder/field_visitor.dart
index 240cb55..129aa33 100644
--- a/pkg/compiler/lib/src/js_emitter/program_builder/field_visitor.dart
+++ b/pkg/compiler/lib/src/js_emitter/program_builder/field_visitor.dart
@@ -4,7 +4,7 @@
 
 part of dart2js.js_emitter.program_builder;
 
-/// [member] is a field (instance, static, or top level).
+/// [member] is an instance field.
 ///
 /// [name] is the field name that the [Namer] has picked for this field's
 /// storage, that is, the JavaScript property name.
@@ -35,36 +35,18 @@
 
   /// Invokes [f] for each of the fields of [element].
   ///
-  /// [element] must be a [ClassEntity] or a [LibraryEntity].
-  ///
-  /// If [element] is a [ClassEntity], the static fields of the class are
-  /// visited if [visitStatics] is true and the instance fields are visited if
-  /// [visitStatics] is false.
-  ///
-  /// If [element] is a [LibraryEntity], [visitStatics] must be true.
-  ///
   /// When visiting the instance fields of a class, the fields of its superclass
   /// are also visited if the class is instantiated.
-  ///
-  /// Invariant: [element] must be a declaration element.
-  void visitFields(AcceptField f,
-      {bool visitStatics: false, LibraryEntity library, ClassEntity cls}) {
-    bool isNativeClass = false;
-    bool isLibrary = false;
-    bool isInstantiated = false;
-    if (cls != null) {
-      isNativeClass = _nativeData.isNativeClass(cls);
+  void visitFields(AcceptField f, ClassEntity cls) {
+    assert(
+        cls != null, failedAt(NO_LOCATION_SPANNABLE, 'Expected a ClassEntity'));
 
-      // If the class is never instantiated we still need to set it up for
-      // inheritance purposes, but we can simplify its JavaScript constructor.
-      isInstantiated = _codegenWorld.directlyInstantiatedClasses.contains(cls);
-    } else if (library != null) {
-      isLibrary = true;
-      assert(visitStatics, failedAt(library));
-    } else {
-      failedAt(
-          NO_LOCATION_SPANNABLE, 'Expected a ClassEntity or a LibraryEntity.');
-    }
+    bool isNativeClass = _nativeData.isNativeClass(cls);
+
+    // If the class is never instantiated we still need to set it up for
+    // inheritance purposes, but we can simplify its JavaScript constructor.
+    bool isInstantiated =
+        _codegenWorld.directlyInstantiatedClasses.contains(cls);
 
     void visitField(FieldEntity field, {ClassEntity holder}) {
       bool isMixinNativeField =
@@ -76,7 +58,7 @@
       // setters.
       bool needsGetter = false;
       bool needsSetter = false;
-      if (isLibrary || isMixinNativeField || holder == cls) {
+      if (isMixinNativeField || holder == cls) {
         needsGetter = fieldNeedsGetter(field);
         needsSetter = fieldNeedsSetter(field);
       }
@@ -101,34 +83,22 @@
       }
     }
 
-    if (isLibrary) {
-      _elementEnvironment.forEachLibraryMember(library, (MemberEntity member) {
-        if (member.isField) visitField(member);
-      });
-    } else if (visitStatics) {
-      _elementEnvironment.forEachLocalClassMember(cls, (MemberEntity member) {
-        if (member.isField && member.isStatic) {
-          visitField(member, holder: cls);
-        }
-      });
-    } else {
-      // TODO(kasperl): We should make sure to only emit one version of
-      // overridden fields. Right now, we rely on the ordering so the
-      // fields pulled in from mixins are replaced with the fields from
-      // the class definition.
+    // TODO(kasperl): We should make sure to only emit one version of
+    // overridden fields. Right now, we rely on the ordering so the
+    // fields pulled in from mixins are replaced with the fields from
+    // the class definition.
 
-      // If a class is not instantiated then we add the field just so we can
-      // generate the field getter/setter dynamically. Since this is only
-      // allowed on fields that are in [element] we don't need to visit
-      // superclasses for non-instantiated classes.
-      _elementEnvironment.forEachClassMember(cls,
-          (ClassEntity holder, MemberEntity member) {
-        if (cls != holder && !isInstantiated) return;
-        if (member.isField && !member.isStatic) {
-          visitField(member, holder: holder);
-        }
-      });
-    }
+    // If a class is not instantiated then we add the field just so we can
+    // generate the field getter/setter dynamically. Since this is only
+    // allowed on fields that are in [element] we don't need to visit
+    // superclasses for non-instantiated classes.
+    _elementEnvironment.forEachClassMember(cls,
+        (ClassEntity holder, MemberEntity member) {
+      if (cls != holder && !isInstantiated) return;
+      if (member.isField && !member.isStatic) {
+        visitField(member, holder: holder);
+      }
+    });
   }
 
   bool fieldNeedsGetter(FieldEntity field) {
diff --git a/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart b/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
index 26556d7..0660137 100644
--- a/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
+++ b/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
@@ -543,12 +543,7 @@
         .toList();
     classTypeData.addAll(classes.map((Class cls) => cls.typeData).toList());
 
-    bool visitStatics = true;
-    List<Field> staticFieldsForReflection =
-        _buildFields(library: library, visitStatics: visitStatics);
-
-    return new Library(library, uri, statics, classes, classTypeData,
-        staticFieldsForReflection);
+    return Library(library, uri, statics, classes, classTypeData);
   }
 
   Class _buildClass(ClassEntity cls) {
@@ -655,16 +650,7 @@
     bool isInterceptedClass = _interceptorData.isInterceptedClass(cls);
     List<Field> instanceFields = onlyForConstructorOrRti
         ? const []
-        : _buildFields(
-            cls: cls,
-            visitStatics: false,
-            isHolderInterceptedClass: isInterceptedClass);
-    List<Field> staticFieldsForReflection = onlyForConstructorOrRti
-        ? const []
-        : _buildFields(
-            cls: cls,
-            visitStatics: true,
-            isHolderInterceptedClass: isInterceptedClass);
+        : _buildFields(cls: cls, isHolderInterceptedClass: isInterceptedClass);
 
     TypeTestProperties typeTests = runtimeTypeGenerator.generateIsTests(
         cls, _generatedCode,
@@ -717,28 +703,19 @@
       assert(methods.isEmpty);
       assert(!isClosureBaseClass);
 
-      result = new MixinApplication(
-          cls,
-          typeData,
-          name,
-          instanceFields,
-          staticFieldsForReflection,
-          callStubs,
-          checkedSetters,
-          isChecks,
-          typeTests.functionTypeIndex,
+      result = MixinApplication(cls, typeData, name, instanceFields, callStubs,
+          checkedSetters, isChecks, typeTests.functionTypeIndex,
           isDirectlyInstantiated: isInstantiated,
           hasRtiField: hasRtiField,
           onlyForRti: onlyForRti,
           onlyForConstructor: onlyForConstructor);
     } else {
-      result = new Class(
+      result = Class(
           cls,
           typeData,
           name,
           methods,
           instanceFields,
-          staticFieldsForReflection,
           callStubs,
           noSuchMethodStubs,
           checkedSetters,
@@ -1009,18 +986,14 @@
   }
 
   List<Field> _buildFields(
-      {bool visitStatics: false,
-      bool isHolderInterceptedClass: false,
-      LibraryEntity library,
-      ClassEntity cls}) {
+      {bool isHolderInterceptedClass: false, ClassEntity cls}) {
     List<Field> fields = <Field>[];
 
     void visitField(FieldEntity field, js.Name name, js.Name accessorName,
         bool needsGetter, bool needsSetter, bool needsCheckedSetter) {
       int getterFlags = 0;
       if (needsGetter) {
-        if (visitStatics ||
-            !_interceptorData.fieldHasInterceptedGetter(field)) {
+        if (!_interceptorData.fieldHasInterceptedGetter(field)) {
           getterFlags = 1;
         } else {
           getterFlags += 2;
@@ -1035,8 +1008,7 @@
 
       int setterFlags = 0;
       if (needsSetter) {
-        if (visitStatics ||
-            !_interceptorData.fieldHasInterceptedSetter(field)) {
+        if (!_interceptorData.fieldHasInterceptedSetter(field)) {
           setterFlags = 1;
         } else {
           setterFlags += 2;
@@ -1056,7 +1028,7 @@
         constantValue = fieldData.constantValue;
       }
 
-      fields.add(new Field(
+      fields.add(Field(
           field,
           name,
           accessorName,
@@ -1068,10 +1040,9 @@
           fieldData.isElided));
     }
 
-    FieldVisitor visitor = new FieldVisitor(
+    FieldVisitor visitor = FieldVisitor(
         _elementEnvironment, _codegenWorld, _nativeData, _namer, _closedWorld);
-    visitor.visitFields(visitField,
-        visitStatics: visitStatics, library: library, cls: cls);
+    visitor.visitFields(visitField, cls);
 
     return fields;
   }
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
index ef83ff6..9c4864f 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
@@ -420,19 +420,6 @@
   hunk(hunkHelpers, #embeddedGlobalsObject, holders, #staticState);
 }
 
-// Returns the global with the given [name].
-function getGlobalFromName(name) {
-  // TODO(floitsch): we are running through all holders. Since negative
-  // lookups are expensive we might need to improve this.
-  // Relies on the fact that all names are unique across all holders.
-  for (var i = 0; i < holders.length; i++) {
-    // The constant holder reuses the same names. Therefore we must skip it.
-    if (holders[i] == #constantHolderReference) continue;
-    // Relies on the fact that all variables are unique.
-    if (holders[i][name]) return holders[i][name];
-  }
-}
-
 if (#isTrackingAllocations) {
   var allocations = #deferredGlobal['allocations'] = {};
 }
@@ -738,8 +725,6 @@
       'embeddedGlobalsObject': js.js("init"),
       'staticStateDeclaration': DeferredHolderParameter(),
       'staticState': DeferredHolderParameter(),
-      'constantHolderReference':
-          DeferredHolderResourceExpression.constantHolderReference(),
       'holders': holderDeclaration,
       'callName': js.string(_namer.fixedNames.callNameField),
       'stubName': js.string(_namer.stubNameField),
@@ -1039,19 +1024,26 @@
     Iterable<Method> noSuchMethodStubs = cls.noSuchMethodStubs;
     Iterable<Method> gettersSetters = generateGettersSetters(cls);
     Iterable<Method> allMethods = [
-      methods,
-      checkedSetters,
-      isChecks,
-      callStubs,
-      noSuchMethodStubs,
-      gettersSetters
-    ].expand((x) => x);
+      ...methods,
+      ...checkedSetters,
+      ...isChecks,
+      ...callStubs,
+      ...noSuchMethodStubs,
+      ...gettersSetters
+    ];
 
     List<js.Property> properties = [];
 
     if (cls.superclass == null) {
-      // ie11 might require us to set 'constructor' but we aren't 100% sure.
+      // This is Dart `Object`. Add properties that are usually added by
+      // `inherit`.
+
+      // TODO(sra): Adding properties here appears to be redundant with the call
+      // to `inherit(P.Object, null)` in the generated code. See if we can
+      // remove that.
+
       if (_options.legacyJavaScript) {
+        // IE11 might require us to set 'constructor' but we aren't 100% sure.
         properties
             .add(js.Property(js.string("constructor"), classReference(cls)));
       }
@@ -1147,15 +1139,12 @@
 
   /// Generates all getters and setters the given class [cls] needs.
   Iterable<Method> generateGettersSetters(Class cls) {
-    Iterable<Method> getters = cls.fields
-        .where((Field field) => field.needsGetter)
-        .map(generateGetter);
-
-    Iterable<Method> setters = cls.fields
-        .where((Field field) => field.needsUncheckedSetter)
-        .map(generateSetter);
-
-    return [getters, setters].expand((x) => x);
+    return [
+      for (Field field in cls.fields)
+        if (field.needsGetter) generateGetter(field),
+      for (Field field in cls.fields)
+        if (field.needsUncheckedSetter) generateSetter(field),
+    ];
   }
 
   /// Emits the given instance [method].
@@ -1816,15 +1805,6 @@
         js.string(MANGLED_GLOBAL_NAMES), new js.ObjectInitializer(names));
   }
 
-  /// Emits the [GET_TYPE_FROM_NAME] embedded global.
-  ///
-  /// This embedded global provides a way to go from a class name (which is
-  /// also the constructor's name) to the constructor itself.
-  js.Property emitGetTypeFromName() {
-    js.Expression function = js.js("getGlobalFromName");
-    return new js.Property(js.string(GET_TYPE_FROM_NAME), function);
-  }
-
   /// Emits the [METADATA] embedded global.
   ///
   /// The metadata itself has already been computed earlier and is stored in
@@ -1876,8 +1856,6 @@
     globals.add(js.Property(
         js.string(MANGLED_NAMES), js.ObjectInitializer(<js.Property>[])));
 
-    globals.add(emitGetTypeFromName());
-
     globals.addAll(emitMetadata(program));
 
     if (program.needsNativeSupport) {
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
index 52434c7..21fe02f 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
@@ -13,7 +13,6 @@
         DEFERRED_LIBRARY_PARTS,
         DEFERRED_PART_URIS,
         DEFERRED_PART_HASHES,
-        GET_TYPE_FROM_NAME,
         INITIALIZE_LOADED_HUNK,
         INTERCEPTORS_BY_TAG,
         IS_HUNK_INITIALIZED,
@@ -66,7 +65,6 @@
         DeferredHolderExpressionFinalizerImpl,
         DeferredHolderParameter,
         DeferredHolderResource,
-        DeferredHolderResourceExpression,
         DeferredHolderResourceKind;
 import '../../js_backend/type_reference.dart'
     show
diff --git a/pkg/compiler/test/helpers/program_lookup.dart b/pkg/compiler/test/helpers/program_lookup.dart
index 0ded799..1c98822 100644
--- a/pkg/compiler/test/helpers/program_lookup.dart
+++ b/pkg/compiler/test/helpers/program_lookup.dart
@@ -134,23 +134,6 @@
       }
     }
 
-    void addField(Field field) {
-      ClassEntity enclosingClass = field.element?.enclosingClass;
-      if (enclosingClass != null) {
-        ClassData data =
-            _classMap.putIfAbsent(enclosingClass, () => new ClassData(null));
-        assert(!data._fieldMap.containsKey(field.element));
-        data._fieldMap[field.element] = field;
-      } else if (field.element != null) {
-        assert(!_fieldMap.containsKey(field.element));
-        _fieldMap[field.element] = field;
-      }
-    }
-
-    for (Field field in library.staticFieldsForReflection) {
-      addField(field);
-    }
-
     void addStaticField(StaticField field) {
       ClassEntity enclosingClass = field.element?.enclosingClass;
       if (enclosingClass != null) {
diff --git a/pkg/compiler/test/inference/data/foreign.dart b/pkg/compiler/test/inference/data/foreign.dart
index 7ae6e76..a7afea0 100644
--- a/pkg/compiler/test/inference/data/foreign.dart
+++ b/pkg/compiler/test/inference/data/foreign.dart
@@ -7,9 +7,6 @@
 /// ignore: IMPORT_INTERNAL_LIBRARY
 import 'dart:_foreign_helper';
 
-/// ignore: IMPORT_INTERNAL_LIBRARY
-import 'dart:_js_embedded_names';
-
 /// ignore: IMPORT_INTERNAL_LIBRARY, UNUSED_IMPORT
 import 'dart:_interceptors';
 
@@ -20,8 +17,6 @@
   jsCallVoid();
   jsCallUnion();
 
-  jsEmbeddedGlobal_getTypeFromName();
-
   jsStringConcat();
 }
 
@@ -37,10 +32,5 @@
 /*member: jsCallUnion:Union([exact=JSString], [subclass=JSInt])*/
 jsCallUnion() => JS('int|String', '#', 0);
 
-/*member: jsEmbeddedGlobal_getTypeFromName:[null|subclass=Object]*/
-jsEmbeddedGlobal_getTypeFromName() {
-  return JS_EMBEDDED_GLOBAL('', GET_TYPE_FROM_NAME);
-}
-
 /*member: jsStringConcat:[exact=JSString]*/
 jsStringConcat() => JS_STRING_CONCAT('a', 'b');
diff --git a/pkg/compiler/test/inference/side_effects/foreign.dart b/pkg/compiler/test/inference/side_effects/foreign.dart
index 68ff097..5152286 100644
--- a/pkg/compiler/test/inference/side_effects/foreign.dart
+++ b/pkg/compiler/test/inference/side_effects/foreign.dart
@@ -7,9 +7,6 @@
 /// ignore: IMPORT_INTERNAL_LIBRARY
 import 'dart:_foreign_helper';
 
-/// ignore: IMPORT_INTERNAL_LIBRARY
-import 'dart:_js_embedded_names';
-
 /// ignore: IMPORT_INTERNAL_LIBRARY, UNUSED_IMPORT
 import 'dart:_interceptors';
 
@@ -26,11 +23,6 @@
 jsCallEffectsNoInstanceDependsNoStatic() =>
     JS('effects:no-instance;depends:no-static', '#', 0);
 
-/*member: jsEmbeddedGlobal_getTypeFromName:SideEffects(reads nothing; writes nothing)*/
-jsEmbeddedGlobal_getTypeFromName() {
-  return JS_EMBEDDED_GLOBAL('', GET_TYPE_FROM_NAME);
-}
-
 /*member: jsStringConcat:SideEffects(reads nothing; writes nothing)*/
 jsStringConcat() => JS_STRING_CONCAT('a', 'b');
 
@@ -41,7 +33,5 @@
   jsCallEffectsAllDependsNoIndex();
   jsCallEffectsNoInstanceDependsNoStatic();
 
-  jsEmbeddedGlobal_getTypeFromName();
-
   jsStringConcat();
 }
diff --git a/pkg/js_runtime/lib/shared/embedded_names.dart b/pkg/js_runtime/lib/shared/embedded_names.dart
index 1df7671..9538239 100644
--- a/pkg/js_runtime/lib/shared/embedded_names.dart
+++ b/pkg/js_runtime/lib/shared/embedded_names.dart
@@ -40,12 +40,6 @@
 /// Use [JsBuiltin.getType] instead of directly accessing this embedded global.
 const TYPES = 'types';
 
-/// Returns a function that maps a name of a class to its type.
-///
-/// This embedded global is used by the runtime when computing the internal
-/// runtime-type-information (rti) object.
-const GET_TYPE_FROM_NAME = 'getTypeFromName';
-
 /// A JS map from mangled global names to their unmangled names.
 ///
 /// If the program does not use reflection, this embedded global may be empty
diff --git a/sdk/lib/_internal/js_runtime/lib/js_helper.dart b/sdk/lib/_internal/js_runtime/lib/js_helper.dart
index 890fd9a..7fe9802 100644
--- a/sdk/lib/_internal/js_runtime/lib/js_helper.dart
+++ b/sdk/lib/_internal/js_runtime/lib/js_helper.dart
@@ -11,7 +11,6 @@
         DEFERRED_LIBRARY_PARTS,
         DEFERRED_PART_URIS,
         DEFERRED_PART_HASHES,
-        GET_TYPE_FROM_NAME,
         GET_ISOLATE_TAG,
         INITIALIZE_LOADED_HUNK,
         INTERCEPTED_NAMES,
diff --git a/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart b/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart
index 1df7671..9538239 100644
--- a/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart
+++ b/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart
@@ -40,12 +40,6 @@
 /// Use [JsBuiltin.getType] instead of directly accessing this embedded global.
 const TYPES = 'types';
 
-/// Returns a function that maps a name of a class to its type.
-///
-/// This embedded global is used by the runtime when computing the internal
-/// runtime-type-information (rti) object.
-const GET_TYPE_FROM_NAME = 'getTypeFromName';
-
 /// A JS map from mangled global names to their unmangled names.
 ///
 /// If the program does not use reflection, this embedded global may be empty
diff --git a/sdk/lib/developer/profiler.dart b/sdk/lib/developer/profiler.dart
index 07fb625..db0ae1d 100644
--- a/sdk/lib/developer/profiler.dart
+++ b/sdk/lib/developer/profiler.dart
@@ -4,7 +4,8 @@
 
 part of dart.developer;
 
-/// A UserTag can be used to group samples in the Observatory profiler.
+/// A UserTag can be used to group samples in the
+/// [DevTools CPU profiler](https://flutter.dev/docs/development/tools/devtools/cpu-profiler).
 abstract class UserTag {
   /// The maximum number of UserTag instances that can be created by a program.
   static const MAX_USER_TAGS = 64;
diff --git a/tools/VERSION b/tools/VERSION
index 4eae23a..64a9a48 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 114
+PRERELEASE 115
 PRERELEASE_PATCH 0
\ No newline at end of file