Version 2.15.0-251.0.dev

Merge commit '9f00d0c94e1c2b728a63b4e43055b91efc69ff5e' into 'dev'
diff --git a/README.dart-sdk b/README.dart-sdk
index ff4bb61..99dbb49 100644
--- a/README.dart-sdk
+++ b/README.dart-sdk
@@ -5,16 +5,26 @@
 Here's a brief guide to what's in here:
 
 bin/               Binaries/scripts to compile, run, and manage Dart apps.
-  dart             Dart virtual machine
+  dart             Command line Dart tool
   dartaotruntime   Minimal Dart runtime for running AOT modules
-  dart2js          Dart-to-JavaScript compiler
+  dart2js          Dart to JavaScript production compiler
+  dartdevc         Dart to Javascript development compiler
   dartanalyzer     Dart static analyzer
   dartdoc          Dart documentation generator
 
+include/           header files that define the Dart embedding API for use by
+                   - C/C++ applications that embed the Dart Virtual machine
+                   - native libraries loaded into a dart application using FFI
+                     (https://dart.dev/guides/libraries/c-interop)
+
 lib/               Libraries that are shipped with the Dart runtime. More
                    information is available at https://api.dart.dev.
 
-version            The version number of the SDK (for example, 2.12.1).
+LICENSE            Description of Dart SDK license
+
+README             This file
 
 revision           The git commit ID of the SDK build
                    (for example, 020b3efd3f0023c5db2097787f7cf778db837a8f).
+
+version            The version number of the SDK (for example, 2.12.1).
diff --git a/pkg/analysis_server/lib/src/cider/completion.dart b/pkg/analysis_server/lib/src/cider/completion.dart
index 3a20010..214d3f2 100644
--- a/pkg/analysis_server/lib/src/cider/completion.dart
+++ b/pkg/analysis_server/lib/src/cider/completion.dart
@@ -6,9 +6,9 @@
 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/fuzzy_filter_sort.dart';
 import 'package:analysis_server/src/services/completion/dart/local_library_contributor.dart';
 import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
-import 'package:analysis_server/src/services/completion/filtering/fuzzy_matcher.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/element/element.dart' show LibraryElement;
 import 'package:analyzer/src/dart/analysis/performance_logger.dart';
@@ -78,7 +78,7 @@
         CompletionPerformance(),
       );
 
-      _dartCompletionRequest = await DartCompletionRequestImpl.from(
+      _dartCompletionRequest = DartCompletionRequestImpl.from(
         completionRequest,
       );
 
@@ -123,15 +123,15 @@
         }
       });
 
-      var filter = _FilterSort(
-        _dartCompletionRequest,
-        suggestions,
-      );
+      var filterPattern = _dartCompletionRequest.targetPrefix;
 
       performance.run('filter', (performance) {
         _logger.run('Filter suggestions', () {
           performance.getDataInt('count').add(suggestions.length);
-          suggestions = filter.perform();
+          suggestions = fuzzyFilterSort(
+            pattern: filterPattern,
+            suggestions: suggestions,
+          );
           performance.getDataInt('matchCount').add(suggestions.length);
         });
       });
@@ -141,7 +141,7 @@
         performance: CiderCompletionPerformance._(
           operations: _performanceRoot.children.first,
         ),
-        prefixStart: CiderPosition(line, column - filter._pattern.length),
+        prefixStart: CiderPosition(line, column - filterPattern.length),
       );
 
       return result;
@@ -260,63 +260,3 @@
 
   _CiderImportedLibrarySuggestions(this.signature, this.suggestions);
 }
-
-class _FilterSort {
-  final DartCompletionRequestImpl _request;
-  final List<CompletionSuggestion> _suggestions;
-
-  late FuzzyMatcher _matcher;
-  late String _pattern;
-
-  _FilterSort(this._request, this._suggestions);
-
-  List<CompletionSuggestion> perform() {
-    _pattern = _request.targetPrefix;
-    _matcher = FuzzyMatcher(_pattern, matchStyle: MatchStyle.SYMBOL);
-
-    var scored = _suggestions
-        .map((e) => _FuzzyScoredSuggestion(e, _score(e)))
-        .where((e) => e.score > 0)
-        .toList();
-
-    scored.sort((a, b) {
-      // Prefer what the user requested by typing.
-      if (a.score > b.score) {
-        return -1;
-      } else if (a.score < b.score) {
-        return 1;
-      }
-
-      // Then prefer what is more relevant in the context.
-      if (a.suggestion.relevance != b.suggestion.relevance) {
-        return -(a.suggestion.relevance - b.suggestion.relevance);
-      }
-
-      // Other things being equal, sort by name.
-      return a.suggestion.completion.compareTo(b.suggestion.completion);
-    });
-
-    return scored.map((e) => e.suggestion).toList();
-  }
-
-  double _score(CompletionSuggestion e) {
-    var suggestionTextToMatch = e.completion;
-
-    if (e.kind == CompletionSuggestionKind.NAMED_ARGUMENT) {
-      var index = suggestionTextToMatch.indexOf(':');
-      if (index != -1) {
-        suggestionTextToMatch = suggestionTextToMatch.substring(0, index);
-      }
-    }
-
-    return _matcher.score(suggestionTextToMatch);
-  }
-}
-
-/// [CompletionSuggestion] scored using [FuzzyMatcher].
-class _FuzzyScoredSuggestion {
-  final CompletionSuggestion suggestion;
-  final double score;
-
-  _FuzzyScoredSuggestion(this.suggestion, this.score);
-}
diff --git a/pkg/analysis_server/lib/src/computer/computer_highlights.dart b/pkg/analysis_server/lib/src/computer/computer_highlights.dart
index eac6314..cd4ec1f 100644
--- a/pkg/analysis_server/lib/src/computer/computer_highlights.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_highlights.dart
@@ -179,6 +179,12 @@
     } else {
       type = HighlightRegionType.CLASS;
     }
+
+    if (_isAnnotationIdentifier(node)) {
+      semanticModifiers ??= {};
+      semanticModifiers.add(CustomSemanticTokenModifiers.annotation);
+    }
+
     // add region
     return _addRegion_node(
       node,
@@ -199,7 +205,11 @@
       // For semantic tokens, constructor names are coloured like methods but
       // have a modifier applied.
       semanticTokenType: SemanticTokenTypes.method,
-      semanticTokenModifiers: {CustomSemanticTokenModifiers.constructor},
+      semanticTokenModifiers: {
+        CustomSemanticTokenModifiers.constructor,
+        if (_isAnnotationIdentifier(node))
+          CustomSemanticTokenModifiers.annotation,
+      },
     );
   }
 
@@ -293,7 +303,13 @@
     }
     // add region
     if (type != null) {
-      return _addRegion_node(node, type);
+      return _addRegion_node(
+        node,
+        type,
+        semanticTokenModifiers: _isAnnotationIdentifier(node)
+            ? {CustomSemanticTokenModifiers.annotation}
+            : null,
+      );
     }
     return false;
   }
@@ -575,6 +591,18 @@
     _addRegion(offset, end - offset, type);
   }
 
