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