+  /// Checks whether [node] is the identifier part of an annotation.
+  bool _isAnnotationIdentifier(AstNode node) {
+    if (node.parent is Annotation) {
+      return true;
+    } else if (node.parent is PrefixedIdentifier &&
+        node.parent?.parent is Annotation) {
+      return true;
+    } else {
+      return false;
+    }
+  }
+
   void _reset() {
     _computeRegions = false;
     _computeSemanticTokens = false;
diff --git a/pkg/analysis_server/lib/src/domain_completion.dart b/pkg/analysis_server/lib/src/domain_completion.dart
index bef8ea5..69d0dec 100644
--- a/pkg/analysis_server/lib/src/domain_completion.dart
+++ b/pkg/analysis_server/lib/src/domain_completion.dart
@@ -102,7 +102,7 @@
 
       var contributorTag = 'computeSuggestions - ${manager.runtimeType}';
       await perf.runAsync(contributorTag, (performance) async {
-        var dartRequest = await DartCompletionRequestImpl.from(
+        var dartRequest = DartCompletionRequestImpl.from(
           request,
           dartdocDirectiveInfo: server.getDartdocDirectiveInfoFor(
             request.result,
diff --git a/pkg/analysis_server/lib/src/domains/execution/completion.dart b/pkg/analysis_server/lib/src/domains/execution/completion.dart
index 6988248..346dc00 100644
--- a/pkg/analysis_server/lib/src/domains/execution/completion.dart
+++ b/pkg/analysis_server/lib/src/domains/execution/completion.dart
@@ -76,7 +76,7 @@
       CompletionPerformance(),
     );
 
-    var dartRequest = await DartCompletionRequestImpl.from(request);
+    var dartRequest = DartCompletionRequestImpl.from(request);
 
     var suggestions = await request.performance.runRequestOperation(
       (performance) async {
diff --git a/pkg/analysis_server/lib/src/lsp/constants.dart b/pkg/analysis_server/lib/src/lsp/constants.dart
index 8a8d52a..d8cc97f 100644
--- a/pkg/analysis_server/lib/src/lsp/constants.dart
+++ b/pkg/analysis_server/lib/src/lsp/constants.dart
@@ -92,12 +92,19 @@
 }
 
 abstract class CustomSemanticTokenModifiers {
+  /// A modifier applied to the identifier following the `@` annotation token to
+  /// allow users to color it differently (for example in the same way as `@`).
+  static const annotation = SemanticTokenModifiers('annotation');
+
   /// A modifier applied to control keywords like if/for/etc. so they can be
   /// colored differently to other keywords (void, import, etc), matching the
   /// original Dart textmate grammar.
   /// https://github.com/dart-lang/dart-syntax-highlight/blob/84a8e84f79bc917ebd959a4587349c865dc945e0/grammars/dart.json#L244-L261
   static const control = SemanticTokenModifiers('control');
 
+  /// A modifier applied to the identifier for an import prefix.
+  static const importPrefix = SemanticTokenModifiers('importPrefix');
+
   /// A modifier applied to parameter references to indicate they are the name/label
   /// to allow theming them differently to the values. For example in the code
   /// `foo({String a}) => foo(a: a)` the a's will be differentiated as:
@@ -127,8 +134,8 @@
   /// of the expression would show through the simple-colorings "string" colors.
   static const interpolation = SemanticTokenModifiers('interpolation');
 
-  /// A modifier applied to the void keyword to users to color it differently
-  /// (for example as a type).
+  /// A modifier applied to the void keyword to allow users to color it
+  /// differently (for example as a type).
   static const void_ = SemanticTokenModifiers('void');
 
   /// All custom semantic token modifiers, used to populate the LSP Legend.
@@ -137,7 +144,9 @@
   /// HighlightRegion mappings will be automatically included, but should still
   /// be listed here in case they are removed from mappings in the future.
   static const values = [
+    annotation,
     control,
+    importPrefix,
     label,
     constructor,
     escape,
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 c93b316..80aa6a3 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
@@ -213,7 +213,7 @@
           CompletionRequestImpl(unit, offset, performance);
       final directiveInfo =
           server.getDartdocDirectiveInfoFor(completionRequest.result);
-      final dartCompletionRequest = await DartCompletionRequestImpl.from(
+      final dartCompletionRequest = DartCompletionRequestImpl.from(
         completionRequest,
         dartdocDirectiveInfo: directiveInfo,
         completionPreference: CompletionPreference.replace,
diff --git a/pkg/analysis_server/lib/src/lsp/semantic_tokens/mapping.dart b/pkg/analysis_server/lib/src/lsp/semantic_tokens/mapping.dart
index 1758d41..18b0a43 100644
--- a/pkg/analysis_server/lib/src/lsp/semantic_tokens/mapping.dart
+++ b/pkg/analysis_server/lib/src/lsp/semantic_tokens/mapping.dart
@@ -21,6 +21,9 @@
   HighlightRegionType.DYNAMIC_PARAMETER_DECLARATION: {
     SemanticTokenModifiers.declaration
   },
+  HighlightRegionType.IMPORT_PREFIX: {
+    CustomSemanticTokenModifiers.importPrefix,
+  },
   HighlightRegionType.INSTANCE_FIELD_DECLARATION: {
     SemanticTokenModifiers.declaration
   },
@@ -81,6 +84,9 @@
 };
 
 /// A mapping from [HighlightRegionType] to [SemanticTokenTypes].
+///
+/// A description of the intended uses for each token type can be found here:
+/// https://code.visualstudio.com/api/language-extensions/semantic-highlight-guide#semantic-token-classification
 final highlightRegionTokenTypes = {
   HighlightRegionType.ANNOTATION: CustomSemanticTokenTypes.annotation,
   HighlightRegionType.BUILT_IN: SemanticTokenTypes.keyword,
@@ -100,8 +106,9 @@
   HighlightRegionType.ENUM_CONSTANT: SemanticTokenTypes.enumMember,
   HighlightRegionType.FUNCTION_TYPE_ALIAS: SemanticTokenTypes.type,
   HighlightRegionType.IDENTIFIER_DEFAULT: CustomSemanticTokenTypes.source,
-  HighlightRegionType.INSTANCE_FIELD_DECLARATION: SemanticTokenTypes.variable,
-  HighlightRegionType.INSTANCE_FIELD_REFERENCE: SemanticTokenTypes.variable,
+  HighlightRegionType.IMPORT_PREFIX: SemanticTokenTypes.variable,
+  HighlightRegionType.INSTANCE_FIELD_DECLARATION: SemanticTokenTypes.property,
+  HighlightRegionType.INSTANCE_FIELD_REFERENCE: SemanticTokenTypes.property,
   HighlightRegionType.INSTANCE_GETTER_DECLARATION: SemanticTokenTypes.property,
   HighlightRegionType.INSTANCE_GETTER_REFERENCE: SemanticTokenTypes.property,
   HighlightRegionType.INSTANCE_METHOD_DECLARATION: SemanticTokenTypes.method,
@@ -122,7 +129,7 @@
   HighlightRegionType.LOCAL_VARIABLE_REFERENCE: SemanticTokenTypes.variable,
   HighlightRegionType.PARAMETER_DECLARATION: SemanticTokenTypes.parameter,
   HighlightRegionType.PARAMETER_REFERENCE: SemanticTokenTypes.parameter,
-  HighlightRegionType.STATIC_FIELD_DECLARATION: SemanticTokenTypes.variable,
+  HighlightRegionType.STATIC_FIELD_DECLARATION: SemanticTokenTypes.property,
   HighlightRegionType.STATIC_GETTER_DECLARATION: SemanticTokenTypes.property,
   HighlightRegionType.STATIC_GETTER_REFERENCE: SemanticTokenTypes.property,
   HighlightRegionType.STATIC_METHOD_DECLARATION: SemanticTokenTypes.method,
@@ -138,9 +145,9 @@
   HighlightRegionType.TOP_LEVEL_GETTER_REFERENCE: SemanticTokenTypes.property,
   HighlightRegionType.TOP_LEVEL_SETTER_DECLARATION: SemanticTokenTypes.property,
   HighlightRegionType.TOP_LEVEL_SETTER_REFERENCE: SemanticTokenTypes.property,
-  HighlightRegionType.TOP_LEVEL_VARIABLE: SemanticTokenTypes.variable,
+  HighlightRegionType.TOP_LEVEL_VARIABLE: SemanticTokenTypes.property,
   HighlightRegionType.TOP_LEVEL_VARIABLE_DECLARATION:
-      SemanticTokenTypes.variable,
+      SemanticTokenTypes.property,
   HighlightRegionType.TYPE_ALIAS: SemanticTokenTypes.type,
   HighlightRegionType.TYPE_NAME_DYNAMIC: SemanticTokenTypes.type,
   HighlightRegionType.TYPE_PARAMETER: SemanticTokenTypes.typeParameter,
diff --git a/pkg/analysis_server/lib/src/provisional/completion/dart/completion_dart.dart b/pkg/analysis_server/lib/src/provisional/completion/dart/completion_dart.dart
index c2d764a..90c6495 100644
--- a/pkg/analysis_server/lib/src/provisional/completion/dart/completion_dart.dart
+++ b/pkg/analysis_server/lib/src/provisional/completion/dart/completion_dart.dart
@@ -64,12 +64,6 @@
   /// is occurring.
   LibraryElement get libraryElement;
 
-  /// The source for the library containing the completion request.
-  /// This may be different from the source in which the completion is requested
-  /// if the completion is being requested in a part file.
-  /// This may be `null` if the library for a part file cannot be determined.
-  Source? get librarySource;
-
   /// Answer the [DartType] for Object in dart:core
   DartType get objectType;
 
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 039cd5d..6b3f53c 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
@@ -104,10 +104,10 @@
 
     request.checkAborted();
 
-    var range = dartRequest.replacementRange;
+    var replacementRange = dartRequest.replacementRange;
     (request as CompletionRequestImpl)
-      ..replacementOffset = range.offset
-      ..replacementLength = range.length;
+      ..replacementOffset = replacementRange.offset
+      ..replacementLength = replacementRange.length;
 
     // Request Dart specific completions from each contributor
     var builder = SuggestionBuilder(dartRequest, listener: listener);
@@ -244,87 +244,61 @@
 
 /// The information about a requested list of completions within a Dart file.
 class DartCompletionRequestImpl implements DartCompletionRequest {
+  @override
+  final CompletionPreference completionPreference;
+
+  @override
+  final DartType? contextType;
+
+  @override
+  final DartdocDirectiveInfo dartdocDirectiveInfo;
+
+  final DocumentationCache? documentationCache;
+
+  @override
+  final Expression? dotTarget;
+
+  @override
+  final FeatureComputer featureComputer;
+
+  @override
+  final int offset;
+
+  @override
+  final OpType opType;
+
+  @override
+  final SourceRange replacementRange;
+
   final CompletionRequest request;
 
   @override
   final ResolvedUnitResult result;
 
   @override
-  final ResourceProvider resourceProvider;
-
-  @override
-  final InterfaceType objectType;
-
-  @override
   final Source source;
 
   @override
-  final int offset;
+  final CompletionTarget target;
+
+  DartCompletionRequestImpl._({
+    required this.completionPreference,
+    required this.contextType,
+    required this.dartdocDirectiveInfo,
+    required this.documentationCache,
+    required this.dotTarget,
+    required this.featureComputer,
+    required this.offset,
+    required this.opType,
+    required this.replacementRange,
+    required this.request,
+    required this.result,
+    required this.source,
+    required this.target,
+  });
 
   @override
-  Expression? dotTarget;
-
-  @override
-  final Source librarySource;
-
-  @override
-  late CompletionTarget target;
-
-  OpType? _opType;
-
-  @override
-  final FeatureComputer featureComputer;
-
-  @override
-  final DartdocDirectiveInfo dartdocDirectiveInfo;
-
-  /// A flag indicating whether the [_contextType] has been computed.
-  bool _hasComputedContextType = false;
-
-  /// The context type associated with the target's `containingNode`.
-  DartType? _contextType;
-
-  final CompletionRequest _originalRequest;
-
-  SourceRange? _replacementRange;
-
-  @override
-  final CompletionPreference completionPreference;
-
-  final DocumentationCache? documentationCache;
-
-  DartCompletionRequestImpl._(
-      this.request,
-      this.result,
-      this.resourceProvider,
-      this.objectType,
-      this.librarySource,
-      this.source,
-      this.offset,
-      CompilationUnit unit,
-      this.dartdocDirectiveInfo,
-      this._originalRequest,
-      {CompletionPreference? completionPreference,
-      this.documentationCache})
-      : featureComputer =
-            FeatureComputer(result.typeSystem, result.typeProvider),
-        completionPreference =
-            completionPreference ?? CompletionPreference.insert {
-    _updateTargets(unit);
-  }
-
-  @override
-  DartType? get contextType {
-    if (!_hasComputedContextType) {
-      _contextType = featureComputer.computeContextType(
-          target.containingNode, target.offset);
-      _hasComputedContextType = true;
-    }
-    return _contextType;
-  }
-
-  @override
-  FeatureSet get featureSet => result.libraryElement.featureSet;
+  FeatureSet get featureSet => libraryElement.featureSet;
 
   @override
   bool get includeIdentifiers {
@@ -341,28 +315,10 @@
   LibraryElement get libraryElement => result.libraryElement;
 
   @override
-  OpType get opType {
-    var opType = _opType;
-    if (opType == null) {
-      opType = OpType.forCompletion(target, offset);
-      var contextType = this.contextType;
-      if (contextType is FunctionType) {
-        contextType = contextType.returnType;
-      }
-      if (contextType != null && contextType.isVoid) {
-        opType.includeVoidReturnSuggestions = true;
-      }
-      _opType = opType;
-    }
-    return opType;
-  }
+  DartType get objectType => libraryElement.typeProvider.objectType;
 
-  /// The source range that represents the region of text that should be
-  /// replaced when a suggestion is selected.
   @override
-  SourceRange get replacementRange {
-    return _replacementRange ??= target.computeReplacementRange(offset);
-  }
+  ResourceProvider get resourceProvider => result.session.resourceProvider;
 
   @override
   String? get sourceContents => result.content;
@@ -402,65 +358,80 @@
   /// Throw [AbortCompletion] if the completion request has been aborted.
   @override
   void checkAborted() {
-    _originalRequest.checkAborted();
-  }
-
-  /// Update the completion [target] and [dotTarget] based on the given [unit].
-  void _updateTargets(CompilationUnit unit) {
-    _opType = null;
-    dotTarget = null;
-    target = CompletionTarget.forOffset(unit, offset);
-    var node = target.containingNode;
-    if (node is MethodInvocation) {
-      if (identical(node.methodName, target.entity)) {
-        dotTarget = node.realTarget;
-      } else if (node.isCascaded &&
-          node.operator!.offset + 1 == target.offset) {
-        dotTarget = node.realTarget;
-      }
-    }
-    if (node is PropertyAccess) {
-      if (identical(node.propertyName, target.entity)) {
-        dotTarget = node.realTarget;
-      } else if (node.isCascaded && node.operator.offset + 1 == target.offset) {
-        dotTarget = node.realTarget;
-      }
-    }
-    if (node is PrefixedIdentifier) {
-      if (identical(node.identifier, target.entity)) {
-        dotTarget = node.prefix;
-      }
-    }
+    request.checkAborted();
   }
 
   /// Return a newly created completion request based on the given [request].
   /// This method will throw [AbortCompletion] if the completion request has
   /// been aborted.
-  static Future<DartCompletionRequestImpl> from(
+  static DartCompletionRequestImpl from(
     CompletionRequest request, {
     DartdocDirectiveInfo? dartdocDirectiveInfo,
-    CompletionPreference? completionPreference,
+    CompletionPreference completionPreference = CompletionPreference.insert,
     DocumentationCache? documentationCache,
-  }) async {
+  }) {
     request.checkAborted();
 
-    var unit = request.result.unit;
-    var libSource = unit.declaredElement!.library.source;
-    var objectType = request.result.typeProvider.objectType;
+    var result = request.result;
+    var offset = request.offset;
+
+    var target = CompletionTarget.forOffset(result.unit, offset);
+    var dotTarget = _dotTarget(target);
+
+    var featureComputer = FeatureComputer(
+      result.typeSystem,
+      result.typeProvider,
+    );
+
+    var contextType = featureComputer.computeContextType(
+      target.containingNode,
+      offset,
+    );
+
+    var opType = OpType.forCompletion(target, offset);
+    if (contextType != null && contextType.isVoid) {
+      opType.includeVoidReturnSuggestions = true;
+    }
 
     return DartCompletionRequestImpl._(
-      request,
-      request.result,
-      request.resourceProvider,
-      objectType,
-      libSource,
-      request.source,
-      request.offset,
-      unit,
-      dartdocDirectiveInfo ?? DartdocDirectiveInfo(),
-      request,
       completionPreference: completionPreference,
+      contextType: contextType,
+      dartdocDirectiveInfo: dartdocDirectiveInfo ?? DartdocDirectiveInfo(),
       documentationCache: documentationCache,
+      dotTarget: dotTarget,
+      featureComputer: featureComputer,
+      offset: offset,
+      opType: opType,
+      replacementRange: target.computeReplacementRange(offset),
+      request: request,
+      result: request.result,
+      source: request.source,
+      target: target,
     );
   }
+
+  /// TODO(scheglov) Should this be a property of [CompletionTarget]?
+  static Expression? _dotTarget(CompletionTarget target) {
+    var node = target.containingNode;
+    var offset = target.offset;
+    if (node is MethodInvocation) {
+      if (identical(node.methodName, target.entity)) {
+        return node.realTarget;
+      } else if (node.isCascaded && node.operator!.offset + 1 == offset) {
+        return node.realTarget;
+      }
+    }
+    if (node is PropertyAccess) {
+      if (identical(node.propertyName, target.entity)) {
+        return node.realTarget;
+      } else if (node.isCascaded && node.operator.offset + 1 == offset) {
+        return node.realTarget;
+      }
+    }
+    if (node is PrefixedIdentifier) {
+      if (identical(node.identifier, target.entity)) {
+        return node.prefix;
+      }
+    }
+  }
 }
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/fuzzy_filter_sort.dart b/pkg/analysis_server/lib/src/services/completion/dart/fuzzy_filter_sort.dart
new file mode 100644
index 0000000..4408a7a
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/completion/dart/fuzzy_filter_sort.dart
@@ -0,0 +1,60 @@
+// 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/protocol_server.dart';
+import 'package:analysis_server/src/services/completion/filtering/fuzzy_matcher.dart';
+
+/// Filters and scores [suggestions] according to how well they match the
+/// [pattern]. Sorts [suggestions] by the score, relevance, and name.
+List<CompletionSuggestion> fuzzyFilterSort({
+  required String pattern,
+  required List<CompletionSuggestion> suggestions,
+}) {
+  var matcher = FuzzyMatcher(pattern, matchStyle: MatchStyle.SYMBOL);
+
+  double score(CompletionSuggestion suggestion) {
+    var suggestionTextToMatch = suggestion.completion;
+
+    if (suggestion.kind == CompletionSuggestionKind.NAMED_ARGUMENT) {
+      var index = suggestionTextToMatch.indexOf(':');
+      if (index != -1) {
+        suggestionTextToMatch = suggestionTextToMatch.substring(0, index);
+      }
+    }
+
+    return matcher.score(suggestionTextToMatch);
+  }
+
+  var scored = suggestions
+      .map((e) => _FuzzyScoredSuggestion(e, score(e)))
+      .where((e) => e.score > 0)
+      .toList();
+
+  scored.sort((a, b) {
+    // Prefer what the user requested by typing.
+    if (a.score > b.score) {
+      return -1;
+    } else if (a.score < b.score) {
+      return 1;
+    }
+
+    // Then prefer what is more relevant in the context.
+    if (a.suggestion.relevance != b.suggestion.relevance) {
+      return b.suggestion.relevance - a.suggestion.relevance;
+    }
+
+    // Other things being equal, sort by name.
+    return a.suggestion.completion.compareTo(b.suggestion.completion);
+  });
+
+  return scored.map((e) => e.suggestion).toList();
+}
+
+/// [CompletionSuggestion] scored using [FuzzyMatcher].
+class _FuzzyScoredSuggestion {
+  final CompletionSuggestion suggestion;
+  final double score;
+
+  _FuzzyScoredSuggestion(this.suggestion, this.score);
+}
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/imported_reference_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/imported_reference_contributor.dart
index c10194f..eb2bd03 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/imported_reference_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/imported_reference_contributor.dart
@@ -26,7 +26,7 @@
     for (var importElement in imports) {
       var libraryElement = importElement.importedLibrary;
       if (libraryElement != null) {
-        _buildSuggestions(request, builder, importElement.namespace,
+        _buildSuggestions(importElement.namespace,
             prefix: importElement.prefix?.name);
         if (libraryElement.isDartCore &&
             request.opType.includeTypeNameSuggestions) {
@@ -36,9 +36,7 @@
     }
   }
 
-  void _buildSuggestions(DartCompletionRequest request,
-      SuggestionBuilder builder, Namespace namespace,
-      {String? prefix}) {
+  void _buildSuggestions(Namespace namespace, {String? prefix}) {
     var visitor = LibraryElementSuggestionBuilder(request, builder, prefix);
     for (var elem in namespace.definedNames.values) {
       elem.accept(visitor);
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/library_member_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/library_member_contributor.dart
index 460324a..0b49e0f 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/library_member_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/library_member_contributor.dart
@@ -27,16 +27,12 @@
       var elem = targetId.staticElement;
       if (elem is PrefixElement && !elem.isSynthetic) {
         var imports = request.libraryElement.imports;
-        _buildSuggestions(request, builder, elem, imports);
+        _buildSuggestions(elem, imports);
       }
     }
   }
 
-  void _buildSuggestions(
-      DartCompletionRequest request,
-      SuggestionBuilder builder,
-      PrefixElement elem,
-      List<ImportElement> imports) {
+  void _buildSuggestions(PrefixElement elem, List<ImportElement> imports) {
     var parent = request.target.containingNode.parent;
     var typesOnly = parent is NamedType;
     var isConstructor = parent?.parent is ConstructorName;
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/local_reference_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/local_reference_contributor.dart
index 39c1dda..dd5d733 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/local_reference_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/local_reference_contributor.dart
@@ -89,15 +89,14 @@
           var declaredElement = classOrMixin.declaredElement;
           if (declaredElement != null) {
             memberBuilder = MemberSuggestionBuilder(request, builder);
-            _computeSuggestionsForClass(declaredElement, request);
+            _computeSuggestionsForClass(declaredElement);
           }
         }
       }
     }
   }
 
-  void _addSuggestionsForType(InterfaceType type, DartCompletionRequest request,
-      double inheritanceDistance,
+  void _addSuggestionsForType(InterfaceType type, double inheritanceDistance,
       {bool isFunctionalArgument = false}) {
     var opType = request.opType;
     if (!isFunctionalArgument) {
@@ -138,8 +137,7 @@
     }
   }
 
-  void _computeSuggestionsForClass(
-      ClassElement classElement, DartCompletionRequest request) {
+  void _computeSuggestionsForClass(ClassElement classElement) {
     var isFunctionalArgument = request.target.isFunctionalArgument();
     classMemberSuggestionKind = isFunctionalArgument
         ? CompletionSuggestionKind.IDENTIFIER
@@ -147,7 +145,7 @@
     for (var type in classElement.allSupertypes) {
       var inheritanceDistance = request.featureComputer
           .inheritanceDistanceFeature(classElement, type.element);
-      _addSuggestionsForType(type, request, inheritanceDistance,
+      _addSuggestionsForType(type, inheritanceDistance,
           isFunctionalArgument: isFunctionalArgument);
     }
   }
diff --git a/pkg/analysis_server/test/lsp/semantic_tokens_test.dart b/pkg/analysis_server/test/lsp/semantic_tokens_test.dart
index 7710bd8..907b8fa 100644
--- a/pkg/analysis_server/test/lsp/semantic_tokens_test.dart
+++ b/pkg/analysis_server/test/lsp/semantic_tokens_test.dart
@@ -54,6 +54,113 @@
     return results;
   }
 
+  Future<void> test_annotation() async {
+    final content = '''
+    import 'other_file.dart' as other;
+
+    @a
+    @A()
+    @A.n()
+    @B(A())
+    @other.C()
+    @other.C.n()
+    void foo() {}
+
+    class A {
+      const A();
+      const A.n();
+    }
+
+    const a = A();
+
+    class B {
+      final A a;
+      const B(this.a);
+    }
+    ''';
+
+    final otherContent = '''
+    class C {
+      const C();
+      const C.n();
+    }
+    ''';
+
+    final expectedStart = [
+      _Token('import', SemanticTokenTypes.keyword),
+      _Token("'other_file.dart'", SemanticTokenTypes.string),
+      _Token('as', SemanticTokenTypes.keyword),
+      _Token('other', SemanticTokenTypes.variable,
+          [CustomSemanticTokenModifiers.importPrefix]),
+      _Token('@', CustomSemanticTokenTypes.annotation),
+      _Token('a', SemanticTokenTypes.property,
+          [CustomSemanticTokenModifiers.annotation]),
+      _Token('@', CustomSemanticTokenTypes.annotation),
+      _Token('A', SemanticTokenTypes.class_,
+          [CustomSemanticTokenModifiers.annotation]),
+      _Token('(', CustomSemanticTokenTypes.annotation),
+      _Token(')', CustomSemanticTokenTypes.annotation),
+      _Token('@', CustomSemanticTokenTypes.annotation),
+      _Token('A', SemanticTokenTypes.class_,
+          [CustomSemanticTokenModifiers.annotation]),
+      _Token('.', CustomSemanticTokenTypes.annotation),
+      _Token('n', SemanticTokenTypes.method, [
+        CustomSemanticTokenModifiers.constructor,
+        CustomSemanticTokenModifiers.annotation
+      ]),
+      _Token('(', CustomSemanticTokenTypes.annotation),
+      _Token(')', CustomSemanticTokenTypes.annotation),
+      _Token('@', CustomSemanticTokenTypes.annotation),
+      _Token('B', SemanticTokenTypes.class_,
+          [CustomSemanticTokenModifiers.annotation]),
+      _Token('(', CustomSemanticTokenTypes.annotation),
+      _Token('A', SemanticTokenTypes.class_,
+          [CustomSemanticTokenModifiers.constructor]),
+      _Token(')', CustomSemanticTokenTypes.annotation),
+      _Token('@', CustomSemanticTokenTypes.annotation),
+      _Token('other', SemanticTokenTypes.variable,
+          [CustomSemanticTokenModifiers.importPrefix]),
+      _Token('.', CustomSemanticTokenTypes.annotation),
+      _Token('C', SemanticTokenTypes.class_,
+          [CustomSemanticTokenModifiers.annotation]),
+      _Token('(', CustomSemanticTokenTypes.annotation),
+      _Token(')', CustomSemanticTokenTypes.annotation),
+      _Token('@', CustomSemanticTokenTypes.annotation),
+      _Token('other', SemanticTokenTypes.variable,
+          [CustomSemanticTokenModifiers.importPrefix]),
+      _Token('.', CustomSemanticTokenTypes.annotation),
+      _Token('C', SemanticTokenTypes.class_,
+          [CustomSemanticTokenModifiers.annotation]),
+      _Token('.', CustomSemanticTokenTypes.annotation),
+      _Token('n', SemanticTokenTypes.method, [
+        CustomSemanticTokenModifiers.constructor,
+        CustomSemanticTokenModifiers.annotation
+      ]),
+      _Token('(', CustomSemanticTokenTypes.annotation),
+      _Token(')', CustomSemanticTokenTypes.annotation),
+      _Token('void', SemanticTokenTypes.keyword,
+          [CustomSemanticTokenModifiers.void_]),
+      _Token('foo', SemanticTokenTypes.function,
+          [SemanticTokenModifiers.declaration, SemanticTokenModifiers.static])
+    ];
+
+    final otherFilePath = join(projectFolderPath, 'lib', 'other_file.dart');
+    final otherFileUri = Uri.file(otherFilePath);
+
+    await initialize();
+    await openFile(mainFileUri, withoutMarkers(content));
+    await openFile(otherFileUri, withoutMarkers(otherContent));
+
+    final tokens = await getSemanticTokens(mainFileUri);
+    final decoded = decodeSemanticTokens(content, tokens);
+    expect(
+      // Only check the first expectedStart.length items since the test code
+      // is mostly unrelated to the annotations.
+      decoded.sublist(0, expectedStart.length),
+      equals(expectedStart),
+    );
+  }
+
   Future<void> test_class() async {
     final content = '''
     /// class docs
@@ -110,26 +217,26 @@
       _Token('MyClass', SemanticTokenTypes.class_,
           [CustomSemanticTokenModifiers.constructor]),
       _Token('final', SemanticTokenTypes.keyword),
-      _Token('a', SemanticTokenTypes.variable,
+      _Token('a', SemanticTokenTypes.property,
           [SemanticTokenModifiers.declaration]),
       _Token('MyClass', SemanticTokenTypes.class_,
           [CustomSemanticTokenModifiers.constructor]),
       _Token('final', SemanticTokenTypes.keyword),
-      _Token('b', SemanticTokenTypes.variable,
+      _Token('b', SemanticTokenTypes.property,
           [SemanticTokenModifiers.declaration]),
       _Token('MyClass', SemanticTokenTypes.class_,
           [CustomSemanticTokenModifiers.constructor]),
       _Token('named', SemanticTokenTypes.method,
           [CustomSemanticTokenModifiers.constructor]),
       _Token('final', SemanticTokenTypes.keyword),
-      _Token('c', SemanticTokenTypes.variable,
+      _Token('c', SemanticTokenTypes.property,
           [SemanticTokenModifiers.declaration]),
       _Token('MyClass', SemanticTokenTypes.class_,
           [CustomSemanticTokenModifiers.constructor]),
       _Token('factory', SemanticTokenTypes.method,
           [CustomSemanticTokenModifiers.constructor]),
       _Token('final', SemanticTokenTypes.keyword),
-      _Token('d', SemanticTokenTypes.variable,
+      _Token('d', SemanticTokenTypes.property,
           [SemanticTokenModifiers.declaration]),
       _Token('MyClass', SemanticTokenTypes.class_),
       _Token('named', SemanticTokenTypes.method,
@@ -166,14 +273,14 @@
       _Token('/// field docs', SemanticTokenTypes.comment,
           [SemanticTokenModifiers.documentation]),
       _Token('String', SemanticTokenTypes.class_),
-      _Token('myField', SemanticTokenTypes.variable,
+      _Token('myField', SemanticTokenTypes.property,
           [SemanticTokenModifiers.declaration]),
       _Token("'FieldVal'", SemanticTokenTypes.string),
       _Token('/// static field docs', SemanticTokenTypes.comment,
           [SemanticTokenModifiers.documentation]),
       _Token('static', SemanticTokenTypes.keyword),
       _Token('String', SemanticTokenTypes.class_),
-      _Token('myStaticField', SemanticTokenTypes.variable,
+      _Token('myStaticField', SemanticTokenTypes.property,
           [SemanticTokenModifiers.declaration, SemanticTokenModifiers.static]),
       _Token("'StaticFieldVal'", SemanticTokenTypes.string),
       _Token('main', SemanticTokenTypes.function,
@@ -305,7 +412,8 @@
       _Token('/// method docs', SemanticTokenTypes.comment,
           [SemanticTokenModifiers.documentation]),
       _Token('@', CustomSemanticTokenTypes.annotation),
-      _Token('override', SemanticTokenTypes.property),
+      _Token('override', SemanticTokenTypes.property,
+          [CustomSemanticTokenModifiers.annotation]),
       _Token('void', SemanticTokenTypes.keyword,
           [CustomSemanticTokenModifiers.void_]),
       _Token('myMethod', SemanticTokenTypes.method,
@@ -371,7 +479,7 @@
       _Token('class', SemanticTokenTypes.keyword),
       _Token('MyClass', SemanticTokenTypes.class_),
       _Token('String', SemanticTokenTypes.class_),
-      _Token('aaa', SemanticTokenTypes.variable,
+      _Token('aaa', SemanticTokenTypes.property,
           [SemanticTokenModifiers.declaration]),
       _Token('/// before [', SemanticTokenTypes.comment,
           [SemanticTokenModifiers.documentation]),
@@ -479,7 +587,7 @@
         plugin.HighlightRegion(plugin.HighlightRegionType.CLASS, 0, 5),
         plugin.HighlightRegion(plugin.HighlightRegionType.LITERAL_STRING, 6, 6),
         plugin.HighlightRegion(
-            plugin.HighlightRegionType.TOP_LEVEL_VARIABLE_DECLARATION, 13, 8),
+            plugin.HighlightRegionType.LOCAL_VARIABLE_DECLARATION, 13, 8),
       ],
     );
     configureTestPlugin(notification: pluginResult.toNotification());
@@ -579,11 +687,11 @@
   }
 
   Future<void> test_lastLine_code() async {
-    final content = 'String var;';
+    final content = 'String bar;';
 
     final expected = [
       _Token('String', SemanticTokenTypes.class_),
-      _Token('var', SemanticTokenTypes.variable,
+      _Token('bar', SemanticTokenTypes.property,
           [SemanticTokenModifiers.declaration]),
     ];
 
@@ -705,7 +813,7 @@
         _Token('/// test', SemanticTokenTypes.comment,
             [SemanticTokenModifiers.documentation]),
         _Token('bool', SemanticTokenTypes.class_),
-        _Token('test$i', SemanticTokenTypes.variable,
+        _Token('test$i', SemanticTokenTypes.property,
             [SemanticTokenModifiers.declaration]),
         _Token('false', CustomSemanticTokenTypes.boolean),
       ],
@@ -927,12 +1035,12 @@
       _Token('c', SemanticTokenTypes.parameter),
 
       _Token('const', SemanticTokenTypes.keyword),
-      _Token('string1', SemanticTokenTypes.variable,
+      _Token('string1', SemanticTokenTypes.property,
           [SemanticTokenModifiers.declaration]),
       _Token("'test'", SemanticTokenTypes.string),
 
       _Token('const', SemanticTokenTypes.keyword),
-      _Token('string2', SemanticTokenTypes.variable,
+      _Token('string2', SemanticTokenTypes.property,
           [SemanticTokenModifiers.declaration]),
       _Token(r"'test1 ", SemanticTokenTypes.string),
       _Token(r'$', CustomSemanticTokenTypes.source,
@@ -954,12 +1062,12 @@
 
       // string3 is raw and should be treated as a single string.
       _Token('const', SemanticTokenTypes.keyword),
-      _Token('string3', SemanticTokenTypes.variable,
+      _Token('string3', SemanticTokenTypes.property,
           [SemanticTokenModifiers.declaration]),
       _Token(r"r'$string1 ${string1.length}'", SemanticTokenTypes.string),
       _Token('const', SemanticTokenTypes.keyword),
 
-      _Token('string4', SemanticTokenTypes.variable,
+      _Token('string4', SemanticTokenTypes.property,
           [SemanticTokenModifiers.declaration]),
       _Token("'''\n", SemanticTokenTypes.string),
       _Token('multi\n', SemanticTokenTypes.string),
@@ -987,7 +1095,7 @@
 
     final expected = [
       _Token('const', SemanticTokenTypes.keyword),
-      _Token('string1', SemanticTokenTypes.variable,
+      _Token('string1', SemanticTokenTypes.property,
           [SemanticTokenModifiers.declaration]),
       _Token("'it", SemanticTokenTypes.string),
       _Token(r"\'", SemanticTokenTypes.string,
@@ -999,7 +1107,7 @@
           [CustomSemanticTokenModifiers.escape]),
       _Token(r"'", SemanticTokenTypes.string),
       _Token('const', SemanticTokenTypes.keyword),
-      _Token('string2', SemanticTokenTypes.variable,
+      _Token('string2', SemanticTokenTypes.property,
           [SemanticTokenModifiers.declaration]),
       _Token("'hex ", SemanticTokenTypes.string),
       _Token(r'\x12', SemanticTokenTypes.string,
@@ -1009,7 +1117,7 @@
       // The 99 is not part of the escape
       _Token("99'", SemanticTokenTypes.string),
       _Token('const', SemanticTokenTypes.keyword),
-      _Token('string3', SemanticTokenTypes.variable,
+      _Token('string3', SemanticTokenTypes.property,
           [SemanticTokenModifiers.declaration]),
       _Token("'unicode ", SemanticTokenTypes.string),
       _Token(r'\u1234', SemanticTokenTypes.string,
@@ -1044,13 +1152,20 @@
     bool get abc => true;
 
     final funcTearOff = func;
+
+    void main() {
+      strings;
+      func;
+      abc;
+      funcTearOff;
+    }
     ''';
 
     final expected = [
       _Token('/// strings docs', SemanticTokenTypes.comment,
           [SemanticTokenModifiers.documentation]),
       _Token('const', SemanticTokenTypes.keyword),
-      _Token('strings', SemanticTokenTypes.variable,
+      _Token('strings', SemanticTokenTypes.property,
           [SemanticTokenModifiers.declaration]),
       _Token('String', SemanticTokenTypes.class_),
       _Token('"test"', SemanticTokenTypes.string),
@@ -1074,9 +1189,17 @@
           [SemanticTokenModifiers.declaration]),
       _Token('true', CustomSemanticTokenTypes.boolean),
       _Token('final', SemanticTokenTypes.keyword),
-      _Token('funcTearOff', SemanticTokenTypes.variable,
+      _Token('funcTearOff', SemanticTokenTypes.property,
           [SemanticTokenModifiers.declaration]),
       _Token('func', SemanticTokenTypes.function),
+      _Token('void', SemanticTokenTypes.keyword,
+          [CustomSemanticTokenModifiers.void_]),
+      _Token('main', SemanticTokenTypes.function,
+          [SemanticTokenModifiers.declaration, SemanticTokenModifiers.static]),
+      _Token('strings', SemanticTokenTypes.property),
+      _Token('func', SemanticTokenTypes.function),
+      _Token('abc', SemanticTokenTypes.property),
+      _Token('funcTearOff', SemanticTokenTypes.property),
     ];
 
     await initialize();
diff --git a/pkg/analysis_server/test/services/completion/dart/completion_contributor_util.dart b/pkg/analysis_server/test/services/completion/dart/completion_contributor_util.dart
index 35f317f..6bfb885 100644
--- a/pkg/analysis_server/test/services/completion/dart/completion_contributor_util.dart
+++ b/pkg/analysis_server/test/services/completion/dart/completion_contributor_util.dart
@@ -537,7 +537,7 @@
     return await baseRequest.performance.runRequestOperation(
       (performance) async {
         // Build the request
-        var request = await DartCompletionRequestImpl.from(
+        var request = DartCompletionRequestImpl.from(
           baseRequest,
           dartdocDirectiveInfo: dartdocInfo,
         );
diff --git a/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart b/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart
index 9e8f9e7..3924da2 100644
--- a/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart
@@ -57,12 +57,7 @@
         completionOffset,
         CompletionPerformance());
     await baseRequest.performance.runRequestOperation((performance) async {
-      var requestCompleter = Completer<DartCompletionRequest>();
-      DartCompletionRequestImpl.from(baseRequest)
-          .then((DartCompletionRequest request) {
-        requestCompleter.complete(request);
-      });
-      request = await performAnalysis(200, requestCompleter);
+      request = DartCompletionRequestImpl.from(baseRequest);
     });
 
     var directives = request.target.unit.directives;
diff --git a/pkg/analysis_server/test/src/services/completion/dart/suggestion_builder_test.dart b/pkg/analysis_server/test/src/services/completion/dart/suggestion_builder_test.dart
index 1bd5992..64130c8 100644
--- a/pkg/analysis_server/test/src/services/completion/dart/suggestion_builder_test.dart
+++ b/pkg/analysis_server/test/src/services/completion/dart/suggestion_builder_test.dart
@@ -29,7 +29,7 @@
   }
 
   Future<CompletionSuggestion> forTopLevelFunction(String functionName) async {
-    var request = await DartCompletionRequestImpl.from(
+    var request = DartCompletionRequestImpl.from(
       CompletionRequestImpl(testAnalysisResult, 0, CompletionPerformance()),
     );
     var builder = SuggestionBuilder(request);
diff --git a/pkg/analysis_server/test/stress/completion/completion_runner.dart b/pkg/analysis_server/test/stress/completion/completion_runner.dart
index dcb36a9..921a82f 100644
--- a/pkg/analysis_server/test/stress/completion/completion_runner.dart
+++ b/pkg/analysis_server/test/stress/completion/completion_runner.dart
@@ -100,7 +100,7 @@
 
           timer.start();
           var request = CompletionRequestImpl(result, offset, statistics);
-          var dartRequest = await DartCompletionRequestImpl.from(request);
+          var dartRequest = DartCompletionRequestImpl.from(request);
           var suggestions = await request.performance.runRequestOperation(
             (performance) async {
               return await contributor.computeSuggestions(
diff --git a/pkg/analysis_server/tool/code_completion/completion_metrics.dart b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
index 6b4dd8a..4746652 100644
--- a/pkg/analysis_server/tool/code_completion/completion_metrics.dart
+++ b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
@@ -1208,7 +1208,7 @@
           availableSuggestionsParams]) async {
     List<protocol.CompletionSuggestion> suggestions;
 
-    var dartRequest = await DartCompletionRequestImpl.from(
+    var dartRequest = DartCompletionRequestImpl.from(
       request,
       dartdocDirectiveInfo: dartdocDirectiveInfo,
       documentationCache: documentationCache,
@@ -1385,7 +1385,7 @@
           late List<protocol.CompletionSuggestion> suggestions;
           await request.performance.runRequestOperation(
             (performance) async {
-              var dartRequest = await DartCompletionRequestImpl.from(
+              var dartRequest = DartCompletionRequestImpl.from(
                 request,
                 documentationCache: documentationCache,
               );
diff --git a/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart b/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
index 4db5fcf..be5b2ba 100644
--- a/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
@@ -110,7 +110,7 @@
 
     // Parse all files.
     performance.run('parse', (performance) {
-      for (FileState file in _library.libraryFiles) {
+      for (FileState file in _library.files().ofLibrary) {
         if (completionPath == null || file.path == completionPath) {
           units[file] = _parse(
             file: file,
@@ -231,19 +231,19 @@
 
     if (_analysisOptions.lint) {
       performance.run('computeLints', (performance) {
-        var allUnits = _library.libraryFiles.map((file) {
+        var allUnits = _library.files().ofLibrary.map((file) {
           var content = getFileContent(file);
           return LinterContextUnit(content, units[file]!);
         }).toList();
         for (int i = 0; i < allUnits.length; i++) {
-          _computeLints(_library.libraryFiles[i], allUnits[i], allUnits);
+          _computeLints(_library.files().ofLibrary[i], allUnits[i], allUnits);
         }
       });
     }
 
     // This must happen after all other diagnostics have been computed but
     // before the list of diagnostics has been filtered.
-    for (var file in _library.libraryFiles) {
+    for (var file in _library.files().ofLibrary) {
       IgnoreValidator(
         _getErrorReporter(file),
         _getErrorListener(file).errors,
@@ -472,7 +472,7 @@
   }
 
   bool _isExistingSource(Source source) {
-    for (var file in _library.directReferencedFiles) {
+    for (var file in _library.files().directReferencedFiles) {
       if (file.uri == source.uri) {
         return file.exists;
       }
@@ -595,7 +595,7 @@
       } else if (directive is PartDirectiveImpl) {
         StringLiteral partUri = directive.uri;
 
-        FileState partFile = _library.partedFiles[partIndex];
+        FileState partFile = _library.files().parted[partIndex];
         var partUnit = units[partFile]!;
         CompilationUnitElement partElement = _libraryElement.parts[partIndex];
         partUnit.element = partElement;
diff --git a/pkg/analyzer/lib/src/dart/micro/library_graph.dart b/pkg/analyzer/lib/src/dart/micro/library_graph.dart
index 2626139..a0379af 100644
--- a/pkg/analyzer/lib/src/dart/micro/library_graph.dart
+++ b/pkg/analyzer/lib/src/dart/micro/library_graph.dart
@@ -112,69 +112,22 @@
 }
 
 class FileState {
-  final FileSystemState _fsState;
-
-  /// The path of the file.
-  final String path;
-
-  /// The URI of the file.
-  final Uri uri;
-
-  /// The [Source] of the file with the [uri].
-  final Source source;
-
-  /// The [WorkspacePackage] that contains this file.
-  ///
-  /// It might be `null` if the file is outside of the workspace.
-  final WorkspacePackage? workspacePackage;
-
-  /// The [FeatureSet] for all files in the analysis context.
-  ///
-  /// Usually it is the feature set of the latest language version, plus
-  /// possibly additional enabled experiments (from the analysis options file,
-  /// or from SDK allowed experiments).
-  ///
-  /// This feature set is then restricted, with the [_packageLanguageVersion],
-  /// or with a `@dart` language override token in the file header.
-  final FeatureSet _contextFeatureSet;
-
-  /// The language version for the package that contains this file.
-  final Version _packageLanguageVersion;
+  final _FileStateUnlinked _unlinked;
 
   /// Files that reference this file.
   final List<FileState> referencingFiles = [];
 
-  final List<FileState> importedFiles = [];
-  final List<FileState> exportedFiles = [];
-  final List<FileState> partedFiles = [];
-  final Set<FileState> directReferencedFiles = {};
-  final Set<FileState> directReferencedLibraries = {};
-  final List<FileState> libraryFiles = [];
-  FileState? partOfLibrary;
+  _FileStateFiles? _files;
 
-  late Uint8List _digest;
-  late bool _exists;
-  late CiderUnlinkedUnit unlinked;
   LibraryCycle? _libraryCycle;
 
-  /// id of the cache entry with unlinked data.
-  late int unlinkedId;
+  FileState._(this._unlinked);
 
-  FileState._(
-    this._fsState,
-    this.path,
-    this.uri,
-    this.source,
-    this.workspacePackage,
-    this._contextFeatureSet,
-    this._packageLanguageVersion,
-  );
+  Uint8List get apiSignature => unlinkedUnit.apiSignature;
 
-  Uint8List get apiSignature => unlinked.unit.apiSignature;
+  Uint8List get digest => _unlinked.digest;
 
-  Uint8List get digest => _digest;
-
-  bool get exists => _exists;
+  bool get exists => _unlinked.exists;
 
   /// Return the [LibraryCycle] this file belongs to, even if it consists of
   /// just this file.  If the library cycle is not known yet, compute it.
@@ -185,7 +138,11 @@
     return _libraryCycle!;
   }
 
-  LineInfo get lineInfo => LineInfo(unlinked.unit.lineStarts);
+  LineInfo get lineInfo => LineInfo(unlinkedUnit.lineStarts);
+
+  FileState? get partOfLibrary => _unlinked.partOfLibrary;
+
+  String get path => _location.path;
 
   /// The resolved signature of the file, that depends on the [libraryCycle]
   /// signature, and the content of the file.
@@ -194,47 +151,62 @@
     signatureBuilder.addString(path);
     signatureBuilder.addBytes(libraryCycle.signature);
 
-    var content = getContentWithSameDigest();
+    var content = getContent();
     signatureBuilder.addString(content);
 
     return signatureBuilder.toHex();
   }
 
+  Source get source => _location.source;
+
+  int get unlinkedId => _unlinked.unlinkedId;
+
+  UnlinkedUnit get unlinkedUnit => _unlinked.unlinked.unit;
+
+  Uri get uri => _location.uri;
+
   /// Return the [uri] string.
   String get uriStr => uri.toString();
 
+  WorkspacePackage? get workspacePackage => _location.workspacePackage;
+
+  FileSystemState get _fsState => _location._fsState;
+
+  _FileStateLocation get _location => _unlinked.location;
+
   /// Collect all files that are transitively referenced by this file via
   /// imports, exports, and parts.
   void collectAllReferencedFiles(Set<String> referencedFiles) {
-    for (var file in {...importedFiles, ...exportedFiles, ...partedFiles}) {
+    for (var file in files().directReferencedFiles) {
       if (referencedFiles.add(file.path)) {
         file.collectAllReferencedFiles(referencedFiles);
       }
     }
   }
 
-  /// Return the content of the file, the empty string if cannot be read.
-  String getContent() {
-    try {
-      var resource = _fsState._resourceProvider.getFile(path);
-      return resource.readAsStringSync();
-    } catch (_) {
-      return '';
-    }
+  _FileStateFiles files({
+    OperationPerformanceImpl? performance,
+  }) {
+    return _files ??= _FileStateFiles(
+      owner: this,
+      performance: performance ?? OperationPerformanceImpl('<root>'),
+    );
   }
 
   /// Return the content of the file, the empty string if cannot be read.
   ///
-  /// Additionally, we read the file digest, end verify that it is the same
-  /// as the [_digest] that we recorded in [refresh]. If it is not, then the
-  /// file was changed, and we failed to call [FileSystemState.changeFile]
-  String getContentWithSameDigest() {
-    var digest = utf8.encode(_fsState.getFileDigest(path));
-    if (!const ListEquality<int>().equals(digest, _digest)) {
+  /// We read the file digest, end verify that it is the same as the digest
+  /// that was recorded during the file creation. If it is not, then the file
+  /// was changed, and we failed to call [FileSystemState.changeFile].
+  String getContent() {
+    var contentWithDigest = _location._getContent();
+
+    var digest = contentWithDigest.digest;
+    if (!const ListEquality<int>().equals(digest, _unlinked.digest)) {
       throw StateError('File was changed, but not invalidated: $path');
     }
 
-    return getContent();
+    return contentWithDigest.content;
   }
 
   void internal_setLibraryCycle(LibraryCycle cycle, String signature) {
@@ -243,366 +215,13 @@
 
   CompilationUnitImpl parse(
       AnalysisErrorListener errorListener, String content) {
-    CharSequenceReader reader = CharSequenceReader(content);
-    Scanner scanner = Scanner(source, reader, errorListener)
-      ..configureFeatures(
-        featureSetForOverriding: _contextFeatureSet,
-        featureSet: _contextFeatureSet.restrictToVersion(
-          _packageLanguageVersion,
-        ),
-      );
-    Token token = scanner.tokenize(reportScannerErrors: false);
-    LineInfo lineInfo = LineInfo(scanner.lineStarts);
-
-    // Pass the feature set from the scanner to the parser
-    // because the scanner may have detected a language version comment
-    // and downgraded the feature set it holds.
-    Parser parser = Parser(
-      source,
-      errorListener,
-      featureSet: scanner.featureSet,
-    );
-    parser.enableOptionalNewAndConst = true;
-    var unit = parser.parseCompilationUnit(token);
-    unit.lineInfo = lineInfo;
-
-    // StringToken uses a static instance of StringCanonicalizer, so we need
-    // to clear it explicitly once we are done using it for this file.
-    StringToken.canonicalizer.clear();
-
-    // TODO(scheglov) Use actual versions.
-    unit.languageVersion = LibraryLanguageVersion(
-      package: ExperimentStatus.currentVersion,
-      override: null,
-    );
-
-    return unit;
-  }
-
-  void refresh({
-    FileState? containingLibrary,
-    required OperationPerformanceImpl performance,
-  }) {
-    _fsState.testView.refreshedFiles.add(path);
-    performance.getDataInt('count').increment();
-
-    performance.run('digest', (_) {
-      _digest = utf8.encode(_fsState.getFileDigest(path)) as Uint8List;
-      _exists = _digest.isNotEmpty;
-    });
-
-    String unlinkedKey = '$path.unlinked';
-
-    // Prepare bytes of the unlinked bundle - existing or new.
-    // TODO(migration): should not be nullable
-    Uint8List? unlinkedBytes;
-    {
-      var unlinkedData = _fsState._byteStore.get(unlinkedKey, _digest);
-      unlinkedBytes = unlinkedData?.bytes;
-
-      if (unlinkedBytes == null || unlinkedBytes.isEmpty) {
-        var content = performance.run('content', (_) {
-          return getContent();
-        });
-
-        var unit = performance.run('parse', (performance) {
-          performance.getDataInt('count').increment();
-          performance.getDataInt('length').add(content.length);
-          return parse(AnalysisErrorListener.NULL_LISTENER, content);
-        });
-
-        performance.run('unlinked', (performance) {
-          var unlinkedBuilder = serializeAstCiderUnlinked(unit);
-          unlinkedBytes = unlinkedBuilder.toBytes();
-          performance.getDataInt('length').add(unlinkedBytes!.length);
-          unlinkedData =
-              _fsState._byteStore.putGet(unlinkedKey, _digest, unlinkedBytes!);
-          unlinkedBytes = unlinkedData!.bytes;
-        });
-
-        unlinked = CiderUnlinkedUnit.fromBytes(unlinkedBytes!);
-
-        // TODO(scheglov) We decode above only because we call it here.
-        performance.run('prefetch', (_) {
-          _prefetchDirectReferences(unlinked.unit);
-        });
-      }
-      unlinkedId = unlinkedData!.id;
-    }
-
-    // Read the unlinked bundle.
-    unlinked = CiderUnlinkedUnit.fromBytes(unlinkedBytes!);
-
-    // Build the graph.
-    for (var directive in unlinked.unit.imports) {
-      var file = _fileForRelativeUri(
-        relativeUri: directive.uri,
-        performance: performance,
-      );
-      if (file != null) {
-        importedFiles.add(file);
-      }
-    }
-    for (var directive in unlinked.unit.exports) {
-      var file = _fileForRelativeUri(
-        relativeUri: directive.uri,
-        performance: performance,
-      );
-      if (file != null) {
-        exportedFiles.add(file);
-      }
-    }
-    for (var uri in unlinked.unit.parts) {
-      var file = _fileForRelativeUri(
-        containingLibrary: this,
-        relativeUri: uri,
-        performance: performance,
-      );
-      if (file != null) {
-        partedFiles.add(file);
-      }
-    }
-    if (unlinked.unit.hasPartOfDirective) {
-      if (containingLibrary == null) {
-        _fsState.testView.partsDiscoveredLibraries.add(path);
-        var libraryName = unlinked.unit.partOfName;
-        var libraryUri = unlinked.unit.partOfUri;
-        partOfLibrary = null;
-        if (libraryName != null) {
-          _findPartOfNameLibrary(performance: performance);
-        } else if (libraryUri != null) {
-          partOfLibrary = _fileForRelativeUri(
-            relativeUri: libraryUri,
-            performance: performance,
-          );
-        }
-      } else {
-        partOfLibrary = containingLibrary;
-      }
-      if (partOfLibrary != null) {
-        directReferencedFiles.add(partOfLibrary!);
-      }
-    }
-    libraryFiles.add(this);
-    libraryFiles.addAll(partedFiles);
-
-    // Compute referenced files.
-    directReferencedFiles
-      ..addAll(importedFiles)
-      ..addAll(exportedFiles)
-      ..addAll(partedFiles);
-    directReferencedLibraries
-      ..addAll(importedFiles)
-      ..addAll(exportedFiles);
+    return _FileStateUnlinked.parse(errorListener, _location, content);
   }
 
   @override
   String toString() {
     return path;
   }
-
-  FileState? _fileForRelativeUri({
-    FileState? containingLibrary,
-    required String relativeUri,
-    required OperationPerformanceImpl performance,
-  }) {
-    if (relativeUri.isEmpty) {
-      return null;
-    }
-
-    Uri absoluteUri;
-    try {
-      absoluteUri = resolveRelativeUri(uri, Uri.parse(relativeUri));
-    } on FormatException {
-      return null;
-    }
-
-    var file = _fsState.getFileForUri(
-      containingLibrary: containingLibrary,
-      uri: absoluteUri,
-      performance: performance,
-    );
-    if (file == null) {
-      return null;
-    }
-
-    file.referencingFiles.add(this);
-    return file;
-  }
-
-  /// This file has a `part of some.library;` directive. Because it does not
-  /// specify the URI of the library, we don't know the library for sure.
-  /// But usually the library is one of the sibling files.
-  void _findPartOfNameLibrary({
-    required OperationPerformanceImpl performance,
-  }) {
-    var resourceProvider = _fsState._resourceProvider;
-    var pathContext = resourceProvider.pathContext;
-
-    var children = <Resource>[];
-    try {
-      var parent = resourceProvider.getFile(path).parent2;
-      children = parent.getChildren();
-    } catch (_) {}
-
-    for (var siblingFile in children) {
-      if (file_paths.isDart(pathContext, siblingFile.path)) {
-        var childState = _fsState.getFileForPath(
-          path: siblingFile.path,
-          performance: performance,
-        );
-        if (childState.partedFiles.contains(this)) {
-          partOfLibrary = childState;
-          break;
-        }
-      }
-    }
-  }
-
-  void _prefetchDirectReferences(UnlinkedUnit unlinkedUnit2) {
-    if (_fsState.prefetchFiles == null) {
-      return;
-    }
-
-    var paths = <String>{};
-
-    void findPathForUri(String relativeUri) {
-      if (relativeUri.isEmpty) {
-        return;
-      }
-      Uri absoluteUri;
-      try {
-        absoluteUri = resolveRelativeUri(uri, Uri.parse(relativeUri));
-      } on FormatException {
-        return;
-      }
-      var p = _fsState.getPathForUri(absoluteUri);
-      if (p != null) {
-        paths.add(p);
-      }
-    }
-
-    for (var directive in unlinked.unit.imports) {
-      findPathForUri(directive.uri);
-    }
-    for (var directive in unlinked.unit.exports) {
-      findPathForUri(directive.uri);
-    }
-    for (var uri in unlinked.unit.parts) {
-      findPathForUri(uri);
-    }
-    _fsState.prefetchFiles!(paths.toList());
-  }
-
-  static CiderUnlinkedUnit serializeAstCiderUnlinked(CompilationUnit unit) {
-    var exports = <UnlinkedNamespaceDirective>[];
-    var imports = <UnlinkedNamespaceDirective>[];
-    var parts = <String>[];
-    var hasDartCoreImport = false;
-    var hasLibraryDirective = false;
-    var hasPartOfDirective = false;
-    String? partOfName;
-    String? partOfUriStr;
-    for (var directive in unit.directives) {
-      if (directive is ExportDirective) {
-        var builder = _serializeNamespaceDirective(directive);
-        exports.add(builder);
-      } else if (directive is ImportDirective) {
-        var builder = _serializeNamespaceDirective(directive);
-        imports.add(builder);
-        if (builder.uri == 'dart:core') {
-          hasDartCoreImport = true;
-        }
-      } else if (directive is LibraryDirective) {
-        hasLibraryDirective = true;
-      } else if (directive is PartDirective) {
-        var uriStr = directive.uri.stringValue;
-        parts.add(uriStr ?? '');
-      } else if (directive is PartOfDirective) {
-        hasPartOfDirective = true;
-        var libraryName = directive.libraryName;
-        var uriStr = directive.uri?.stringValue;
-        if (libraryName != null) {
-          partOfName = libraryName.components.map((e) => e.name).join('.');
-        } else if (uriStr != null) {
-          partOfUriStr = uriStr;
-        }
-      }
-    }
-    if (!hasDartCoreImport) {
-      imports.add(
-        UnlinkedNamespaceDirective(
-          configurations: [],
-          uri: 'dart:core',
-        ),
-      );
-    }
-
-    var declaredExtensions = <String>[];
-    var declaredFunctions = <String>[];
-    var declaredTypes = <String>[];
-    var declaredVariables = <String>[];
-    for (var declaration in unit.declarations) {
-      if (declaration is ClassDeclaration) {
-        declaredTypes.add(declaration.name.name);
-      } else if (declaration is EnumDeclaration) {
-        declaredTypes.add(declaration.name.name);
-      } else if (declaration is ExtensionDeclaration) {
-        var name = declaration.name;
-        if (name != null) {
-          declaredExtensions.add(name.name);
-        }
-      } else if (declaration is FunctionDeclaration) {
-        declaredFunctions.add(declaration.name.name);
-      } else if (declaration is MixinDeclaration) {
-        declaredTypes.add(declaration.name.name);
-      } else if (declaration is TopLevelVariableDeclaration) {
-        for (var variable in declaration.variables.variables) {
-          declaredVariables.add(variable.name.name);
-        }
-      }
-    }
-
-    var unlinkedUnit = UnlinkedUnit(
-      apiSignature: computeUnlinkedApiSignature(unit),
-      exports: exports,
-      hasLibraryDirective: hasLibraryDirective,
-      hasPartOfDirective: hasPartOfDirective,
-      imports: imports,
-      informativeBytes: writeUnitInformative(unit),
-      lineStarts: Uint32List.fromList(unit.lineInfo!.lineStarts),
-      partOfName: partOfName,
-      partOfUri: partOfUriStr,
-      parts: parts,
-    );
-
-    return CiderUnlinkedUnit(
-      unit: unlinkedUnit,
-      topLevelDeclarations: CiderUnitTopLevelDeclarations(
-        extensionNames: declaredExtensions,
-        functionNames: declaredFunctions,
-        typeNames: declaredTypes,
-        variableNames: declaredVariables,
-      ),
-    );
-  }
-
-  static UnlinkedNamespaceDirective _serializeNamespaceDirective(
-    NamespaceDirective directive,
-  ) {
-    return UnlinkedNamespaceDirective(
-      configurations: directive.configurations.map((configuration) {
-        var name = configuration.name.components.join('.');
-        var value = configuration.value?.stringValue ?? '';
-        return UnlinkedNamespaceDirectiveConfiguration(
-          name: name,
-          value: value,
-          uri: configuration.uri.stringValue ?? '',
-        );
-      }).toList(),
-      uri: directive.uri.stringValue ?? '',
-    );
-  }
 }
 
 class FileSystemState {
@@ -659,7 +278,7 @@
     _uriToFile.remove(file.uri);
 
     // The removed file does not reference other file anymore.
-    for (var referencedFile in file.directReferencedFiles) {
+    for (var referencedFile in file.files().directReferencedFiles) {
       referencedFile.referencingFiles.remove(file);
     }
 
@@ -674,7 +293,7 @@
   Set<int> collectSharedDataIdentifiers() {
     var result = <int>{};
     for (var file in _pathToFile.values) {
-      result.add(file.unlinkedId);
+      result.add(file._unlinked.unlinkedId);
     }
     return result;
   }
@@ -730,17 +349,21 @@
       var featureSet = contextFeatureSet(path, uri, workspacePackage);
       var packageLanguageVersion =
           contextLanguageVersion(path, uri, workspacePackage);
-      file = FileState._(this, path, uri, source, workspacePackage, featureSet,
-          packageLanguageVersion);
 
+      var location = _FileStateLocation._(this, path, uri, source,
+          workspacePackage, featureSet, packageLanguageVersion);
+      file = FileState._(
+        _FileStateUnlinked(
+          location: location,
+          partOfLibrary: null,
+          performance: performance,
+        ),
+      );
       _pathToFile[path] = file;
       _uriToFile[uri] = file;
 
-      performance.run('refresh', (performance) {
-        file!.refresh(
-          performance: performance,
-        );
-      });
+      // Recurse with recording performance.
+      file.files(performance: performance);
     }
     return file;
   }
@@ -763,15 +386,21 @@
       var packageLanguageVersion =
           contextLanguageVersion(path, uri, workspacePackage);
 
-      file = FileState._(this, path, uri, source, workspacePackage, featureSet,
-          packageLanguageVersion);
+      var location = _FileStateLocation._(this, path, uri, source,
+          workspacePackage, featureSet, packageLanguageVersion);
+      file = FileState._(
+        _FileStateUnlinked(
+          location: location,
+          partOfLibrary: containingLibrary,
+          performance: performance,
+        ),
+      );
+
       _pathToFile[path] = file;
       _uriToFile[uri] = file;
 
-      file.refresh(
-        containingLibrary: containingLibrary,
-        performance: performance,
-      );
+      // Recurse with recording performance.
+      file.files(performance: performance);
     }
     return file;
   }
@@ -807,7 +436,7 @@
         }
       }
 
-      var topLevelDeclarations = file.unlinked.topLevelDeclarations;
+      var topLevelDeclarations = file._unlinked.unlinked.topLevelDeclarations;
       addDeclaration(
         topLevelDeclarations.extensionNames,
         FileTopLevelDeclarationKind.extension,
@@ -954,6 +583,16 @@
   }
 }
 
+class _ContentWithDigest {
+  final String content;
+  final Uint8List digest;
+
+  _ContentWithDigest({
+    required this.content,
+    required this.digest,
+  });
+}
+
 class _FakeSource implements Source {
   @override
   final String fullName;
@@ -967,6 +606,486 @@
   dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
 }
 
+class _FileStateFiles {
+  final List<FileState> imported = [];
+  final List<FileState> exported = [];
+  final List<FileState> parted = [];
+  final List<FileState> ofLibrary = [];
+
+  _FileStateFiles({
+    required FileState owner,
+    required OperationPerformanceImpl performance,
+  }) {
+    var unlinked = owner._unlinked;
+    var location = unlinked.location;
+    var unlinkedUnit = unlinked.unlinked.unit;
+
+    // Build the graph.
+    for (var directive in unlinkedUnit.imports) {
+      var file = location._fileForRelativeUri(
+        relativeUri: directive.uri,
+        performance: performance,
+      );
+      if (file != null) {
+        file.referencingFiles.add(owner);
+        imported.add(file);
+      }
+    }
+    for (var directive in unlinkedUnit.exports) {
+      var file = location._fileForRelativeUri(
+        relativeUri: directive.uri,
+        performance: performance,
+      );
+      if (file != null) {
+        exported.add(file);
+        file.referencingFiles.add(owner);
+      }
+    }
+    for (var uri in unlinkedUnit.parts) {
+      var file = location._fileForRelativeUri(
+        containingLibrary: owner,
+        relativeUri: uri,
+        performance: performance,
+      );
+      if (file != null) {
+        parted.add(file);
+        file.referencingFiles.add(owner);
+      }
+    }
+
+    ofLibrary.add(owner);
+    ofLibrary.addAll(parted);
+  }
+
+  /// Return all directly referenced files - imported, exported or parted.
+  Set<FileState> get directReferencedFiles {
+    return <FileState>{...imported, ...exported, ...parted};
+  }
+
+  /// Return all directly referenced libraries - imported or exported.
+  Set<FileState> get directReferencedLibraries {
+    return <FileState>{...imported, ...exported};
+  }
+}
+
+class _FileStateLocation {
+  final FileSystemState _fsState;
+
+  /// The path of the file.
+  final String path;
+
+  /// The URI of the file.
+  final Uri uri;
+
+  /// The [Source] of the file with the [uri].
+  final Source source;
+
+  /// The [WorkspacePackage] that contains this file.
+  ///
+  /// It might be `null` if the file is outside of the workspace.
+  final WorkspacePackage? workspacePackage;
+
+  /// The [FeatureSet] for all files in the analysis context.
+  ///
+  /// Usually it is the feature set of the latest language version, plus
+  /// possibly additional enabled experiments (from the analysis options file,
+  /// or from SDK allowed experiments).
+  ///
+  /// This feature set is then restricted, with the [_packageLanguageVersion],
+  /// or with a `@dart` language override token in the file header.
+  final FeatureSet _contextFeatureSet;
+
+  /// The language version for the package that contains this file.
+  final Version _packageLanguageVersion;
+
+  _FileStateLocation._(
+    this._fsState,
+    this.path,
+    this.uri,
+    this.source,
+    this.workspacePackage,
+    this._contextFeatureSet,
+    this._packageLanguageVersion,
+  );
+
+  File get resource {
+    return _fsState._resourceProvider.getFile(path);
+  }
+
+  FileState? _fileForRelativeUri({
+    FileState? containingLibrary,
+    required String relativeUri,
+    required OperationPerformanceImpl performance,
+  }) {
+    if (relativeUri.isEmpty) {
+      return null;
+    }
+
+    Uri absoluteUri;
+    try {
+      absoluteUri = resolveRelativeUri(uri, Uri.parse(relativeUri));
+    } on FormatException {
+      return null;
+    }
+
+    return _fsState.getFileForUri(
+      containingLibrary: containingLibrary,
+      uri: absoluteUri,
+      performance: performance,
+    );
+  }
+
+  /// This file has a `part of some.library;` directive. Because it does not
+  /// specify the URI of the library, we don't know the library for sure.
+  /// But usually the library is one of the sibling files.
+  FileState? _findPartOfNameLibrary({
+    required OperationPerformanceImpl performance,
+  }) {
+    var resourceProvider = _fsState._resourceProvider;
+    var pathContext = resourceProvider.pathContext;
+
+    var siblings = <Resource>[];
+    try {
+      siblings = resource.parent2.getChildren();
+    } catch (_) {}
+
+    for (var sibling in siblings) {
+      if (file_paths.isDart(pathContext, sibling.path)) {
+        var siblingState = _fsState.getFileForPath(
+          path: sibling.path,
+          performance: performance,
+        );
+        if (siblingState.files().parted.any((part) => part.path == path)) {
+          return siblingState;
+        }
+      }
+    }
+  }
+
+  _ContentWithDigest _getContent() {
+    String content;
+    try {
+      content = resource.readAsStringSync();
+    } catch (_) {
+      content = '';
+    }
+
+    var digestStr = _fsState.getFileDigest(path);
+    var digest = utf8.encode(digestStr) as Uint8List;
+
+    return _ContentWithDigest(content: content, digest: digest);
+  }
+}
+
+class _FileStateUnlinked {
+  final _FileStateLocation location;
+  FileState? _partOfLibrary;
+
+  final Uint8List digest;
+  final bool exists;
+  final CiderUnlinkedUnit unlinked;
+
+  /// id of the cache entry with unlinked data.
+  final int unlinkedId;
+
+  factory _FileStateUnlinked({
+    required _FileStateLocation location,
+    required FileState? partOfLibrary,
+    required OperationPerformanceImpl performance,
+  }) {
+    location._fsState.testView.refreshedFiles.add(location.path);
+
+    int unlinkedId;
+    CiderUnlinkedUnit unlinked;
+
+    var digest = performance.run('digest', (performance) {
+      performance.getDataInt('count').increment();
+      var digestStr = location._fsState.getFileDigest(location.path);
+      return utf8.encode(digestStr) as Uint8List;
+    });
+
+    var exists = digest.isNotEmpty;
+
+    var unlinkedKey = '${location.path}.unlinked';
+    var isUnlinkedFromCache = true;
+
+    // Prepare bytes of the unlinked bundle - existing or new.
+    // TODO(migration): should not be nullable
+    Uint8List? unlinkedBytes;
+    {
+      var unlinkedData = location._fsState._byteStore.get(unlinkedKey, digest);
+      unlinkedBytes = unlinkedData?.bytes;
+
+      if (unlinkedBytes == null || unlinkedBytes.isEmpty) {
+        isUnlinkedFromCache = false;
+
+        var contentWithDigest = performance.run('content', (_) {
+          return location._getContent();
+        });
+        digest = contentWithDigest.digest;
+        var content = contentWithDigest.content;
+
+        var unit = performance.run('parse', (performance) {
+          performance.getDataInt('count').increment();
+          performance.getDataInt('length').add(content.length);
+          return parse(AnalysisErrorListener.NULL_LISTENER, location, content);
+        });
+
+        performance.run('unlinked', (performance) {
+          var unlinkedUnit = serializeAstCiderUnlinked(unit);
+          unlinkedBytes = unlinkedUnit.toBytes();
+          performance.getDataInt('length').add(unlinkedBytes!.length);
+          unlinkedData = location._fsState._byteStore
+              .putGet(unlinkedKey, digest, unlinkedBytes!);
+          unlinkedBytes = unlinkedData!.bytes;
+        });
+
+        unlinked = CiderUnlinkedUnit.fromBytes(unlinkedBytes!);
+      }
+      unlinkedId = unlinkedData!.id;
+    }
+
+    // Read the unlinked bundle.
+    unlinked = CiderUnlinkedUnit.fromBytes(unlinkedBytes!);
+
+    var result = _FileStateUnlinked._(
+      location: location,
+      partOfLibrary: partOfLibrary,
+      digest: digest,
+      exists: exists,
+      unlinked: unlinked,
+      unlinkedId: unlinkedId,
+    );
+    if (isUnlinkedFromCache) {
+      performance.run('prefetch', (_) {
+        result._prefetchDirectReferences();
+      });
+    }
+    return result;
+  }
+
+  _FileStateUnlinked._({
+    required this.location,
+    required FileState? partOfLibrary,
+    required this.digest,
+    required this.exists,
+    required this.unlinked,
+    required this.unlinkedId,
+  }) : _partOfLibrary = partOfLibrary;
+
+  FileState? get partOfLibrary {
+    var partOfLibrary = _partOfLibrary;
+    if (partOfLibrary != null) {
+      return partOfLibrary;
+    }
+
+    var performance = OperationPerformanceImpl('<root>');
+
+    var libraryName = unlinked.unit.partOfName;
+    if (libraryName != null) {
+      location._fsState.testView.partsDiscoveredLibraries.add(location.path);
+      return _partOfLibrary = location._findPartOfNameLibrary(
+        performance: performance,
+      );
+    }
+
+    var libraryUri = unlinked.unit.partOfUri;
+    if (libraryUri != null) {
+      location._fsState.testView.partsDiscoveredLibraries.add(location.path);
+      return _partOfLibrary = location._fileForRelativeUri(
+        relativeUri: libraryUri,
+        performance: performance,
+      );
+    }
+  }
+
+  void _prefetchDirectReferences() {
+    if (location._fsState.prefetchFiles == null) {
+      return;
+    }
+
+    var paths = <String>{};
+
+    /// TODO(scheglov) This is duplicate.
+    void findPathForUri(String relativeUri) {
+      if (relativeUri.isEmpty) {
+        return;
+      }
+      Uri absoluteUri;
+      try {
+        absoluteUri = resolveRelativeUri(location.uri, Uri.parse(relativeUri));
+      } on FormatException {
+        return;
+      }
+      var p = location._fsState.getPathForUri(absoluteUri);
+      if (p != null) {
+        paths.add(p);
+      }
+    }
+
+    var unlinkedUnit = unlinked.unit;
+    for (var directive in unlinkedUnit.imports) {
+      findPathForUri(directive.uri);
+    }
+    for (var directive in unlinkedUnit.exports) {
+      findPathForUri(directive.uri);
+    }
+    for (var uri in unlinkedUnit.parts) {
+      findPathForUri(uri);
+    }
+
+    location._fsState.prefetchFiles!(paths.toList());
+  }
+
+  static CompilationUnitImpl parse(AnalysisErrorListener errorListener,
+      _FileStateLocation location, String content) {
+    CharSequenceReader reader = CharSequenceReader(content);
+    Scanner scanner = Scanner(location.source, reader, errorListener)
+      ..configureFeatures(
+        featureSetForOverriding: location._contextFeatureSet,
+        featureSet: location._contextFeatureSet.restrictToVersion(
+          location._packageLanguageVersion,
+        ),
+      );
+    Token token = scanner.tokenize(reportScannerErrors: false);
+    LineInfo lineInfo = LineInfo(scanner.lineStarts);
+
+    // Pass the feature set from the scanner to the parser
+    // because the scanner may have detected a language version comment
+    // and downgraded the feature set it holds.
+    Parser parser = Parser(
+      location.source,
+      errorListener,
+      featureSet: scanner.featureSet,
+    );
+    parser.enableOptionalNewAndConst = true;
+    var unit = parser.parseCompilationUnit(token);
+    unit.lineInfo = lineInfo;
+
+    // StringToken uses a static instance of StringCanonicalizer, so we need
+    // to clear it explicitly once we are done using it for this file.
+    StringToken.canonicalizer.clear();
+
+    // TODO(scheglov) Use actual versions.
+    unit.languageVersion = LibraryLanguageVersion(
+      package: ExperimentStatus.currentVersion,
+      override: null,
+    );
+
+    return unit;
+  }
+
+  static CiderUnlinkedUnit serializeAstCiderUnlinked(CompilationUnit unit) {
+    var exports = <UnlinkedNamespaceDirective>[];
+    var imports = <UnlinkedNamespaceDirective>[];
+    var parts = <String>[];
+    var hasDartCoreImport = false;
+    var hasLibraryDirective = false;
+    var hasPartOfDirective = false;
+    String? partOfName;
+    String? partOfUriStr;
+    for (var directive in unit.directives) {
+      if (directive is ExportDirective) {
+        var builder = _serializeNamespaceDirective(directive);
+        exports.add(builder);
+      } else if (directive is ImportDirective) {
+        var builder = _serializeNamespaceDirective(directive);
+        imports.add(builder);
+        if (builder.uri == 'dart:core') {
+          hasDartCoreImport = true;
+        }
+      } else if (directive is LibraryDirective) {
+        hasLibraryDirective = true;
+      } else if (directive is PartDirective) {
+        var uriStr = directive.uri.stringValue;
+        parts.add(uriStr ?? '');
+      } else if (directive is PartOfDirective) {
+        hasPartOfDirective = true;
+        var libraryName = directive.libraryName;
+        var uriStr = directive.uri?.stringValue;
+        if (libraryName != null) {
+          partOfName = libraryName.components.map((e) => e.name).join('.');
+        } else if (uriStr != null) {
+          partOfUriStr = uriStr;
+        }
+      }
+    }
+    if (!hasDartCoreImport) {
+      imports.add(
+        UnlinkedNamespaceDirective(
+          configurations: [],
+          uri: 'dart:core',
+        ),
+      );
+    }
+
+    var declaredExtensions = <String>[];
+    var declaredFunctions = <String>[];
+    var declaredTypes = <String>[];
+    var declaredVariables = <String>[];
+    for (var declaration in unit.declarations) {
+      if (declaration is ClassDeclaration) {
+        declaredTypes.add(declaration.name.name);
+      } else if (declaration is EnumDeclaration) {
+        declaredTypes.add(declaration.name.name);
+      } else if (declaration is ExtensionDeclaration) {
+        var name = declaration.name;
+        if (name != null) {
+          declaredExtensions.add(name.name);
+        }
+      } else if (declaration is FunctionDeclaration) {
+        declaredFunctions.add(declaration.name.name);
+      } else if (declaration is MixinDeclaration) {
+        declaredTypes.add(declaration.name.name);
+      } else if (declaration is TopLevelVariableDeclaration) {
+        for (var variable in declaration.variables.variables) {
+          declaredVariables.add(variable.name.name);
+        }
+      }
+    }
+
+    var unlinkedUnit = UnlinkedUnit(
+      apiSignature: computeUnlinkedApiSignature(unit),
+      exports: exports,
+      hasLibraryDirective: hasLibraryDirective,
+      hasPartOfDirective: hasPartOfDirective,
+      imports: imports,
+      informativeBytes: writeUnitInformative(unit),
+      lineStarts: Uint32List.fromList(unit.lineInfo!.lineStarts),
+      partOfName: partOfName,
+      partOfUri: partOfUriStr,
+      parts: parts,
+    );
+
+    return CiderUnlinkedUnit(
+      unit: unlinkedUnit,
+      topLevelDeclarations: CiderUnitTopLevelDeclarations(
+        extensionNames: declaredExtensions,
+        functionNames: declaredFunctions,
+        typeNames: declaredTypes,
+        variableNames: declaredVariables,
+      ),
+    );
+  }
+
+  static UnlinkedNamespaceDirective _serializeNamespaceDirective(
+    NamespaceDirective directive,
+  ) {
+    return UnlinkedNamespaceDirective(
+      configurations: directive.configurations.map((configuration) {
+        var name = configuration.name.components.join('.');
+        var value = configuration.value?.stringValue ?? '';
+        return UnlinkedNamespaceDirectiveConfiguration(
+          name: name,
+          value: value,
+          uri: configuration.uri.stringValue ?? '',
+        );
+      }).toList(),
+      uri: directive.uri.stringValue ?? '',
+    );
+  }
+}
+
 /// Node in [_LibraryWalker].
 class _LibraryNode extends graph.Node<_LibraryNode> {
   final _LibraryWalker walker;
@@ -979,7 +1098,7 @@
 
   @override
   List<_LibraryNode> computeDependencies() {
-    return file.directReferencedLibraries.map(walker.getNode).toList();
+    return file.files().directReferencedLibraries.map(walker.getNode).toList();
   }
 }
 
@@ -1013,8 +1132,8 @@
     // Append direct referenced cycles.
     for (var node in scc) {
       var file = node.file;
-      _appendDirectlyReferenced(cycle, signature, file.importedFiles);
-      _appendDirectlyReferenced(cycle, signature, file.exportedFiles);
+      _appendDirectlyReferenced(cycle, signature, file.files().imported);
+      _appendDirectlyReferenced(cycle, signature, file.files().exported);
     }
 
     // Fill the cycle with libraries.
@@ -1023,8 +1142,8 @@
 
       signature.addString(node.file.uriStr);
 
-      signature.addInt(node.file.libraryFiles.length);
-      for (var file in node.file.libraryFiles) {
+      signature.addInt(node.file.files().ofLibrary.length);
+      for (var file in node.file.files().ofLibrary) {
         signature.addBool(file.exists);
         signature.addBytes(file.apiSignature);
       }
diff --git a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
index 7ff94e7..89c1fe0 100644
--- a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
+++ b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
@@ -430,7 +430,7 @@
       var libraryFile = file;
       var partOfLibrary = file.partOfLibrary;
       if (partOfLibrary != null) {
-        if (partOfLibrary.libraryFiles.contains(file)) {
+        if (partOfLibrary.files().ofLibrary.contains(file)) {
           libraryFile = partOfLibrary;
         }
       }
@@ -477,7 +477,7 @@
       var libraryFile = file;
       var partOfLibrary = file.partOfLibrary;
       if (partOfLibrary != null) {
-        if (partOfLibrary.libraryFiles.contains(file)) {
+        if (partOfLibrary.files().ofLibrary.contains(file)) {
           libraryFile = partOfLibrary;
         }
       }
@@ -509,7 +509,7 @@
           libraryContext!.elementFactory,
           contextObjects!.inheritanceManager,
           libraryFile,
-          (file) => file.getContentWithSameDigest(),
+          (file) => file.getContent(),
         );
 
         try {
@@ -522,7 +522,7 @@
           });
         } catch (exception, stackTrace) {
           var fileContentMap = <String, String>{};
-          for (var file in libraryFile.libraryFiles) {
+          for (var file in libraryFile.files().ofLibrary) {
             var path = file.path;
             fileContentMap[path] = _getFileContent(path);
           }
@@ -543,7 +543,7 @@
           file.exists,
           file.getContent(),
           file.lineInfo,
-          file.unlinked.unit.hasPartOfDirective,
+          file.unlinkedUnit.hasPartOfDirective,
           fileResult.unit,
           fileResult.errors,
         );
@@ -822,8 +822,8 @@
 
       var unitsInformativeBytes = <Uri, Uint8List>{};
       for (var library in cycle.libraries) {
-        for (var file in library.libraryFiles) {
-          var informativeBytes = file.unlinked.unit.informativeBytes;
+        for (var file in library.files().ofLibrary) {
+          var informativeBytes = file.unlinkedUnit.informativeBytes;
           unitsInformativeBytes[file.uri] = informativeBytes;
         }
       }
@@ -838,10 +838,10 @@
 
           var inputUnits = <link2.LinkInputUnit>[];
           var partIndex = -1;
-          for (var file in libraryFile.libraryFiles) {
+          for (var file in libraryFile.files().ofLibrary) {
             var isSynthetic = !file.exists;
 
-            var content = file.getContentWithSameDigest();
+            var content = file.getContent();
             performance.getDataInt('parseCount').increment();
             performance.getDataInt('parseLength').add(content.length);
 
@@ -852,7 +852,7 @@
 
             String? partUriStr;
             if (partIndex >= 0) {
-              partUriStr = libraryFile.unlinked.unit.parts[partIndex];
+              partUriStr = libraryFile.unlinkedUnit.parts[partIndex];
             }
             partIndex++;
 
diff --git a/pkg/analyzer/lib/src/lint/analysis.dart b/pkg/analyzer/lib/src/lint/analysis.dart
index b43bd0c..2e6974e 100644
--- a/pkg/analyzer/lib/src/lint/analysis.dart
+++ b/pkg/analyzer/lib/src/lint/analysis.dart
@@ -75,6 +75,7 @@
   bool strongMode = true;
 
   /// The mock SDK (to speed up testing) or `null` to use the actual SDK.
+  @Deprecated('Use createMockSdk() and set dartSdkPath')
   DartSdk? mockSdk;
 
   /// Return `true` is the parser is able to parse asserts in the initializer
diff --git a/pkg/analyzer/test/src/dart/micro/file_resolution.dart b/pkg/analyzer/test/src/dart/micro/file_resolution.dart
index 5836465..52969ec 100644
--- a/pkg/analyzer/test/src/dart/micro/file_resolution.dart
+++ b/pkg/analyzer/test/src/dart/micro/file_resolution.dart
@@ -14,6 +14,7 @@
 import 'package:analyzer/src/test_utilities/find_node.dart';
 import 'package:analyzer/src/test_utilities/mock_sdk.dart';
 import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
+import 'package:analyzer/src/util/performance/operation_performance.dart';
 import 'package:analyzer/src/workspace/bazel.dart';
 import 'package:crypto/crypto.dart';
 import 'package:linter/src/rules.dart';
@@ -71,8 +72,14 @@
   }
 
   @override
-  Future<ResolvedUnitResult> resolveFile(String path) async {
-    result = fileResolver.resolve(path: path);
+  Future<ResolvedUnitResult> resolveFile(
+    String path, {
+    OperationPerformanceImpl? performance,
+  }) async {
+    result = fileResolver.resolve(
+      path: path,
+      performance: performance,
+    );
     return result;
   }
 
diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h
index abc0291..b62c1a7 100644
--- a/runtime/include/dart_api.h
+++ b/runtime/include/dart_api.h
@@ -608,6 +608,7 @@
   bool copy_parent_code;
   bool null_safety;
   bool is_system_isolate;
+  bool snapshot_is_dontneed_safe;
 } Dart_IsolateFlags;
 
 /**
diff --git a/runtime/vm/app_snapshot.cc b/runtime/vm/app_snapshot.cc
index 8f2e5ab..11fe806 100644
--- a/runtime/vm/app_snapshot.cc
+++ b/runtime/vm/app_snapshot.cc
@@ -7833,6 +7833,8 @@
 };
 
 void Deserializer::Deserialize(DeserializationRoots* roots) {
+  const void* clustered_start = CurrentBufferAddress();
+
   Array& refs = Array::Handle(zone_);
   num_base_objects_ = ReadUnsigned();
   num_objects_ = ReadUnsigned();
@@ -7926,8 +7928,8 @@
 
   roots->PostLoad(this, refs);
 
-#if defined(DEBUG)
   auto isolate_group = thread()->isolate_group();
+#if defined(DEBUG)
   isolate_group->ValidateClassTable();
   if (isolate_group != Dart::vm_isolate()->group()) {
     isolate_group->heap()->Verify();
@@ -7941,6 +7943,13 @@
       clusters_[i]->PostLoad(this, refs, primary);
     }
   }
+
+  if (isolate_group->snapshot_is_dontneed_safe()) {
+    size_t clustered_length = reinterpret_cast<uword>(CurrentBufferAddress()) -
+                              reinterpret_cast<uword>(clustered_start);
+    VirtualMemory::DontNeed(const_cast<void*>(clustered_start),
+                            clustered_length);
+  }
 }
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index cb402bd..b1e4939 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -161,7 +161,9 @@
     FLAG_use_field_guards)                                                     \
   V(PRODUCT, should_load_vmservice_library, ShouldLoadVmService,               \
     load_vmservice_library, false)                                             \
-  V(NONPRODUCT, use_osr, UseOsr, use_osr, FLAG_use_osr)
+  V(NONPRODUCT, use_osr, UseOsr, use_osr, FLAG_use_osr)                        \
+  V(NONPRODUCT, snapshot_is_dontneed_safe, SnapshotIsDontNeedSafe,             \
+    snapshot_is_dontneed_safe, false)
 
 #define BOOL_ISOLATE_FLAG_LIST_DEFAULT_GETTER(V)                               \
   V(PRODUCT, copy_parent_code, CopyParentCode, copy_parent_code, false)        \
@@ -786,7 +788,8 @@
   V(NullSafetySet)                                                             \
   V(Obfuscate)                                                                 \
   V(UseFieldGuards)                                                            \
-  V(UseOsr)
+  V(UseOsr)                                                                    \
+  V(SnapshotIsDontNeedSafe)
 
   // Isolate group specific flags.
   enum FlagBits {
diff --git a/runtime/vm/virtual_memory.h b/runtime/vm/virtual_memory.h
index 883bc74..d25093c 100644
--- a/runtime/vm/virtual_memory.h
+++ b/runtime/vm/virtual_memory.h
@@ -46,6 +46,8 @@
   static void Protect(void* address, intptr_t size, Protection mode);
   void Protect(Protection mode) { return Protect(address(), size(), mode); }
 
+  static void DontNeed(void* address, intptr_t size);
+
   // Reserves and commits a virtual memory segment with size. If a segment of
   // the requested size cannot be allocated, NULL is returned.
   static VirtualMemory* Allocate(intptr_t size,
diff --git a/runtime/vm/virtual_memory_fuchsia.cc b/runtime/vm/virtual_memory_fuchsia.cc
index ee26123..149f379 100644
--- a/runtime/vm/virtual_memory_fuchsia.cc
+++ b/runtime/vm/virtual_memory_fuchsia.cc
@@ -299,6 +299,21 @@
   }
 }
 
+void VirtualMemory::DontNeed(void* address, intptr_t size) {
+  uword start_address = reinterpret_cast<uword>(address);
+  uword end_address = start_address + size;
+  uword page_address = Utils::RoundDown(start_address, PageSize());
+  zx_status_t status = zx_vmar_op_range(
+      getVmarForAddress(reinterpret_cast<uword>(address)), ZX_VMAR_OP_DONT_NEED,
+      page_address, end_address - page_address, nullptr, 0);
+  LOG_INFO("zx_vmar_op_range(DONTNEED, 0x%lx, 0x%lx)\n", page_address,
+           end_address - page_address);
+  if (status != ZX_OK) {
+    FATAL("zx_vmar_op_range(DONTNEED, 0x%lx, 0x%lx) failed: %s\n", page_address,
+          end_address - page_address, zx_status_get_string(status));
+  }
+}
+
 }  // namespace dart
 
 #endif  // defined(DART_HOST_OS_FUCHSIA)
diff --git a/runtime/vm/virtual_memory_posix.cc b/runtime/vm/virtual_memory_posix.cc
index 3191878..b2717df 100644
--- a/runtime/vm/virtual_memory_posix.cc
+++ b/runtime/vm/virtual_memory_posix.cc
@@ -560,6 +560,20 @@
            end_address - page_address, prot);
 }
 
+void VirtualMemory::DontNeed(void* address, intptr_t size) {
+  uword start_address = reinterpret_cast<uword>(address);
+  uword end_address = start_address + size;
+  uword page_address = Utils::RoundDown(start_address, PageSize());
+  if (madvise(reinterpret_cast<void*>(page_address), end_address - page_address,
+              MADV_DONTNEED) != 0) {
+    int error = errno;
+    const int kBufferSize = 1024;
+    char error_buf[kBufferSize];
+    FATAL("madvise error: %d (%s)", error,
+          Utils::StrError(error, error_buf, kBufferSize));
+  }
+}
+
 }  // namespace dart
 
 #endif  // defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) ||     \
diff --git a/runtime/vm/virtual_memory_win.cc b/runtime/vm/virtual_memory_win.cc
index 013adf0..816ef0f 100644
--- a/runtime/vm/virtual_memory_win.cc
+++ b/runtime/vm/virtual_memory_win.cc
@@ -241,6 +241,8 @@
   }
 }
 
+void VirtualMemory::DontNeed(void* address, intptr_t size) {}
+
 }  // namespace dart
 
 #endif  // defined(DART_HOST_OS_WINDOWS)
diff --git a/sdk/lib/_http/crypto.dart b/sdk/lib/_http/crypto.dart
index 337f299..39ca5a8 100644
--- a/sdk/lib/_http/crypto.dart
+++ b/sdk/lib/_http/crypto.dart
@@ -5,41 +5,6 @@
 part of dart._http;
 
 class _CryptoUtils {
-  static const int PAD = 61; // '='
-  static const int CR = 13; // '\r'
-  static const int LF = 10; // '\n'
-  static const int LINE_LENGTH = 76;
-
-  static const String _encodeTable =
-      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-  static const String _encodeTableUrlSafe =
-      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
-
-  // Lookup table used for finding Base 64 alphabet index of a given byte.
-  // -2 : Outside Base 64 alphabet.
-  // -1 : '\r' or '\n'
-  //  0 : = (Padding character).
-  // >0 : Base 64 alphabet index of given byte.
-  static const List<int> _decodeTable = [
-    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -2, -2, -1, -2, -2, //
-    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, //
-    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, 62, -2, 63, //
-    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, 00, -2, -2, //
-    -2, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, //
-    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, 63, //
-    -2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, //
-    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2, //
-    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, //
-    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, //
-    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, //
-    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, //
-    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, //
-    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, //
-    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, //
-    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2
-  ];
-
   static Uint8List getRandomBytes(int count) {
     final Uint8List result = Uint8List(count);
     for (int i = 0; i < count; i++) {
@@ -55,116 +20,6 @@
     }
     return result.toString();
   }
-
-  static String bytesToBase64(List<int> bytes,
-      [bool urlSafe = false, bool addLineSeparator = false]) {
-    int len = bytes.length;
-    if (len == 0) {
-      return "";
-    }
-    final String lookup = urlSafe ? _encodeTableUrlSafe : _encodeTable;
-    // Size of 24 bit chunks.
-    final int remainderLength = len.remainder(3);
-    final int chunkLength = len - remainderLength;
-    // Size of base output.
-    int outputLen = ((len ~/ 3) * 4) + ((remainderLength > 0) ? 4 : 0);
-    // Add extra for line separators.
-    if (addLineSeparator) {
-      outputLen += ((outputLen - 1) ~/ LINE_LENGTH) << 1;
-    }
-    List<int> out = List<int>.filled(outputLen, 0);
-
-    // Encode 24 bit chunks.
-    int j = 0, i = 0, c = 0;
-    while (i < chunkLength) {
-      int x = ((bytes[i++] << 16) & 0xFFFFFF) |
-          ((bytes[i++] << 8) & 0xFFFFFF) |
-          bytes[i++];
-      out[j++] = lookup.codeUnitAt(x >> 18);
-      out[j++] = lookup.codeUnitAt((x >> 12) & 0x3F);
-      out[j++] = lookup.codeUnitAt((x >> 6) & 0x3F);
-      out[j++] = lookup.codeUnitAt(x & 0x3f);
-      // Add optional line separator for each 76 char output.
-      if (addLineSeparator && ++c == 19 && j < outputLen - 2) {
-        out[j++] = CR;
-        out[j++] = LF;
-        c = 0;
-      }
-    }
-
-    // If input length if not a multiple of 3, encode remaining bytes and
-    // add padding.
-    if (remainderLength == 1) {
-      int x = bytes[i];
-      out[j++] = lookup.codeUnitAt(x >> 2);
-      out[j++] = lookup.codeUnitAt((x << 4) & 0x3F);
-      out[j++] = PAD;
-      out[j++] = PAD;
-    } else if (remainderLength == 2) {
-      int x = bytes[i];
-      int y = bytes[i + 1];
-      out[j++] = lookup.codeUnitAt(x >> 2);
-      out[j++] = lookup.codeUnitAt(((x << 4) | (y >> 4)) & 0x3F);
-      out[j++] = lookup.codeUnitAt((y << 2) & 0x3F);
-      out[j++] = PAD;
-    }
-
-    return String.fromCharCodes(out);
-  }
-
-  static List<int> base64StringToBytes(String input,
-      [bool ignoreInvalidCharacters = true]) {
-    int len = input.length;
-    if (len == 0) {
-      return List<int>.empty();
-    }
-
-    // Count '\r', '\n' and illegal characters, For illegal characters,
-    // if [ignoreInvalidCharacters] is false, throw an exception.
-    int extrasLen = 0;
-    for (int i = 0; i < len; i++) {
-      int c = _decodeTable[input.codeUnitAt(i)];
-      if (c < 0) {
-        extrasLen++;
-        if (c == -2 && !ignoreInvalidCharacters) {
-          throw FormatException('Invalid character: ${input[i]}');
-        }
-      }
-    }
-
-    if ((len - extrasLen) % 4 != 0) {
-      throw FormatException('''Size of Base 64 characters in Input
-          must be a multiple of 4. Input: $input''');
-    }
-
-    // Count pad characters, ignore illegal characters at the end.
-    int padLength = 0;
-    for (int i = len - 1; i >= 0; i--) {
-      int currentCodeUnit = input.codeUnitAt(i);
-      if (_decodeTable[currentCodeUnit] > 0) break;
-      if (currentCodeUnit == PAD) padLength++;
-    }
-    int outputLen = (((len - extrasLen) * 6) >> 3) - padLength;
-    List<int> out = List<int>.filled(outputLen, 0);
-
-    for (int i = 0, o = 0; o < outputLen;) {
-      // Accumulate 4 valid 6 bit Base 64 characters into an int.
-      int x = 0;
-      for (int j = 4; j > 0;) {
-        int c = _decodeTable[input.codeUnitAt(i++)];
-        if (c >= 0) {
-          x = ((x << 6) & 0xFFFFFF) | c;
-          j--;
-        }
-      }
-      out[o++] = x >> 16;
-      if (o < outputLen) {
-        out[o++] = (x >> 8) & 0xFF;
-        if (o < outputLen) out[o++] = x & 0xFF;
-      }
-    }
-    return out;
-  }
 }
 
 // Constants.
@@ -181,17 +36,17 @@
   final bool _bigEndianWords;
   int _lengthInBytes = 0;
   List<int> _pendingData;
-  final List<int> _currentChunk;
-  final List<int> _h;
+  final Uint32List _currentChunk;
+  final Uint32List _h;
   bool _digestCalled = false;
 
   _HashBase(this._chunkSizeInWords, int digestSizeInWords, this._bigEndianWords)
       : _pendingData = [],
-        _currentChunk = List.filled(_chunkSizeInWords, 0),
-        _h = List.filled(digestSizeInWords, 0);
+        _currentChunk = Uint32List(_chunkSizeInWords),
+        _h = Uint32List(digestSizeInWords);
 
   // Update the hasher with more data.
-  add(List<int> data) {
+  void add(List<int> data) {
     if (_digestCalled) {
       throw StateError('Hash update method called after digest was retrieved');
     }
@@ -217,11 +72,8 @@
     return _chunkSizeInWords * _BYTES_PER_WORD;
   }
 
-  // Create a fresh instance of this Hash.
-  newInstance();
-
   // One round of the hash computation.
-  _updateHash(List<int> m);
+  _updateHash(Uint32List m);
 
   // Helper methods.
   int _add32(int x, int y) => (x + y) & _MASK_32;
@@ -244,7 +96,7 @@
   }
 
   // Converts a list of bytes to a chunk of 32-bit words.
-  _bytesToChunk(List<int> data, int dataIndex) {
+  void _bytesToChunk(List<int> data, int dataIndex) {
     assert((data.length - dataIndex) >= (_chunkSizeInWords * _BYTES_PER_WORD));
 
     for (var wordIndex = 0; wordIndex < _chunkSizeInWords; wordIndex++) {
@@ -273,7 +125,7 @@
 
   // Iterate through data updating the hash computation for each
   // chunk.
-  _iterate() {
+  void _iterate() {
     var len = _pendingData.length;
     var chunkSizeInBytes = _chunkSizeInWords * _BYTES_PER_WORD;
     if (len >= chunkSizeInBytes) {
@@ -288,7 +140,7 @@
 
   // Finalize the data. Add a 1 bit to the end of the message. Expand with
   // 0 bits and add the length of the message.
-  _finalizeData() {
+  void _finalizeData() {
     _pendingData.add(0x80);
     var contentsLength = _lengthInBytes + 9;
     var chunkSizeInBytes = _chunkSizeInWords * _BYTES_PER_WORD;
@@ -318,11 +170,6 @@
     _h[3] = 0x10325476;
   }
 
-  // Returns a new instance of this Hash.
-  _MD5 newInstance() {
-    return _MD5();
-  }
-
   static const _k = [
     0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, //
     0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, //
@@ -346,7 +193,7 @@
 
   // Compute one iteration of the MD5 algorithm with a chunk of
   // 16 32-bit pieces.
-  void _updateHash(List<int> m) {
+  void _updateHash(Uint32List m) {
     assert(m.length == 16);
 
     var a = _h[0];
@@ -402,14 +249,9 @@
     _h[4] = 0xC3D2E1F0;
   }
 
-  // Returns a new instance of this Hash.
-  _SHA1 newInstance() {
-    return _SHA1();
-  }
-
   // Compute one iteration of the SHA1 algorithm with a chunk of
   // 16 32-bit pieces.
-  void _updateHash(List<int> m) {
+  void _updateHash(Uint32List m) {
     assert(m.length == 16);
 
     var a = _h[0];
diff --git a/sdk/lib/_http/http_impl.dart b/sdk/lib/_http/http_impl.dart
index 9ff1eef..6c9c786 100644
--- a/sdk/lib/_http/http_impl.dart
+++ b/sdk/lib/_http/http_impl.dart
@@ -2115,8 +2115,8 @@
     if (proxy.isAuthenticated) {
       // If the proxy configuration contains user information use that
       // for proxy basic authorization.
-      String auth = _CryptoUtils.bytesToBase64(
-          utf8.encode("${proxy.username}:${proxy.password}"));
+      String auth =
+          base64Encode(utf8.encode("${proxy.username}:${proxy.password}"));
       request.headers.set(HttpHeaders.proxyAuthorizationHeader, "Basic $auth");
     } else if (!proxy.isDirect && _httpClient._proxyCredentials.isNotEmpty) {
       proxyCreds = _httpClient._findProxyCredentials(proxy);
@@ -2127,7 +2127,7 @@
     if (uri.userInfo != null && uri.userInfo.isNotEmpty) {
       // If the URL contains user information use that for basic
       // authorization.
-      String auth = _CryptoUtils.bytesToBase64(utf8.encode(uri.userInfo));
+      String auth = base64Encode(utf8.encode(uri.userInfo));
       request.headers.set(HttpHeaders.authorizationHeader, "Basic $auth");
     } else {
       // Look for credentials.
@@ -2278,8 +2278,8 @@
     if (proxy.isAuthenticated) {
       // If the proxy configuration contains user information use that
       // for proxy basic authorization.
-      String auth = _CryptoUtils.bytesToBase64(
-          utf8.encode("${proxy.username}:${proxy.password}"));
+      String auth =
+          base64Encode(utf8.encode("${proxy.username}:${proxy.password}"));
       request.headers.set(HttpHeaders.proxyAuthorizationHeader, "Basic $auth");
     }
     return request.close().then((response) {
@@ -3640,8 +3640,7 @@
     // Proxy-Authenticate headers, see
     // http://tools.ietf.org/html/draft-reschke-basicauth-enc-06. For
     // now always use UTF-8 encoding.
-    String auth =
-        _CryptoUtils.bytesToBase64(utf8.encode("$username:$password"));
+    String auth = base64Encode(utf8.encode("$username:$password"));
     return "Basic $auth";
   }
 
diff --git a/sdk/lib/_http/websocket_impl.dart b/sdk/lib/_http/websocket_impl.dart
index e43c3e5..c7126c2 100644
--- a/sdk/lib/_http/websocket_impl.dart
+++ b/sdk/lib/_http/websocket_impl.dart
@@ -463,7 +463,7 @@
       String key = request.headers.value("Sec-WebSocket-Key")!;
       _SHA1 sha1 = _SHA1();
       sha1.add("$key$_webSocketGUID".codeUnits);
-      String accept = _CryptoUtils.bytesToBase64(sha1.close());
+      String accept = base64Encode(sha1.close());
       response.headers.add("Sec-WebSocket-Accept", accept);
       if (protocol != null) {
         response.headers.add("Sec-WebSocket-Protocol", protocol);
@@ -1006,7 +1006,7 @@
     for (int i = 0; i < 16; i++) {
       nonceData[i] = random.nextInt(256);
     }
-    String nonce = _CryptoUtils.bytesToBase64(nonceData);
+    String nonce = base64Encode(nonceData);
 
     final callerStackTrace = StackTrace.current;
 
@@ -1022,7 +1022,7 @@
       if (uri.userInfo != null && uri.userInfo.isNotEmpty) {
         // If the URL contains user information use that for basic
         // authorization.
-        String auth = _CryptoUtils.bytesToBase64(utf8.encode(uri.userInfo));
+        String auth = base64Encode(utf8.encode(uri.userInfo));
         request.headers.set(HttpHeaders.authorizationHeader, "Basic $auth");
       }
       if (headers != null) {
@@ -1071,7 +1071,7 @@
       _SHA1 sha1 = _SHA1();
       sha1.add("$nonce$_webSocketGUID".codeUnits);
       List<int> expectedAccept = sha1.close();
-      List<int> receivedAccept = _CryptoUtils.base64StringToBytes(accept);
+      List<int> receivedAccept = base64Decode(accept);
       if (expectedAccept.length != receivedAccept.length) {
         return error(
             "Response header 'Sec-WebSocket-Accept' is the wrong length");
diff --git a/tests/standalone/io/web_socket_error_test.dart b/tests/standalone/io/web_socket_error_test.dart
index aca2548..a37afdb 100644
--- a/tests/standalone/io/web_socket_error_test.dart
+++ b/tests/standalone/io/web_socket_error_test.dart
@@ -12,6 +12,7 @@
 library dart._http;
 
 import "dart:async";
+import "dart:convert";
 import "dart:io";
 import "dart:math";
 import "dart:typed_data";
@@ -61,7 +62,7 @@
         String? key = request.headers.value("Sec-WebSocket-Key");
         _SHA1 sha1 = new _SHA1();
         sha1.add("$key$webSocketGUID".codeUnits);
-        String accept = _CryptoUtils.bytesToBase64(sha1.close());
+        String accept = base64Encode(sha1.close());
         response.headers.add("Sec-WebSocket-Accept", accept);
         response.headers.contentLength = 0;
         response.detachSocket().then((socket) {
diff --git a/tests/standalone/io/web_socket_ping_test.dart b/tests/standalone/io/web_socket_ping_test.dart
index 672e48f..fc1b0c1 100644
--- a/tests/standalone/io/web_socket_ping_test.dart
+++ b/tests/standalone/io/web_socket_ping_test.dart
@@ -9,7 +9,7 @@
 
 library dart._http;
 
-import "dart:async";
+import "dart:convert";
 import "dart:io";
 import "dart:math";
 import "dart:typed_data";
@@ -31,7 +31,7 @@
       String? key = request.headers.value("Sec-WebSocket-Key");
       _SHA1 sha1 = new _SHA1();
       sha1.add("$key$webSocketGUID".codeUnits);
-      String accept = _CryptoUtils.bytesToBase64(sha1.close());
+      String accept = base64Encode(sha1.close());
       response.headers.add("Sec-WebSocket-Accept", accept);
       response.headers.contentLength = 0;
       response.detachSocket().then((socket) {
diff --git a/tools/VERSION b/tools/VERSION
index 9540a64..bd85cf6 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 250
+PRERELEASE 251
 PRERELEASE_PATCH 0
\ No newline at end of file