Version 2.18.0-199.0.dev
Merge commit '2885ecc42664e4972343addeb73beb892f4737ea' into 'dev'
diff --git a/DEPS b/DEPS
index c048fa6..89a6b62 100644
--- a/DEPS
+++ b/DEPS
@@ -75,23 +75,23 @@
"gperftools_revision": "180bfa10d7cb38e8b3784d60943d50e8fcef0dcb",
# Revisions of /third_party/* dependencies.
- "args_rev": "862d929b980b993334974d38485a39d891d83918",
+ "args_rev": "73e8d3b55cbedc9765f8e266f3422d8914f8e62a",
"async_rev": "f3ed5f690e2ec9dbe1bfc5184705575b4f6480e5",
"bazel_worker_rev": "9710de6c9c70b1b583183db9d9721ba64e5a16fe",
- "benchmark_harness_rev": "0530da692a5d689f4b5450a7c8d1a8abe3e2d555",
+ "benchmark_harness_rev": "0ae822e264e410e9f1e927daea68601cc54906ef",
"boolean_selector_rev": "1d3565e2651d16566bb556955b96ea75018cbd0c",
"boringssl_gen_rev": "ced85ef0a00bbca77ce5a91261a5f2ae61b1e62f",
"boringssl_rev": "87f316d7748268eb56f2dc147bd593254ae93198",
"browser-compat-data_tag": "ac8cae697014da1ff7124fba33b0b4245cc6cd1b", # v1.0.22
- "browser_launcher_rev": "f841375ad337381e23d333b6eaaebde3d8266c68",
- "characters_rev": "4b1d4b7737ad47cd2b8105c47e2159174010f29f",
+ "browser_launcher_rev": "981ca8847dd2b0fe022f9e742045cfb8f214d35f",
+ "characters_rev": "559755d67af2c78b9beaaeb7ca57d7c4ae0b836d",
"chrome_rev": "19997",
"cli_util_rev": "b0adbba89442b2ea6fef39c7a82fe79cb31e1168",
- "clock_rev": "f594d86da123015186d5680b0d0e8255c52fc162",
- "collection_rev": "f9b433dfc7dba5c8a987b1a8e6a9050292f5582e",
- "convert_rev": "00b251529c074df394b3391c7e3eea3dd9e5778e",
- "crypto_rev": "4297d240b0e1e780ec0a9eab23eaf1ad491f3e68",
- "csslib_rev": "518761b166974537f334dbf264e7f56cb157a96a",
+ "clock_rev": "a75eb69c8e939e2e7eab70e4728da3bcf004e717",
+ "collection_rev": "414ffa1bc8ba18bd608bbf916d95715311d89ac1",
+ "convert_rev": "7145da14f9cd730e80fb4c6a10108fcfd205e8e7",
+ "crypto_rev": "223e0a62c0f762fd2b510f753861445b52e14fc3",
+ "csslib_rev": "ba2eb2d80530eedefadaade338a09c2dd60410f3",
# Note: Updates to dart_style have to be coordinated with the infrastructure
# team so that the internal formatter `tools/sdks/dart-sdk/bin/dart format`
@@ -109,60 +109,60 @@
"devtools_rev": "51ac983d2db7eb19b3ce5956cb70b769d74fe784",
"ffi_rev": "0c8364a728cfe4e4ba859c53b99d56b3dbe3add4",
"file_rev": "0132eeedea2933513bf230513a766a8baeab0c4f",
- "fixnum_rev": "3bfc2ed1eea7e7acb79ad4f17392f92c816fc5ce",
+ "fixnum_rev": "164712f6547cdfb2709b752188186baf31fd1730",
"glob_rev": "e10eb2407c58427144004458ef85c9bbf7286e56",
- "html_rev": "f108bce59d136c584969fd24a5006914796cf213",
+ "html_rev": "8243e967caad9932c13971af3b2a7c8f028383d5",
"http_multi_server_rev": "35a3b947256768426090e3b1f5132e4fc23c175d",
- "http_parser_rev": "9126ee04e77fd8e4e2e6435b503ee4dd708d7ddc",
+ "http_parser_rev": "eaa63304c333316acd114e3be7ed701d7d7ba32c",
"http_rev": "0c2293062d7c1fa472f299da764a7dbb3895ee22",
"icu_rev": "81d656878ec611cb0b42d52c82e9dae93920d9ba",
- "intl_rev": "9145f308f1458f37630a1ffce3b7d3b471ebbc56",
+ "intl_rev": "e9b573679de5e703d89a242b9dca331c772979ef",
"jinja2_rev": "2222b31554f03e62600cd7e383376a7c187967a1",
"json_rpc_2_rev": "2de9a1f9821807fa2c85fd48e2f70b9cbcddcb67",
"linter_rev": "1ddc70948d94f2449fec69a95e3ceb7b6b6c8348", # 1.25.0
"lints_rev": "8294e5648ab49474541527e2911e72e4c5aefe55",
"logging_rev": "f6979e3bc3b6e1847a08335b7eb6304e18986195",
- "markdown_rev": "e3f4bd28c9e61b522f75f291d4d6cfcfeccd83ee", # 5.0.0
+ "markdown_rev": "73ad99eeddc1ecebd27faa3221e68c81dc8217eb", # 5.0.0
"markupsafe_rev": "8f45f5cfa0009d2a70589bcda0349b8cb2b72783",
"matcher_rev": "12cdc5fbafd666ed908359ae215d5d0306087969",
- "mime_rev": "c2c5ffd594674f32dc277521369da1557a1622d3",
- "mockito_rev": "fcd6b285f7c4a631778890cf02b52a9a68171a71",
+ "mime_rev": "0a75a41445eb642674a0a271eecde78cb025ee60",
+ "mockito_rev": "25d25dab6b57ac710c0be0e759def7505b352ea7",
"oauth2_rev": "199ebf15cbd5b07958438184f32e41c4447a57bf",
"package_config_rev": "cff98c90acc457a3b0750f0a7da0e351a35e5d0c",
- "path_rev": "3d41ea582f5b0b18de3d90008809b877ff3f69bc",
+ "path_rev": "7a0ed40280345b1c11df4c700c71e590738f4257",
"ply_rev": "604b32590ffad5cbb82e4afef1d305512d06ae93",
"pool_rev": "c40cc32eabecb9d60f1045d1403108d968805f9a",
- "protobuf_rev": "3105588b8e515a3391ac5f0673cc06e16d0e3c0c",
+ "protobuf_rev": "3149f6f2d323e11dbcc983b7ac8b3b9e9d686293",
"pub_rev": "c4e9ddc888c3aa89ef4462f0c4298929191e32b9",
"pub_semver_rev": "5c0b4bfd5ca57fe16f1319c581dc8c882e9b8cb2",
"root_certificates_rev": "692f6d6488af68e0121317a9c2c9eb393eb0ee50",
"rust_revision": "b7856f695d65a8ebc846754f97d15814bcb1c244",
"shelf_rev": "05f42601d22c9bfe490ceda50e812f83b7d1de77",
- "source_map_stack_trace_rev": "8eabd96b1811e30a11d3c54c9b4afae4fb72e98f",
- "source_maps_rev": "c07a01b8d5547ce3a47ee7a7a2b938a2bc09afe3",
- "source_span_rev": "8ae724c3e67f5afaacead44e93ff145bfb8775c7",
- "sse_rev": "9a54f1cdd91c8d79a6bf5ef8e849a12756607453",
+ "source_map_stack_trace_rev": "72dbf21a33293b2b8434d0a9751e36f9463981ac",
+ "source_maps_rev": "e93565b43a7b6b367789de8ffba969c4ebeeb317",
+ "source_span_rev": "24151fd80e4557a626f81f2bc0d6a2ebde172cae",
+ "sse_rev": "2df072848a6090d3ed67f30c69e86ec4d6b96cd6",
"stack_trace_rev": "17f09c2c6845bb31c7c385acecce5befb8527a13",
- "stream_channel_rev": "3fa3e40c75c210d617b8b943b9b8f580e9866a89",
+ "stream_channel_rev": "8e0d7ef1f4a3fb97fbd82e11cd539093f58511f3",
"string_scanner_rev": "c637deb8d998b72a5807afbd06aba8370db725c0",
- "sync_http_rev": "b6bd47965694dddffb6e62fb8a6c12d17c4ae4cd",
+ "sync_http_rev": "39509d69fd5a9c3da46eab48fcafdf62e6ad4580",
"term_glyph_rev": "d0f205c67ea70eea47b9f41c8440129a72a9c86e",
"test_descriptor_rev": "5ed5d7f6bf1191592995dcb8eedbbc17df69d386",
"test_process_rev": "3e695bcfeab551473ddc288970f345f30e5e1375",
"test_reflective_loader_rev": "8d0de01bbe852fea1f8e33aba907abcba50a8a1e",
- "test_rev": "d54846bc2b5cfa4e1445fda85c5e48a00940aa68",
- "typed_data_rev": "8b19e29bcf4077147de4d67adeabeb48270c65eb",
- "usage_rev": "79eef484e7403f24b414354a4af2008967484e46",
- "vector_math_rev": "1c72944e8c2f02340a1d90b32aab2e3836cef8cc",
+ "test_rev": "90ec2561a9725d54597498d51b23f1ccf09a6db1",
+ "typed_data_rev": "bb10b64f9a56b8fb49307d4465474bf1c1309f6d",
+ "usage_rev": "1d3c31e780af665fb796a27898a441fcb7d263db",
+ "vector_math_rev": "cdcee487bde4353a6ba7a29bfc7db3840426e50f",
"watcher_rev": "e00c0ea769e32821d91c0880da8eb736839a6e6d",
"web_components_rev": "8f57dac273412a7172c8ade6f361b407e2e4ed02",
"web_socket_channel_rev": "99dbdc5769e19b9eeaf69449a59079153c6a8b1f",
"WebCore_rev": "bcb10901266c884e7b3740abc597ab95373ab55c",
- "webdev_rev": "8c814f9d89915418d8abe354ff9befec8f2906b2",
+ "webdev_rev": "a74737fc46286b3e34475e20e6e44fc92efc8916",
"webdriver_rev": "e1a9ad671ee82e05eee463f922a34585ed2d2f15",
- "webkit_inspection_protocol_rev": "e4965778e2837adc62354eec3a19123402997897",
- "yaml_edit_rev": "0b74d85fac10b4fbf7d1a347debcf16c8f7b0e9c",
- "yaml_rev": "0971c06490b9670add644ed62182acd6a5536946",
+ "webkit_inspection_protocol_rev": "57522d6b29d94903b765c757079d906555d5a171",
+ "yaml_edit_rev": "01589b3ce447b03aed991db49f1ec6445ad5476d",
+ "yaml_rev": "fda5b15692ccfa0feb7793a27fe3829b3d0f77fa",
"zlib_rev": "64bbf988543996eb8df9a86877b32917187eba8f",
# Windows deps
diff --git a/pkg/analysis_server/lib/lsp_protocol/protocol_special.dart b/pkg/analysis_server/lib/lsp_protocol/protocol_special.dart
index 610813f..7f1d192 100644
--- a/pkg/analysis_server/lib/lsp_protocol/protocol_special.dart
+++ b/pkg/analysis_server/lib/lsp_protocol/protocol_special.dart
@@ -62,7 +62,7 @@
bool _alwaysTrue(_, [__]) => true;
-class Either2<T1, T2> {
+class Either2<T1, T2> implements ToJsonable {
final int _which;
final T1? _t1;
final T2? _t2;
@@ -88,6 +88,7 @@
return _which == 1 ? f1(_t1 as T1) : f2(_t2 as T2);
}
+ @override
Object? toJson() => map(specToJson, specToJson);
@override
@@ -97,7 +98,7 @@
bool valueEquals(o) => map((t) => t == o, (t) => t == o);
}
-class Either3<T1, T2, T3> {
+class Either3<T1, T2, T3> implements ToJsonable {
final int _which;
final T1? _t1;
final T2? _t2;
@@ -141,6 +142,7 @@
}
}
+ @override
Object? toJson() => map(specToJson, specToJson, specToJson);
@override
@@ -154,7 +156,7 @@
bool valueEquals(o) => map((t) => t == o, (t) => t == o, (t) => t == o);
}
-class Either4<T1, T2, T3, T4> {
+class Either4<T1, T2, T3, T4> implements ToJsonable {
final int _which;
final T1? _t1;
final T2? _t2;
@@ -212,6 +214,7 @@
}
}
+ @override
Object? toJson() => map(specToJson, specToJson, specToJson, specToJson);
@override
@@ -286,7 +289,7 @@
}
abstract class ToJsonable {
- Object toJson();
+ Object? toJson();
}
extension IncomingMessageExtension on IncomingMessage {
diff --git a/pkg/analysis_server/lib/src/cider/fixes.dart b/pkg/analysis_server/lib/src/cider/fixes.dart
index fd9efd7..fe44877 100644
--- a/pkg/analysis_server/lib/src/cider/fixes.dart
+++ b/pkg/analysis_server/lib/src/cider/fixes.dart
@@ -12,6 +12,7 @@
import 'package:analyzer/error/error.dart';
import 'package:analyzer/instrumentation/service.dart';
import 'package:analyzer/source/line_info.dart';
+import 'package:analyzer/src/dart/analysis/file_state.dart';
import 'package:analyzer/src/dart/analysis/performance_logger.dart';
import 'package:analyzer/src/dart/micro/resolve_file.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_workspace.dart';
@@ -88,7 +89,8 @@
var result = <LibraryElement, Element>{};
var files = _fileResolver.getFilesWithTopLevelDeclarations(name);
for (var file in files) {
- if (file.partOfLibrary == null) {
+ final kind = file.kind;
+ if (kind is LibraryFileStateKind) {
var libraryElement = await _fileResolver.getLibraryByUri2(
uriStr: file.uriStr,
);
diff --git a/pkg/analysis_server/lib/src/lsp/constants.dart b/pkg/analysis_server/lib/src/lsp/constants.dart
index 49ae1ca..2bcebf6 100644
--- a/pkg/analysis_server/lib/src/lsp/constants.dart
+++ b/pkg/analysis_server/lib/src/lsp/constants.dart
@@ -44,8 +44,8 @@
/// Characters to trigger formatting when format-on-type is enabled.
const dartTypeFormattingCharacters = ['}', ';'];
-/// A [ProgressToken] used for reporting progress when the server is analyzing.
-final analyzingProgressToken = Either2<int, String>.t2('ANALYZING');
+/// A [ProgressToken] used for reporting progress while the server is analyzing.
+final analyzingProgressToken = ProgressToken.t2('ANALYZING');
final emptyWorkspaceEdit = WorkspaceEdit();
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
index 000c634..b34d459 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
@@ -23,10 +23,11 @@
import 'package:analyzer/src/util/performance/operation_performance.dart';
import 'package:analyzer_plugin/protocol/protocol.dart' as plugin;
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
-import 'package:collection/collection.dart' show groupBy;
+import 'package:collection/collection.dart'
+ show IterableNullableExtension, groupBy;
-class CodeActionHandler extends MessageHandler<CodeActionParams,
- List<Either2<Command, CodeAction>>> {
+class CodeActionHandler
+ extends MessageHandler<CodeActionParams, TextDocumentCodeActionResult> {
// Because server+plugin results are different types and we lose
// priorities when converting them to CodeActions, store the priorities
// against each action in an expando. This avoids wrapping CodeActions in
@@ -60,10 +61,8 @@
CodeActionParams.jsonHandler;
@override
- Future<ErrorOr<List<Either2<Command, CodeAction>>>> handle(
- CodeActionParams params,
- MessageInfo message,
- CancellationToken token) async {
+ Future<ErrorOr<TextDocumentCodeActionResult>> handle(CodeActionParams params,
+ MessageInfo message, CancellationToken token) async {
if (!isDartDocument(params.textDocument)) {
return success(const []);
}
@@ -189,16 +188,16 @@
/// Wraps a command in a CodeAction if the client supports it so that a
/// CodeActionKind can be supplied.
- Either2<Command, CodeAction> _commandOrCodeAction(
+ Either2<CodeAction, Command> _commandOrCodeAction(
bool supportsLiteralCodeActions,
CodeActionKind kind,
Command command,
) {
return supportsLiteralCodeActions
- ? Either2<Command, CodeAction>.t2(
+ ? Either2<CodeAction, Command>.t1(
CodeAction(title: command.title, kind: kind, command: command),
)
- : Either2<Command, CodeAction>.t1(command);
+ : Either2<CodeAction, Command>.t2(command);
}
/// Creates a CodeAction to apply this assist. Note: This code will fetch the
@@ -280,7 +279,7 @@
}).toList();
}
- Future<List<Either2<Command, CodeAction>>> _getAssistActions(
+ Future<TextDocumentCodeActionResult> _getAssistActions(
bool Function(CodeActionKind?) shouldIncludeKind,
bool supportsLiteralCodeActions,
String path,
@@ -323,7 +322,7 @@
return dedupedCodeActions
.where((action) => shouldIncludeKind(action.kind))
- .map((action) => Either2<Command, CodeAction>.t2(action))
+ .map((action) => Either2<CodeAction, Command>.t1(action))
.toList();
} on InconsistentAnalysisException {
// If an InconsistentAnalysisException occurs, it's likely the user modified
@@ -333,7 +332,7 @@
}
}
- Future<ErrorOr<List<Either2<Command, CodeAction>>>> _getCodeActions(
+ Future<ErrorOr<TextDocumentCodeActionResult>> _getCodeActions(
OperationPerformanceImpl performance,
bool Function(CodeActionKind?) shouldIncludeKind,
bool Function(CodeActionKind?) shouldIncludeAnyOfKind,
@@ -376,12 +375,12 @@
offset, supportedDiagnosticTags, range, unit),
),
]);
- final flatResults = results.expand((x) => x).toList();
+ final flatResults = results.whereNotNull().expand((x) => x).toList();
return success(flatResults);
}
- Future<List<Either2<Command, CodeAction>>> _getFixActions(
+ Future<TextDocumentCodeActionResult> _getFixActions(
bool Function(CodeActionKind?) shouldIncludeKind,
bool supportsLiteralCodeActions,
String path,
@@ -455,7 +454,7 @@
return dedupedActions
.where((action) => shouldIncludeKind(action.kind))
- .map((action) => Either2<Command, CodeAction>.t2(action))
+ .map((action) => Either2<CodeAction, Command>.t1(action))
.toList();
} on InconsistentAnalysisException {
// If an InconsistentAnalysisException occurs, it's likely the user modified
@@ -520,7 +519,7 @@
return pluginFixes;
}
- Future<List<Either2<Command, CodeAction>>> _getRefactorActions(
+ Future<TextDocumentCodeActionResult> _getRefactorActions(
bool Function(CodeActionKind) shouldIncludeKind,
bool supportsLiteralCodeActions,
String path,
@@ -537,7 +536,7 @@
/// Helper to create refactors that execute commands provided with
/// the current file, location and document version.
- Either2<Command, CodeAction> createRefactor(
+ Either2<CodeAction, Command> createRefactor(
CodeActionKind actionKind,
String name,
RefactoringKind refactorKind, [
@@ -564,7 +563,7 @@
}
try {
- final refactorActions = <Either2<Command, CodeAction>>[];
+ final refactorActions = <Either2<CodeAction, Command>>[];
// Extracts
if (shouldIncludeKind(CodeActionKind.RefactorExtract)) {
@@ -641,7 +640,7 @@
/// Gets "Source" CodeActions, which are actions that apply to whole files of
/// source such as Sort Members and Organise Imports.
- Future<List<Either2<Command, CodeAction>>> _getSourceActions(
+ Future<TextDocumentCodeActionResult> _getSourceActions(
bool Function(CodeActionKind) shouldIncludeKind,
bool supportsLiteralCodeActions,
bool supportsApplyEdit,
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart
index a066a4c..530a30f 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart
@@ -22,8 +22,7 @@
import 'package:collection/collection.dart';
class DefinitionHandler extends MessageHandler<TextDocumentPositionParams,
- Either2<List<Location>, List<LocationLink>>>
- with LspPluginRequestHandlerMixin {
+ TextDocumentDefinitionResult> with LspPluginRequestHandlerMixin {
DefinitionHandler(super.server);
@override
Method get handlesMessage => Method.textDocument_definition;
@@ -69,7 +68,7 @@
}
@override
- Future<ErrorOr<Either2<List<Location>, List<LocationLink>>>> handle(
+ Future<ErrorOr<TextDocumentDefinitionResult>> handle(
TextDocumentPositionParams params,
MessageInfo message,
CancellationToken token) async {
@@ -89,9 +88,7 @@
// If there is no lineInfo, the request cannot be translated from LSP line/col
// to server offset/length.
if (lineInfo == null) {
- return success(
- Either2<List<Location>, List<LocationLink>>.t1(const []),
- );
+ return success(TextDocumentDefinitionResult.t2(const []));
}
final offset = toOffset(lineInfo, pos);
@@ -107,9 +104,7 @@
final mergedTargets = mergedResults?.targets ?? [];
if (mergedResults == null) {
- return success(
- Either2<List<Location>, List<LocationLink>>.t1(const []),
- );
+ return success(TextDocumentDefinitionResult.t2(const []));
}
// Convert and filter the results using the correct type of Location class
@@ -129,9 +124,7 @@
(LocationLink element) => element.targetSelectionRange,
);
- return success(
- Either2<List<Location>, List<LocationLink>>.t2(results),
- );
+ return success(TextDocumentDefinitionResult.t2(results));
} else {
final convertedResults = convert(
mergedTargets,
@@ -147,7 +140,7 @@
);
return success(
- Either2<List<Location>, List<LocationLink>>.t1(results),
+ TextDocumentDefinitionResult.t1(Definition.t1(results)),
);
}
});
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_document_symbols.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_document_symbols.dart
index 1e67440..4f0a96c 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_document_symbols.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_document_symbols.dart
@@ -12,7 +12,7 @@
import 'package:analyzer/source/line_info.dart';
class DocumentSymbolHandler extends MessageHandler<DocumentSymbolParams,
- Either2<List<DocumentSymbol>, List<SymbolInformation>>?> {
+ TextDocumentDocumentSymbolResult> {
DocumentSymbolHandler(super.server);
@override
Method get handlesMessage => Method.textDocument_documentSymbol;
@@ -22,13 +22,14 @@
DocumentSymbolParams.jsonHandler;
@override
- Future<ErrorOr<Either2<List<DocumentSymbol>, List<SymbolInformation>>?>>
- handle(DocumentSymbolParams params, MessageInfo message,
- CancellationToken token) async {
+ Future<ErrorOr<TextDocumentDocumentSymbolResult>> handle(
+ DocumentSymbolParams params,
+ MessageInfo message,
+ CancellationToken token) async {
final clientCapabilities = server.clientCapabilities;
if (clientCapabilities == null || !isDartDocument(params.textDocument)) {
return success(
- Either2<List<DocumentSymbol>, List<SymbolInformation>>.t2([]),
+ TextDocumentDocumentSymbolResult.t2([]),
);
}
@@ -85,7 +86,7 @@
);
}
- ErrorOr<Either2<List<DocumentSymbol>, List<SymbolInformation>>?> _getSymbols(
+ ErrorOr<TextDocumentDocumentSymbolResult> _getSymbols(
LspClientCapabilities capabilities,
String path,
ResolvedUnitResult unit,
@@ -101,7 +102,7 @@
return success(null);
}
return success(
- Either2<List<DocumentSymbol>, List<SymbolInformation>>.t1(
+ TextDocumentDocumentSymbolResult.t1(
children
.map((child) => _asDocumentSymbol(
capabilities.documentSymbolKinds, unit.lineInfo, child))
@@ -133,9 +134,7 @@
outline.children?.forEach(addSymbol);
- return success(
- Either2<List<DocumentSymbol>, List<SymbolInformation>>.t2(allSymbols),
- );
+ return success(TextDocumentDocumentSymbolResult.t2(allSymbols));
}
}
}
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_rename.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_rename.dart
index 98d4e5f..facbfd8 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_rename.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_rename.dart
@@ -11,13 +11,8 @@
import 'package:analysis_server/src/services/refactoring/rename_unit_member.dart';
import 'package:analyzer/dart/element/element.dart';
-// TODO(dantup): Generate typedefs in protocol_generated for all named types
-// that map onto unions like this after the switch to JSON spec.
-typedef PrepareRenameResult
- = Either3<Range, PlaceholderAndRange, PrepareRenameResult2>;
-
-class PrepareRenameHandler
- extends MessageHandler<TextDocumentPositionParams, PrepareRenameResult?> {
+class PrepareRenameHandler extends MessageHandler<TextDocumentPositionParams,
+ TextDocumentPrepareRenameResult> {
PrepareRenameHandler(super.server);
@override
Method get handlesMessage => Method.textDocument_prepareRename;
@@ -27,7 +22,7 @@
TextDocumentPositionParams.jsonHandler;
@override
- Future<ErrorOr<PrepareRenameResult?>> handle(
+ Future<ErrorOr<TextDocumentPrepareRenameResult>> handle(
TextDocumentPositionParams params,
MessageInfo message,
CancellationToken token) async {
@@ -66,7 +61,7 @@
ServerErrorCodes.RenameNotValid, initStatus.problem!.message, null);
}
- return success(PrepareRenameResult.t2(PlaceholderAndRange(
+ return success(TextDocumentPrepareRenameResult.t1(PlaceholderAndRange(
range: toRange(
unit.result.lineInfo,
// If the offset is set to -1 it means there is no location for the
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_type_definition.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_type_definition.dart
index 59cf2cc..929a960 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_type_definition.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_type_definition.dart
@@ -15,12 +15,9 @@
import 'package:analyzer_plugin/protocol/protocol_common.dart' as plugin;
import 'package:analyzer_plugin/utilities/analyzer_converter.dart';
-typedef _LocationsOrLinks = Either2<List<Location>, List<LocationLink>>;
-
-class TypeDefinitionHandler
- extends MessageHandler<TypeDefinitionParams, _LocationsOrLinks>
- with LspPluginRequestHandlerMixin {
- static const _emptyResult = _LocationsOrLinks.t1([]);
+class TypeDefinitionHandler extends MessageHandler<TypeDefinitionParams,
+ TextDocumentTypeDefinitionResult> with LspPluginRequestHandlerMixin {
+ static const _emptyResult = TextDocumentTypeDefinitionResult.t2([]);
TypeDefinitionHandler(super.server);
@@ -35,8 +32,10 @@
// The private type in the return type is dictated by the signature of the
// super-method and the class's super-class.
// ignore: library_private_types_in_public_api
- Future<ErrorOr<_LocationsOrLinks>> handle(TypeDefinitionParams params,
- MessageInfo message, CancellationToken token) async {
+ Future<ErrorOr<TextDocumentTypeDefinitionResult>> handle(
+ TypeDefinitionParams params,
+ MessageInfo message,
+ CancellationToken token) async {
if (!isDartDocument(params.textDocument)) {
return success(_emptyResult);
}
@@ -90,13 +89,14 @@
}
if (supportsLocationLink) {
- return success(_LocationsOrLinks.t2([
+ return success(TextDocumentTypeDefinitionResult.t2([
_toLocationLink(
result.lineInfo, targetLineInfo, node, element, location)
]));
} else {
- return success(
- _LocationsOrLinks.t1([_toLocation(location, targetLineInfo)]));
+ return success(TextDocumentTypeDefinitionResult.t1(
+ Definition.t2(_toLocation(location, targetLineInfo)),
+ ));
}
});
});
diff --git a/pkg/analysis_server/lib/src/lsp/progress.dart b/pkg/analysis_server/lib/src/lsp/progress.dart
index 370f54e..17b3e3a 100644
--- a/pkg/analysis_server/lib/src/lsp/progress.dart
+++ b/pkg/analysis_server/lib/src/lsp/progress.dart
@@ -16,7 +16,7 @@
/// Creates a reporter for a token that was supplied by the client and does
/// not need creating prior to use.
factory ProgressReporter.clientProvided(
- LspAnalysisServer server, Either2<int, String> token) =>
+ LspAnalysisServer server, ProgressToken token) =>
_TokenProgressReporter(server, token);
/// Creates a reporter for a new token that must be created prior to being
@@ -24,7 +24,7 @@
///
/// If [token] is not supplied, a random identifier will be used.
factory ProgressReporter.serverCreated(LspAnalysisServer server,
- [Either2<int, String>? token]) =>
+ [ProgressToken? token]) =>
_ServerCreatedProgressReporter(server, token);
ProgressReporter._();
@@ -49,10 +49,10 @@
_ServerCreatedProgressReporter(
LspAnalysisServer server,
- Either2<int, String>? token,
+ ProgressToken? token,
) : super(
server,
- token ?? Either2<int, String>.t2(_randomTokenIdentifier()),
+ token ?? ProgressToken.t2(_randomTokenIdentifier()),
);
@override
@@ -101,7 +101,7 @@
class _TokenProgressReporter extends ProgressReporter {
final LspAnalysisServer _server;
- final Either2<int, String> _token;
+ final ProgressToken _token;
bool _needsEnd = false;
_TokenProgressReporter(this._server, this._token) : super._();
diff --git a/pkg/analysis_server/lib/src/lsp/source_edits.dart b/pkg/analysis_server/lib/src/lsp/source_edits.dart
index 99021fb..ac2a8b8 100644
--- a/pkg/analysis_server/lib/src/lsp/source_edits.dart
+++ b/pkg/analysis_server/lib/src/lsp/source_edits.dart
@@ -26,10 +26,7 @@
/// changes into account, this will also apply the edits to [oldContent].
ErrorOr<Pair<String, List<plugin.SourceEdit>>> applyAndConvertEditsToServer(
String oldContent,
- List<
- Either2<TextDocumentContentChangeEvent1,
- TextDocumentContentChangeEvent2>>
- changes, {
+ List<TextDocumentContentChangeEvent> changes, {
bool failureIsCritical = false,
}) {
var newContent = oldContent;
diff --git a/pkg/analysis_server/test/analysis/notification_navigation_test.dart b/pkg/analysis_server/test/analysis/notification_navigation_test.dart
index a397b81..2d1b4e5 100644
--- a/pkg/analysis_server/test/analysis/notification_navigation_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_navigation_test.dart
@@ -377,12 +377,13 @@
class BBB {}
''');
await prepareNavigation();
- // has region for complete "A.named"
- assertHasRegionString('A.named');
+ // has no region for complete "A.named"
+ assertNoRegion('A.named', 'A.named'.length);
+ // has separate regions for "A" and "named"
+ assertHasRegion('A.named(', 'A'.length);
+ assertHasTarget('A {');
+ assertHasRegion('named(', 'named'.length);
assertHasTarget('named(BBB');
- // no separate regions for "A" and "named"
- assertNoRegion('A.named(', 'A'.length);
- assertNoRegion('named(', 'named'.length);
// validate that we don't forget to resolve parameters
assertHasRegionTarget('BBB p', 'BBB {}');
}
@@ -413,7 +414,7 @@
}
''');
await prepareNavigation();
- assertHasRegionTarget('B<A>.named;', 'named();');
+ assertHasRegionTarget('B<A>.named;', 'B<T> {');
assertHasRegionTarget('named;', 'named();');
assertHasRegionTarget('A>', 'A {}');
}
@@ -428,7 +429,7 @@
}
''');
await prepareNavigation();
- assertHasRegionTarget('A.new;', 'A();');
+ assertHasRegionTarget('A.new;', 'A {');
assertHasRegionTarget('new;', 'A();');
}
@@ -442,7 +443,7 @@
}
''');
await prepareNavigation();
- assertHasRegionTarget('A.new;', 'new();');
+ assertHasRegionTarget('A.new;', 'A {');
assertHasRegionTarget('new;', 'new();');
}
@@ -503,7 +504,7 @@
await prepareNavigation();
{
assertHasRegionString('B.named;', 'B'.length);
- assertHasTarget('named();');
+ assertHasTarget('B {');
}
{
assertHasRegionString('named;', 'named'.length);
@@ -525,7 +526,7 @@
await prepareNavigation();
{
assertHasRegion('C<A>');
- assertHasTarget('named() {}');
+ assertHasTarget('C<T> {');
}
{
assertHasRegion('A>.named');
@@ -1015,7 +1016,7 @@
await prepareNavigation();
{
assertHasRegionString('A.named();', 'A'.length);
- assertHasTarget('named() {}');
+ assertHasTarget('A {');
}
{
assertHasRegionString('named();', 'named'.length);
@@ -1036,7 +1037,7 @@
await prepareNavigation();
{
assertHasRegionString('B<A>', 'B'.length);
- assertHasTarget('named() {}');
+ assertHasTarget('B<T> {');
}
{
assertHasRegion('A>.named');
diff --git a/pkg/analysis_server/test/integration/analysis/navigation_test.dart b/pkg/analysis_server/test/integration/analysis/navigation_test.dart
index 8413241..91a6e69 100644
--- a/pkg/analysis_server/test/integration/analysis/navigation_test.dart
+++ b/pkg/analysis_server/test/integration/analysis/navigation_test.dart
@@ -105,9 +105,7 @@
checkLocal('Class<int>', 'Class<TypeParameter>', ElementKind.CLASS);
checkRemote("'test2.dart';", r'test2.dart$', ElementKind.COMPILATION_UNIT);
checkLocal(
- 'Class<int>.constructor',
- 'constructor(); /* constructor declaration */',
- ElementKind.CONSTRUCTOR);
+ 'Class<int>.constructor', 'Class<TypeParameter>', ElementKind.CLASS);
checkLocal(
'constructor(); // usage',
'constructor(); /* constructor declaration */',
diff --git a/pkg/analysis_server/test/lsp/code_actions_abstract.dart b/pkg/analysis_server/test/lsp/code_actions_abstract.dart
index 3d228b7..2f059a1 100644
--- a/pkg/analysis_server/test/lsp/code_actions_abstract.dart
+++ b/pkg/analysis_server/test/lsp/code_actions_abstract.dart
@@ -89,7 +89,7 @@
Future verifyCodeActionEdits(Either2<Command, CodeAction> codeAction,
String content, String expectedContent,
{bool expectDocumentChanges = false,
- Either2<int, String>? workDoneToken}) async {
+ ProgressToken? workDoneToken}) async {
final command = codeAction.map(
(command) => command,
(codeAction) => codeAction.command!,
@@ -106,7 +106,7 @@
Future<void> verifyCommandEdits(
Command command, String content, String expectedContent,
{bool expectDocumentChanges = false,
- Either2<int, String>? workDoneToken}) async {
+ ProgressToken? workDoneToken}) async {
ApplyWorkspaceEditParams? editParams;
final commandResponse = await handleExpectedRequest<Object?,
diff --git a/pkg/analysis_server/test/lsp/definition_test.dart b/pkg/analysis_server/test/lsp/definition_test.dart
index aa59e35..1577e53 100644
--- a/pkg/analysis_server/test/lsp/definition_test.dart
+++ b/pkg/analysis_server/test/lsp/definition_test.dart
@@ -55,6 +55,62 @@
expect(loc.uri, equals(referencedFileUri.toString()));
}
+ Future<void> test_atDeclaration_class() async {
+ final contents = '''
+class [[^A]] {}
+ ''';
+
+ await testContents(contents);
+ }
+
+ Future<void> test_atDeclaration_constructorNamed() async {
+ final contents = '''
+class A {
+ A.[[^named]]() {}
+}
+ ''';
+
+ await testContents(contents);
+ }
+
+ Future<void> test_atDeclaration_constructorNamed_typeName() async {
+ final contents = '''
+class [[A]] {
+ ^A.named() {}
+}
+ ''';
+
+ await testContents(contents);
+ }
+
+ Future<void> test_atDeclaration_defaultConstructor() async {
+ final contents = '''
+class A {
+ [[^A]]() {}
+}
+ ''';
+
+ await testContents(contents);
+ }
+
+ Future<void> test_atDeclaration_function() async {
+ final contents = '''
+void [[^f]]() {}
+ ''';
+
+ await testContents(contents);
+ }
+
+ Future<void> test_atDeclaration_method() async {
+ final contents = '''
+class A {
+ void [[^f]]() {}
+}
+ ''';
+
+ await testContents(contents);
+ }
+
Future<void> test_comment_adjacentReference() async {
/// Computing Dart navigation locates a node at the provided offset then
/// returns all navigation regions inside it. This test ensures we filter
@@ -89,7 +145,7 @@
await testContents(contents);
}
- Future<void> test_constructor_named() async {
+ Future<void> test_constructorNamed() async {
final contents = '''
f() {
final a = A.named^();
@@ -103,6 +159,20 @@
await testContents(contents);
}
+ Future<void> test_constructorNamed_typeName() async {
+ final contents = '''
+f() {
+ final a = A^.named();
+}
+
+class [[A]] {
+ A.named();
+}
+''';
+
+ await testContents(contents);
+ }
+
Future<void> test_fromPlugins() async {
final pluginAnalyzedFilePath = join(projectFolderPath, 'lib', 'foo.foo');
final pluginAnalyzedFileUri = Uri.file(pluginAnalyzedFilePath);
diff --git a/pkg/analysis_server/test/lsp/document_changes_test.dart b/pkg/analysis_server/test/lsp/document_changes_test.dart
index db37854..db65c63 100644
--- a/pkg/analysis_server/test/lsp/document_changes_test.dart
+++ b/pkg/analysis_server/test/lsp/document_changes_test.dart
@@ -35,15 +35,13 @@
Future<void> test_documentChange_notifiesPlugins() async {
await _initializeAndOpen();
await changeFile(2, mainFileUri, [
- Either2<TextDocumentContentChangeEvent1,
- TextDocumentContentChangeEvent2>.t1(TextDocumentContentChangeEvent1(
+ TextDocumentContentChangeEvent.t1(TextDocumentContentChangeEvent1(
range: Range(
start: Position(line: 0, character: 6),
end: Position(line: 0, character: 9)),
text: 'Bar',
)),
- Either2<TextDocumentContentChangeEvent1,
- TextDocumentContentChangeEvent2>.t1(TextDocumentContentChangeEvent1(
+ TextDocumentContentChangeEvent.t1(TextDocumentContentChangeEvent1(
range: Range(
start: Position(line: 1, character: 21),
end: Position(line: 1, character: 24)),
@@ -63,15 +61,13 @@
Future<void> test_documentChange_updatesOverlay() async {
await _initializeAndOpen();
await changeFile(2, mainFileUri, [
- Either2<TextDocumentContentChangeEvent1,
- TextDocumentContentChangeEvent2>.t1(TextDocumentContentChangeEvent1(
+ TextDocumentContentChangeEvent.t1(TextDocumentContentChangeEvent1(
range: Range(
start: Position(line: 0, character: 6),
end: Position(line: 0, character: 9)),
text: 'Bar',
)),
- Either2<TextDocumentContentChangeEvent1,
- TextDocumentContentChangeEvent2>.t1(TextDocumentContentChangeEvent1(
+ TextDocumentContentChangeEvent.t1(TextDocumentContentChangeEvent1(
range: Range(
start: Position(line: 1, character: 21),
end: Position(line: 1, character: 24)),
diff --git a/pkg/analysis_server/test/lsp/file_modification_test.dart b/pkg/analysis_server/test/lsp/file_modification_test.dart
index b4fc00a..80cbf07 100644
--- a/pkg/analysis_server/test/lsp/file_modification_test.dart
+++ b/pkg/analysis_server/test/lsp/file_modification_test.dart
@@ -29,8 +29,7 @@
// to alert the user to something failing.
final error = await expectErrorNotification(() async {
await changeFile(222, mainFileUri, [
- Either2<TextDocumentContentChangeEvent1,
- TextDocumentContentChangeEvent2>.t1(TextDocumentContentChangeEvent1(
+ TextDocumentContentChangeEvent.t1(TextDocumentContentChangeEvent1(
range: Range(
start: Position(line: 999, character: 999),
end: Position(line: 999, character: 999)),
@@ -63,8 +62,7 @@
await openFile(mainFileUri, initialContent);
await changeFile(222, mainFileUri, [
// Replace line1:5-1:8 with spaces.
- Either2<TextDocumentContentChangeEvent1,
- TextDocumentContentChangeEvent2>.t1(TextDocumentContentChangeEvent1(
+ TextDocumentContentChangeEvent.t1(TextDocumentContentChangeEvent1(
range: Range(
start: Position(line: 1, character: 5),
end: Position(line: 1, character: 8)),
@@ -81,8 +79,8 @@
// It's not valid for a client to send a request to modify a file that it
// has not opened, but Visual Studio has done it in the past so we should
// ensure it generates an obvious error that the user can understand.
- final simpleEdit = Either2<TextDocumentContentChangeEvent1,
- TextDocumentContentChangeEvent2>.t1(TextDocumentContentChangeEvent1(
+ final simpleEdit =
+ TextDocumentContentChangeEvent.t1(TextDocumentContentChangeEvent1(
range: Range(
start: Position(line: 1, character: 1),
end: Position(line: 1, character: 1)),
diff --git a/pkg/analysis_server/test/lsp/server_abstract.dart b/pkg/analysis_server/test/lsp/server_abstract.dart
index 2799810..9a119b2 100644
--- a/pkg/analysis_server/test/lsp/server_abstract.dart
+++ b/pkg/analysis_server/test/lsp/server_abstract.dart
@@ -626,8 +626,7 @@
/// A progress token used in tests where the client-provides the token, which
/// should not be validated as being created by the server first.
- final clientProvidedTestWorkDoneToken =
- Either2<int, String>.t2('client-test');
+ final clientProvidedTestWorkDoneToken = ProgressToken.t2('client-test');
int _id = 0;
late String projectFolderPath,
@@ -646,7 +645,7 @@
/// null if an initialization request has not yet been sent.
ClientCapabilities? _clientCapabilities;
- final validProgressTokens = <Either2<num, String>>{};
+ final validProgressTokens = <ProgressToken>{};
/// Whether to include 'clientRequestTime' fields in outgoing messages.
bool includeClientRequestTime = false;
@@ -822,10 +821,7 @@
Future changeFile(
int newVersion,
Uri uri,
- List<
- Either2<TextDocumentContentChangeEvent1,
- TextDocumentContentChangeEvent2>>
- changes,
+ List<TextDocumentContentChangeEvent> changes,
) async {
var notification = makeNotification(
Method.textDocument_didChange,
@@ -872,7 +868,7 @@
Future<T> executeCommand<T>(
Command command, {
T Function(Map<String, Object?>)? decoder,
- Either2<int, String>? workDoneToken,
+ ProgressToken? workDoneToken,
}) async {
final request = makeRequest(
Method.workspace_executeCommand,
@@ -1294,7 +1290,7 @@
return expectSuccessfulResponseTo(request, Location.fromJson);
}
- Future<Either2<List<Location>, List<LocationLink>>> getTypeDefinition(
+ Future<TextDocumentTypeDefinitionResult> getTypeDefinition(
Uri uri, Position pos) {
final request = makeRequest(
Method.textDocument_typeDefinition,
@@ -1303,30 +1299,51 @@
position: pos,
),
);
+
+ // TextDocumentTypeDefinitionResult is a nested Either, so we need to handle
+ // nested fromJson/canParse here.
+ // TextDocumentTypeDefinitionResult: Either2<Definition, List<DefinitionLink>>?
+ // Definition: Either2<List<Location>, Location>
+
+ // Definition = Either2<List<Location>, Location>
+ final definitionCanParse = _generateCanParseFor(
+ _canParseList(Location.canParse),
+ Location.canParse,
+ );
+ final definitionFromJson = _generateFromJsonFor(
+ _canParseList(Location.canParse),
+ _fromJsonList(Location.fromJson),
+ Location.canParse,
+ Location.fromJson,
+ );
+
return expectSuccessfulResponseTo(
request,
_generateFromJsonFor(
- _canParseList(Location.canParse),
- _fromJsonList(Location.fromJson),
- _canParseList(LocationLink.canParse),
- _fromJsonList(LocationLink.fromJson)),
+ definitionCanParse,
+ definitionFromJson,
+ _canParseList(DefinitionLink.canParse),
+ _fromJsonList(DefinitionLink.fromJson)),
);
}
Future<List<Location>> getTypeDefinitionAsLocation(
Uri uri, Position pos) async {
- final results = await getTypeDefinition(uri, pos);
+ final results = (await getTypeDefinition(uri, pos))!;
return results.map(
- (locations) => locations,
- (locationLinks) => throw 'Expected List<Location> got List<LocationLink>',
+ (locationOrList) => locationOrList.map(
+ (locations) => locations,
+ (location) => [location],
+ ),
+ (locationLinks) => throw 'Expected Locations, got LocationLinks',
);
}
Future<List<LocationLink>> getTypeDefinitionAsLocationLinks(
Uri uri, Position pos) async {
- final results = await getTypeDefinition(uri, pos);
+ final results = (await getTypeDefinition(uri, pos))!;
return results.map(
- (locations) => throw 'Expected List<LocationLink> got List<Location>',
+ (locationOrList) => throw 'Expected LocationLinks, got Locations',
(locationLinks) => locationLinks,
);
}
@@ -1728,8 +1745,7 @@
newVersion,
uri,
[
- Either2<TextDocumentContentChangeEvent1,
- TextDocumentContentChangeEvent2>.t2(
+ TextDocumentContentChangeEvent.t2(
TextDocumentContentChangeEvent2(text: content))
],
);
@@ -1928,15 +1944,17 @@
String withoutRangeMarkers(String contents) =>
contents.replaceAll(rangeMarkerStart, '').replaceAll(rangeMarkerEnd, '');
- bool Function(List<dynamic>, LspJsonReporter) _canParseList<T>(
- bool Function(Map<String, dynamic>, LspJsonReporter) canParse) =>
- (input, reporter) => input
- .cast<Map<String, dynamic>>()
- .every((item) => canParse(item, reporter));
+ bool Function(Object?, LspJsonReporter) _canParseList<T>(
+ bool Function(Map<String, Object?>, LspJsonReporter) canParse) =>
+ (input, reporter) =>
+ input is List &&
+ input
+ .cast<Map<String, Object?>>()
+ .every((item) => canParse(item, reporter));
- List<T> Function(List<dynamic>) _fromJsonList<T>(
- T Function(Map<String, dynamic>) fromJson) =>
- (input) => input.cast<Map<String, dynamic>>().map(fromJson).toList();
+ List<T> Function(List<Object?>) _fromJsonList<T>(
+ T Function(Map<String, Object?>) fromJson) =>
+ (input) => input.cast<Map<String, Object?>>().map(fromJson).toList();
Future<void> _handleProgress(NotificationMessage request) async {
final params =
@@ -1974,22 +1992,32 @@
notification.method == Method.window_showMessage;
}
+ /// Creates a `canParse()` function for an `Either2<T1, T2>` using
+ /// the `canParse` function for each type.
+ static bool Function(Object?, LspJsonReporter) _generateCanParseFor<T1, T2>(
+ bool Function(Object?, LspJsonReporter) canParse1,
+ bool Function(Object?, LspJsonReporter) canParse2,
+ ) {
+ return (input, reporter) =>
+ canParse1(input, reporter) || canParse2(input, reporter);
+ }
+
/// Creates a `fromJson()` function for an `Either2<T1, T2>` using
/// the `canParse` and `fromJson` functions for each type.
- static Either2<T1, T2> Function(R) _generateFromJsonFor<T1, T2, R>(
- bool Function(R, LspJsonReporter) canParse1,
- T1 Function(R) fromJson1,
- bool Function(R, LspJsonReporter) canParse2,
- T2 Function(R) fromJson2,
+ static Either2<T1, T2> Function(Object?) _generateFromJsonFor<T1, T2, R1, R2>(
+ bool Function(Object?, LspJsonReporter) canParse1,
+ T1 Function(R1) fromJson1,
+ bool Function(Object?, LspJsonReporter) canParse2,
+ T2 Function(R2) fromJson2,
[LspJsonReporter? reporter]) {
reporter ??= nullLspJsonReporter;
return (input) {
reporter!;
if (canParse1(input, reporter)) {
- return Either2<T1, T2>.t1(fromJson1(input));
+ return Either2<T1, T2>.t1(fromJson1(input as R1));
}
if (canParse2(input, reporter)) {
- return Either2<T1, T2>.t2(fromJson2(input));
+ return Either2<T1, T2>.t2(fromJson2(input as R2));
}
throw '$input was not one of ($T1, $T2)';
};
diff --git a/pkg/analysis_server/test/lsp/server_test.dart b/pkg/analysis_server/test/lsp/server_test.dart
index b29f98e..8bcb631 100644
--- a/pkg/analysis_server/test/lsp/server_test.dart
+++ b/pkg/analysis_server/test/lsp/server_test.dart
@@ -58,13 +58,11 @@
// client and server are out of sync and we expect the server to shut down.
final error = await expectErrorNotification(() async {
await changeFile(222, mainFileUri, [
- Either2<TextDocumentContentChangeEvent1,
- TextDocumentContentChangeEvent2>.t1(
- TextDocumentContentChangeEvent1(
- range: Range(
- start: Position(line: 99, character: 99),
- end: Position(line: 99, character: 99)),
- text: ' ')),
+ TextDocumentContentChangeEvent.t1(TextDocumentContentChangeEvent1(
+ range: Range(
+ start: Position(line: 99, character: 99),
+ end: Position(line: 99, character: 99)),
+ text: ' ')),
]);
});
diff --git a/pkg/analysis_server/test/src/cider/cider_service.dart b/pkg/analysis_server/test/src/cider/cider_service.dart
index 5fb6ccc..cdfefa6 100644
--- a/pkg/analysis_server/test/src/cider/cider_service.dart
+++ b/pkg/analysis_server/test/src/cider/cider_service.dart
@@ -44,6 +44,7 @@
prefetchFiles: null,
workspace: workspace,
byteStore: MemoryByteStore(),
+ isGenerated: (_) => false,
);
fileResolver.testView = FileResolverTestView();
}
diff --git a/pkg/analysis_server/test/tool/lsp_spec/json_test.dart b/pkg/analysis_server/test/tool/lsp_spec/json_test.dart
index 9275e6d..6cfdde6 100644
--- a/pkg/analysis_server/test/tool/lsp_spec/json_test.dart
+++ b/pkg/analysis_server/test/tool/lsp_spec/json_test.dart
@@ -10,6 +10,10 @@
void main() {
group('toJson', () {
+ final start = Position(line: 1, character: 1);
+ final end = Position(line: 2, character: 2);
+ final range = Range(start: start, end: end);
+
test('returns correct JSON for a union', () {
final num = Either2.t1(1);
final string = Either2.t2('Test');
@@ -26,6 +30,29 @@
expect(output, equals('{"id":1,"jsonrpc":"test","method":"shutdown"}'));
});
+ test('returns correct output for nested union types', () {
+ final message = ResponseMessage(
+ id: Either2<int, String>.t1(1),
+ result:
+ Either2<Either2<List<Location>, Location>, List<LocationLink>>.t1(
+ Either2<List<Location>, Location>.t1([
+ Location(
+ range: range,
+ uri: '!uri',
+ )
+ ])),
+ jsonrpc: jsonRpcVersion,
+ );
+ final output = json.encode(message.toJson());
+ expect(
+ output,
+ equals(
+ '{"id":1,"jsonrpc":"2.0",'
+ '"result":[{"range":{"end":{"character":2,"line":2},"start":{"character":1,"line":1}},'
+ '"uri":"!uri"}]}',
+ ));
+ });
+
test('returns correct output for union types containing interface types',
() {
final params = Either2<String, TextDocumentItem>.t2(TextDocumentItem(
@@ -38,9 +65,6 @@
});
test('returns correct output for types with lists', () {
- final start = Position(line: 1, character: 1);
- final end = Position(line: 2, character: 2);
- final range = Range(start: start, end: end);
final location = Location(uri: 'y-uri', range: range);
final codeAction = Diagnostic(
range: range,
diff --git a/pkg/analyzer/lib/src/context/context.dart b/pkg/analyzer/lib/src/context/context.dart
index e6b7d64..1b9c657 100644
--- a/pkg/analyzer/lib/src/context/context.dart
+++ b/pkg/analyzer/lib/src/context/context.dart
@@ -24,22 +24,12 @@
}
@override
- set analysisOptions(AnalysisOptions options) {
- throw StateError('Cannot be changed.');
- }
-
- @override
DeclaredVariables get declaredVariables {
return _synchronousSession.declaredVariables;
}
bool get hasTypeProvider => _synchronousSession.hasTypeProvider;
- @override
- set sourceFactory(SourceFactory factory) {
- throw StateError('Cannot be changed.');
- }
-
TypeProviderImpl get typeProviderLegacy {
return _synchronousSession.typeProviderLegacy;
}
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 611e4fe..f80867d 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -86,7 +86,7 @@
/// TODO(scheglov) Clean up the list of implicitly analyzed files.
class AnalysisDriver implements AnalysisDriverGeneric {
/// The version of data format, should be incremented on every format change.
- static const int DATA_VERSION = 223;
+ static const int DATA_VERSION = 224;
/// The number of exception contexts allowed to write. Once this field is
/// zero, we stop writing any new exception contexts in this process.
@@ -116,15 +116,17 @@
/// the content from the file.
final FileContentCache _fileContentCache;
+ late final StoredFileContentStrategy _fileContentStrategy;
+
/// The analysis options to analyze with.
- AnalysisOptionsImpl _analysisOptions;
+ final AnalysisOptionsImpl _analysisOptions;
/// The [Packages] object with packages and their language versions.
- Packages _packages;
+ final Packages _packages;
/// The [SourceFactory] is used to resolve URIs to paths and restore URIs
/// from file paths.
- SourceFactory _sourceFactory;
+ final SourceFactory _sourceFactory;
final MacroKernelBuilder? macroKernelBuilder;
@@ -277,6 +279,9 @@
analysisContext?.driver = this;
_onResults = _resultController.stream.asBroadcastStream();
_testView = AnalysisDriverTestView(this);
+
+ _fileContentStrategy = StoredFileContentStrategy(_fileContentCache);
+
_createFileTracker();
_scheduler.add(this);
_search = Search(this);
@@ -568,42 +573,6 @@
_libraryContext = null;
}
- /// Some state on which analysis depends has changed, so the driver needs to be
- /// re-configured with the new state.
- ///
- /// At least one of the optional parameters should be provided, but only those
- /// that represent state that has actually changed need be provided.
- @Deprecated('Provide all necessary values to the constructor')
- void configure({
- DriverBasedAnalysisContext? analysisContext,
- AnalysisOptionsImpl? analysisOptions,
- Packages? packages,
- SourceFactory? sourceFactory,
- }) {
- if (analysisContext != null) {
- this.analysisContext = analysisContext;
- _scheduler.driverWatcher?.addedDriver(this);
- }
- if (analysisOptions != null) {
- _analysisOptions = analysisOptions;
- }
- if (packages != null) {
- _packages = packages;
- }
- if (sourceFactory != null) {
- _sourceFactory = sourceFactory;
- }
-
- _priorityResults.clear();
- clearLibraryContext();
-
- Iterable<String> addedFiles = _fileTracker.addedFiles;
- _createFileTracker();
- _fileTracker.addFiles(addedFiles);
-
- _scheduler.notify(this);
- }
-
/// Return a [Future] that completes when discovery of all files that are
/// potentially available is done, so that they are included in [knownFiles].
Future<void> discoverAvailableFiles() {
@@ -1507,9 +1476,11 @@
_saltForUnlinked,
_saltForElements,
featureSetProvider,
- fileContentCache: _fileContentCache,
+ fileContentStrategy: _fileContentStrategy,
+ prefetchFiles: null,
+ isGenerated: (_) => false,
);
- _fileTracker = FileTracker(_logger, _fsState);
+ _fileTracker = FileTracker(_logger, _fsState, _fileContentStrategy);
}
/// If this has not been done yet, schedule discovery of all files that are
diff --git a/pkg/analyzer/lib/src/dart/analysis/file_state.dart b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
index 193ed91..8ce8e30 100644
--- a/pkg/analyzer/lib/src/dart/analysis/file_state.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
@@ -30,11 +30,14 @@
import 'package:analyzer/src/generated/parser.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
+import 'package:analyzer/src/ignore_comments/ignore_info.dart';
import 'package:analyzer/src/source/source_resource.dart';
import 'package:analyzer/src/summary/api_signature.dart';
import 'package:analyzer/src/summary/package_bundle_reader.dart';
import 'package:analyzer/src/summary2/informative_data.dart';
import 'package:analyzer/src/util/either.dart';
+import 'package:analyzer/src/util/file_paths.dart' as file_paths;
+import 'package:analyzer/src/util/performance/operation_performance.dart';
import 'package:analyzer/src/util/uri.dart';
import 'package:analyzer/src/workspace/workspace.dart';
import 'package:collection/collection.dart';
@@ -186,6 +189,12 @@
ExternalLibrary._(this.source);
}
+abstract class FileContent {
+ String get content;
+ String get contentHash;
+ bool get exists;
+}
+
/// [FileContentOverlay] is used to temporary override content of files.
class FileContentOverlay {
final _map = <String, String>{};
@@ -211,6 +220,10 @@
}
}
+abstract class FileContentStrategy {
+ FileContent get(String path);
+}
+
/// Information about a file being analyzed, explicitly or implicitly.
///
/// It provides a consistent view on its properties.
@@ -251,9 +264,7 @@
/// The language version for the package that contains this file.
final Version packageLanguageVersion;
- bool? _exists;
- String? _content;
- String? _contentHash;
+ FileContent? _fileContent;
LineInfo? _lineInfo;
Uint8List? _unlinkedSignature;
String? _unlinkedKey;
@@ -306,10 +317,10 @@
}
/// The content of the file.
- String get content => _content!;
+ String get content => _fileContent!.content;
/// The MD5 hash of the [content].
- String get contentHash => _contentHash!;
+ String get contentHash => _fileContent!.contentHash;
/// The class member names defined by the file.
Set<String> get definedClassMemberNames {
@@ -341,7 +352,7 @@
}
/// Return `true` if the file exists.
- bool get exists => _exists!;
+ bool get exists => _fileContent!.exists;
/// The list of files this file exports.
List<FileState?> get exportedFiles {
@@ -417,6 +428,10 @@
return _driverUnlinkedUnit!.referencedNames;
}
+ File get resource {
+ return _fsState._resourceProvider.getFile(path);
+ }
+
@visibleForTesting
FileStateTestView get test => FileStateTestView(this);
@@ -448,6 +463,8 @@
/// The [UnlinkedUnit] of the file.
UnlinkedUnit get unlinked2 => _unlinked2!;
+ String get unlinkedKey => _unlinkedKey!;
+
/// The MD5 signature based on the content, feature sets, language version.
Uint8List get unlinkedSignature => _unlinkedSignature!;
@@ -459,6 +476,26 @@
return other is FileState && other.uri == uri;
}
+ /// Collect all files that are transitively referenced by this file via
+ /// imports, exports, and parts.
+ void collectAllReferencedFiles(Set<String> referencedFiles) {
+ for (final file in directReferencedFiles) {
+ if (referencedFiles.add(file.path)) {
+ file.collectAllReferencedFiles(referencedFiles);
+ }
+ }
+ }
+
+ /// Return the content of the file, the empty string if cannot be read.
+ ///
+ /// 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].
+ /// TODO(scheglov) replace with [content]
+ String getContent() {
+ return _fileContent!.content;
+ }
+
void internal_setLibraryCycle(LibraryCycle? cycle) {
_libraryCycle = cycle;
}
@@ -482,6 +519,43 @@
}
}
+ /// TODO(scheglov) Remove it when [IgnoreInfo] is stored here.
+ CompilationUnitImpl parse2(
+ 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);
+
+ Parser parser = Parser(
+ source,
+ errorListener,
+ featureSet: scanner.featureSet,
+ lineInfo: lineInfo,
+ );
+ parser.enableOptionalNewAndConst = true;
+
+ var unit = parser.parseCompilationUnit(token);
+ unit.languageVersion = LibraryLanguageVersion(
+ package: packageLanguageVersion,
+ override: scanner.overrideVersion,
+ );
+
+ // StringToken uses a static instance of StringCanonicalizer, so we need
+ // to clear it explicitly once we are done using it for this file.
+ StringTokenImpl.canonicalizer.clear();
+
+ return unit;
+ }
+
/// Read the file content and ensure that all of the file properties are
/// consistent with the read content, including API signature.
///
@@ -489,11 +563,10 @@
FileStateRefreshResult refresh() {
_invalidateCurrentUnresolvedData();
- final rawFileState = _fsState._fileContentCache.get(path);
- final contentChanged = _contentHash != rawFileState.contentHash;
- _content = rawFileState.content;
- _exists = rawFileState.exists;
- _contentHash = rawFileState.contentHash;
+ final rawFileState = _fsState.fileContentStrategy.get(path);
+ final contentChanged =
+ _fileContent?.contentHash != rawFileState.contentHash;
+ _fileContent = rawFileState;
// Prepare the unlinked bundle key.
{
@@ -501,8 +574,8 @@
signature.addUint32List(_fsState._saltForUnlinked);
signature.addFeatureSet(_contextFeatureSet);
signature.addLanguageVersion(packageLanguageVersion);
- signature.addString(_contentHash!);
- signature.addBool(_exists!);
+ signature.addString(contentHash);
+ signature.addBool(exists);
_unlinkedSignature = signature.toByteList();
var signatureHex = hex.encode(_unlinkedSignature!);
// TODO(scheglov) Use the path as the key, and store the signature.
@@ -514,6 +587,8 @@
_unlinked2 = _driverUnlinkedUnit!.unit;
_lineInfo = LineInfo(_unlinked2!.lineStarts);
+ _prefetchDirectReferences();
+
// Prepare API signature.
var newApiSignature = _unlinked2!.apiSignature;
bool apiSignatureChanged = _apiSignature != null &&
@@ -588,8 +663,11 @@
/// Return the unlinked unit, from bytes or new.
AnalysisDriverUnlinkedUnit _getUnlinkedUnit() {
+ final testData = _fsState.testData?.forFile(resource);
+
var bytes = _fsState._byteStore.get(_unlinkedKey!);
if (bytes != null && bytes.isNotEmpty) {
+ testData?.unlinkedKeyGet.add(unlinkedKey);
return AnalysisDriverUnlinkedUnit.fromBytes(bytes);
}
@@ -608,6 +686,7 @@
);
var bytes = driverUnlinkedUnit.toBytes();
_fsState._byteStore.putGet(_unlinkedKey!, bytes);
+ testData?.unlinkedKeyPut.add(unlinkedKey);
return driverUnlinkedUnit;
});
}
@@ -637,36 +716,43 @@
}
CompilationUnitImpl _parse(AnalysisErrorListener errorListener) {
- 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);
+ return parse2(errorListener, content);
+ }
- Parser parser = Parser(
- source,
- errorListener,
- featureSet: scanner.featureSet,
- lineInfo: lineInfo,
- );
- parser.enableOptionalNewAndConst = true;
+ /// TODO(scheglov) write tests
+ void _prefetchDirectReferences() {
+ final prefetchFiles = _fsState.prefetchFiles;
+ if (prefetchFiles == null) {
+ return;
+ }
- var unit = parser.parseCompilationUnit(token);
- unit.languageVersion = LibraryLanguageVersion(
- package: packageLanguageVersion,
- override: scanner.overrideVersion,
- );
+ var paths = <String>{};
- // StringToken uses a static instance of StringCanonicalizer, so we need
- // to clear it explicitly once we are done using it for this file.
- StringTokenImpl.canonicalizer.clear();
+ void addRelativeUri(String relativeUriStr) {
+ final Uri absoluteUri;
+ try {
+ final relativeUri = Uri.parse(relativeUriStr);
+ absoluteUri = resolveRelativeUri(uri, relativeUri);
+ } on FormatException {
+ return;
+ }
+ final path = _fsState._sourceFactory.forUri2(absoluteUri)?.fullName;
+ if (path != null) {
+ paths.add(path);
+ }
+ }
- return unit;
+ for (final directive in unlinked2.imports) {
+ addRelativeUri(directive.uri);
+ }
+ for (final directive in unlinked2.exports) {
+ addRelativeUri(directive.uri);
+ }
+ for (final uri in unlinked2.parts) {
+ addRelativeUri(uri);
+ }
+
+ prefetchFiles(paths.toList());
}
/// TODO(scheglov) move to _fsState?
@@ -893,6 +979,29 @@
),
);
}
+
+ final topLevelDeclarations = <String>{};
+ for (final declaration in unit.declarations) {
+ if (declaration is ClassDeclaration) {
+ topLevelDeclarations.add(declaration.name.name);
+ } else if (declaration is EnumDeclaration) {
+ topLevelDeclarations.add(declaration.name.name);
+ } else if (declaration is ExtensionDeclaration) {
+ var name = declaration.name;
+ if (name != null) {
+ topLevelDeclarations.add(name.name);
+ }
+ } else if (declaration is FunctionDeclaration) {
+ topLevelDeclarations.add(declaration.name.name);
+ } else if (declaration is MixinDeclaration) {
+ topLevelDeclarations.add(declaration.name.name);
+ } else if (declaration is TopLevelVariableDeclaration) {
+ for (var variable in declaration.variables.variables) {
+ topLevelDeclarations.add(variable.name.name);
+ }
+ }
+ }
+
return UnlinkedUnit(
apiSignature: Uint8List.fromList(computeUnlinkedApiSignature(unit)),
augmentations: augmentations,
@@ -906,6 +1015,7 @@
parts: parts,
partOfNameDirective: partOfNameDirective,
partOfUriDirective: partOfUriDirective,
+ topLevelDeclarations: topLevelDeclarations,
);
}
@@ -1022,12 +1132,20 @@
/// The value of this field is incremented when the set of files is updated.
int fileStamp = 0;
- /// The cache of content of files, possibly shared with other file system
- /// states.
- final FileContentCache _fileContentCache;
+ final FileContentStrategy fileContentStrategy;
+
+ /// A function that fetches the given list of files. This function can be used
+ /// to batch file reads in systems where file fetches are expensive.
+ final void Function(List<String> paths)? prefetchFiles;
+
+ /// A function that returns true if the given file path is likely to be that
+ /// of a file that is generated.
+ final bool Function(String path) isGenerated;
late final FileSystemStateTestView _testView;
+ FileSystemTestData? testData;
+
FileSystemState(
this._logger,
this._byteStore,
@@ -1041,8 +1159,10 @@
this._saltForUnlinked,
this._saltForElements,
this.featureSetProvider, {
- required FileContentCache fileContentCache,
- }) : _fileContentCache = fileContentCache {
+ required this.fileContentStrategy,
+ required this.prefetchFiles,
+ required this.isGenerated,
+ }) {
_testView = FileSystemStateTestView(this);
}
@@ -1051,6 +1171,29 @@
@visibleForTesting
FileSystemStateTestView get test => _testView;
+ /// Update the state to reflect the fact that the file with the given [path]
+ /// was changed. Specifically this means that we evict this file and every
+ /// file that referenced it.
+ void changeFile(String path, List<FileState> removedFiles) {
+ var file = _pathToFile.remove(path);
+ if (file == null) {
+ return;
+ }
+
+ removedFiles.add(file);
+ _uriToFile.remove(file.uri);
+
+ // The removed file does not reference other file anymore.
+ for (var referencedFile in file.directReferencedFiles) {
+ referencedFile.referencingFiles.remove(file);
+ }
+
+ // Recursively remove files that reference the removed file.
+ for (var reference in file.referencingFiles.toList()) {
+ changeFile(reference.path, removedFiles);
+ }
+ }
+
/// Collected files that transitively reference a file with the [path].
/// These files are potentially affected by the change.
void collectAffected(String path, Set<FileState> affected) {
@@ -1104,9 +1247,40 @@
return featureSetProvider.getLanguageVersion(path, uri);
}
+ /// Notifies this object that it is about to be discarded.
+ ///
+ /// Returns the keys of the artifacts that are no longer used.
+ Set<String> dispose() {
+ final result = <String>{};
+ for (final file in _pathToFile.values) {
+ result.add(file._unlinkedKey!);
+ }
+ _pathToFile.clear();
+ _uriToFile.clear();
+ return result;
+ }
+
+ @visibleForTesting
+ FileState? getExistingFileForResource(File file) {
+ return _pathToFile[file.path];
+ }
+
/// Return the [FileState] for the given absolute [path]. The returned file
/// has the last known state since if was last refreshed.
+ /// TODO(scheglov) Merge with [getFileForPath2].
FileState getFileForPath(String path) {
+ return getFileForPath2(
+ path: path,
+ performance: OperationPerformanceImpl('<root>'),
+ );
+ }
+
+ /// Return the [FileState] for the given absolute [path]. The returned file
+ /// has the last known state since if was last refreshed.
+ FileState getFileForPath2({
+ required String path,
+ required OperationPerformanceImpl performance,
+ }) {
var file = _pathToFile[path];
if (file == null) {
File resource = _resourceProvider.getFile(path);
@@ -1164,12 +1338,38 @@
return Either2.t1(file);
}
+ /// Returns a list of files whose contents contains the given string.
+ /// Generated files are not included in the search.
+ List<String> getFilesContaining(String value) {
+ var result = <String>[];
+ _pathToFile.forEach((path, file) {
+ // TODO(scheglov) tests for excluding generated
+ if (!isGenerated(path)) {
+ if (file.getContent().contains(value)) {
+ result.add(path);
+ }
+ }
+ });
+ return result;
+ }
+
/// Return files where the given [name] is subtyped, i.e. used in `extends`,
/// `with` or `implements` clauses.
Set<FileState>? getFilesSubtypingName(String name) {
return _subtypedNameToFiles[name];
}
+ /// Return files that have a top-level declaration with the [name].
+ List<FileState> getFilesWithTopLevelDeclarations(String name) {
+ final result = <FileState>[];
+ for (final file in _pathToFile.values) {
+ if (file.unlinked2.topLevelDeclarations.contains(name)) {
+ result.add(file);
+ }
+ }
+ return result;
+ }
+
/// Return `true` if there is a URI that can be resolved to the [path].
///
/// When a file exists, but for the URI that corresponds to the file is
@@ -1186,18 +1386,34 @@
return flag;
}
- /// The file with the given [path] might have changed, so ensure that it is
- /// read the next time it is refreshed.
- void markFileForReading(String path) {
- _fileContentCache.invalidate(path);
- }
-
/// Remove the file with the given [path].
void removeFile(String path) {
- markFileForReading(path);
_clearFiles();
}
+ /// Computes the set of [FileState]'s used/not used to analyze the given
+ /// [files]. Removes the [FileState]'s of the files not used for analysis from
+ /// the cache. Returns the set of unused [FileState]'s.
+ List<FileState> removeUnusedFiles(List<String> files) {
+ var allReferenced = <String>{};
+ for (var path in files) {
+ allReferenced.add(path);
+ _pathToFile[path]?.collectAllReferencedFiles(allReferenced);
+ }
+
+ var unusedPaths = _pathToFile.keys.toSet();
+ unusedPaths.removeAll(allReferenced);
+
+ var removedFiles = <FileState>[];
+ for (var path in unusedPaths) {
+ var file = _pathToFile.remove(path)!;
+ _uriToFile.remove(file.uri);
+ removedFiles.add(file);
+ }
+
+ return removedFiles;
+ }
+
/// Clear all [FileState] data - all maps from path or URI, etc.
void _clearFiles() {
_uriToFile.clear();
@@ -1240,6 +1456,26 @@
}
}
+class FileSystemTestData {
+ final Map<File, FileTestData> files = {};
+
+ FileTestData forFile(File file) {
+ return files[file] ??= FileTestData._(file);
+ }
+}
+
+class FileTestData {
+ final File file;
+
+ /// We add the key every time we get unlinked data from the byte store.
+ final List<String> unlinkedKeyGet = [];
+
+ /// We add the key every time we put unlinked data into the byte store.
+ final List<String> unlinkedKeyPut = [];
+
+ FileTestData._(this.file);
+}
+
/// Precomputed properties of a file URI, used because [Uri] is relatively
/// expensive to work with, if we do this thousand times.
class FileUriProperties {
@@ -1471,6 +1707,8 @@
/// first one as if sorted by path.
@override
LibraryFileStateKind? get library {
+ _discoverLibraries();
+
LibraryFileStateKind? result;
for (final library in libraries) {
if (library.hasPart(this)) {
@@ -1483,6 +1721,27 @@
}
return result;
}
+
+ void _discoverLibraries() {
+ if (libraries.isEmpty) {
+ var resourceProvider = file._fsState._resourceProvider;
+ var pathContext = resourceProvider.pathContext;
+
+ var siblings = <Resource>[];
+ try {
+ siblings = file.resource.parent.getChildren();
+ } catch (_) {}
+
+ for (final sibling in siblings) {
+ if (file_paths.isDart(pathContext, sibling.path)) {
+ file._fsState.getFileForPath2(
+ path: sibling.path,
+ performance: OperationPerformanceImpl('<root>'),
+ );
+ }
+ }
+ }
+ }
}
/// The file has `part of URI` directive.
@@ -1528,6 +1787,45 @@
LibraryFileStateKind? get library => null;
}
+class StoredFileContent implements FileContent {
+ @override
+ final String content;
+
+ @override
+ final String contentHash;
+
+ @override
+ final bool exists;
+
+ StoredFileContent({
+ required this.content,
+ required this.contentHash,
+ required this.exists,
+ });
+}
+
+class StoredFileContentStrategy implements FileContentStrategy {
+ final FileContentCache _fileContentCache;
+
+ StoredFileContentStrategy(this._fileContentCache);
+
+ @override
+ FileContent get(String path) {
+ final fileContent = _fileContentCache.get(path);
+ return StoredFileContent(
+ content: fileContent.content,
+ contentHash: fileContent.contentHash,
+ exists: fileContent.exists,
+ );
+ }
+
+ /// The file with the given [path] might have changed, so ensure that it is
+ /// read the next time it is refreshed.
+ void markFileForReading(String path) {
+ _fileContentCache.invalidate(path);
+ }
+}
+
class _LibraryNameToFiles {
final Map<String, List<LibraryFileStateKind>> _map = {};
diff --git a/pkg/analyzer/lib/src/dart/analysis/file_tracker.dart b/pkg/analyzer/lib/src/dart/analysis/file_tracker.dart
index 7371c3f..e1606ce 100644
--- a/pkg/analyzer/lib/src/dart/analysis/file_tracker.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/file_tracker.dart
@@ -22,6 +22,8 @@
/// The current file system state.
final FileSystemState _fsState;
+ final StoredFileContentStrategy _fileContentStrategy;
+
/// The set of added files.
final addedFiles = <String>{};
@@ -45,7 +47,7 @@
/// have any special relation with changed files.
var _pendingFiles = <String>{};
- FileTracker(this._logger, this._fsState);
+ FileTracker(this._logger, this._fsState, this._fileContentStrategy);
/// Returns the path to exactly one that needs analysis. Throws a
/// [StateError] if no files need analysis.
@@ -108,7 +110,7 @@
/// Adds the given [path] to the set of "changed files".
void changeFile(String path) {
- _fsState.markFileForReading(path);
+ _fileContentStrategy.markFileForReading(path);
_changedFiles.add(path);
if (addedFiles.contains(path)) {
@@ -137,6 +139,7 @@
/// Removes the given [path] from the set of "added files".
void removeFile(String path) {
+ _fileContentStrategy.markFileForReading(path);
addedFiles.remove(path);
_pendingChangedFiles.remove(path);
_pendingImportFiles.remove(path);
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_graph.dart b/pkg/analyzer/lib/src/dart/analysis/library_graph.dart
index 95957aa..89f3f5f 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_graph.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_graph.dart
@@ -52,6 +52,10 @@
/// include [implSignature] of the macro defining library.
String implSignature;
+ /// The key of the resolution cache entry.
+ /// TODO(scheglov) clean up
+ String? resolutionKey;
+
late final bool hasMacroClass = () {
for (final library in libraries) {
for (final file in library.libraryFiles) {
diff --git a/pkg/analyzer/lib/src/dart/analysis/unlinked_data.dart b/pkg/analyzer/lib/src/dart/analysis/unlinked_data.dart
index d9a7714..18152a3 100644
--- a/pkg/analyzer/lib/src/dart/analysis/unlinked_data.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/unlinked_data.dart
@@ -343,6 +343,9 @@
/// The `part of 'uri';` directive.
final UnlinkedPartOfUriDirective? partOfUriDirective;
+ /// Top-level declarations of the unit.
+ final Set<String> topLevelDeclarations;
+
UnlinkedUnit({
required this.apiSignature,
required this.augmentations,
@@ -356,6 +359,7 @@
required this.parts,
required this.partOfNameDirective,
required this.partOfUriDirective,
+ required this.topLevelDeclarations,
});
factory UnlinkedUnit.read(SummaryDataReader reader) {
@@ -388,6 +392,7 @@
partOfUriDirective: reader.readOptionalObject(
UnlinkedPartOfUriDirective.read,
),
+ topLevelDeclarations: reader.readStringUtf8Set(),
);
}
@@ -424,5 +429,6 @@
partOfUriDirective,
(x) => x.write(sink),
);
+ sink.writeStringUtf8Iterable(topLevelDeclarations);
}
}
diff --git a/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart b/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
index f02eacb..f81ea9f 100644
--- a/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
@@ -9,6 +9,7 @@
import 'package:analyzer/error/error.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/context/source.dart';
+import 'package:analyzer/src/dart/analysis/file_state.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/dart/constant/compute.dart';
@@ -19,7 +20,6 @@
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
import 'package:analyzer/src/dart/element/type_provider.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
-import 'package:analyzer/src/dart/micro/library_graph.dart';
import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart';
import 'package:analyzer/src/dart/resolver/legacy_type_asserter.dart';
import 'package:analyzer/src/dart/resolver/resolution_visitor.dart';
@@ -106,7 +106,8 @@
// Parse all files.
performance.run('parse', (performance) {
- for (FileState file in _library.files().ofLibrary) {
+ final libraryKind = _library.kind.asLibrary;
+ for (FileState file in libraryKind.file.libraryFiles) {
if (completionPath == null || file.path == completionPath) {
units[file] = _parse(
file: file,
@@ -227,21 +228,24 @@
});
}
+ final libraryKind = _library.kind.asLibrary;
+ final libraryFiles = libraryKind.file.libraryFiles.toList();
+
if (_analysisOptions.lint) {
performance.run('computeLints', (performance) {
- var allUnits = _library.files().ofLibrary.map((file) {
+ var allUnits = libraryFiles.map((file) {
var content = getFileContent(file);
return LinterContextUnit(content, units[file]!);
}).toList();
for (int i = 0; i < allUnits.length; i++) {
- _computeLints(_library.files().ofLibrary[i], allUnits[i], allUnits);
+ _computeLints(libraryFiles[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.files().ofLibrary) {
+ for (var file in libraryFiles) {
IgnoreValidator(
_getErrorReporter(file),
_getErrorListener(file).errors,
@@ -475,7 +479,8 @@
}
bool _isExistingSource(Source source) {
- for (var file in _library.files().directReferencedFiles) {
+ final libraryKind = _library.kind.asLibrary;
+ for (var file in libraryKind.file.directReferencedFiles) {
if (file.uri == source.uri) {
return file.exists;
}
@@ -499,7 +504,7 @@
performance.getDataInt('length').add(content.length);
AnalysisErrorListener errorListener = _getErrorListener(file);
- var unit = file.parse(errorListener, content);
+ var unit = file.parse2(errorListener, content);
LineInfo lineInfo = unit.lineInfo;
_fileToLineInfo[file] = lineInfo;
@@ -544,6 +549,8 @@
return;
}
+ final libraryKind = _library.kind.asLibrary;
+
var definingCompilationUnit = units[_library]!;
definingCompilationUnit.element = _libraryElement.definingCompilationUnit;
@@ -598,7 +605,7 @@
} else if (directive is PartDirectiveImpl) {
StringLiteral partUri = directive.uri;
- FileState partFile = _library.files().parted[partIndex];
+ FileState partFile = libraryKind.file.partedFiles[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 12c250f..c182ed9 100644
--- a/pkg/analyzer/lib/src/dart/micro/library_graph.dart
+++ b/pkg/analyzer/lib/src/dart/micro/library_graph.dart
@@ -1045,6 +1045,7 @@
parts: parts,
partOfNameDirective: partOfNameDirective,
partOfUriDirective: partOfUriDirective,
+ topLevelDeclarations: topLevelDeclarations,
);
return CiderUnlinkedUnit(
diff --git a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
index 92f57a8..cb43de1 100644
--- a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
+++ b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
@@ -5,6 +5,7 @@
import 'dart:collection';
import 'dart:typed_data';
+import 'package:analyzer/dart/analysis/declared_variables.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/error/error.dart';
@@ -19,14 +20,14 @@
import 'package:analyzer/src/dart/analysis/driver.dart' show ErrorEncoding;
import 'package:analyzer/src/dart/analysis/experiments.dart';
import 'package:analyzer/src/dart/analysis/feature_set_provider.dart';
+import 'package:analyzer/src/dart/analysis/file_state.dart';
+import 'package:analyzer/src/dart/analysis/library_graph.dart';
import 'package:analyzer/src/dart/analysis/performance_logger.dart';
import 'package:analyzer/src/dart/analysis/results.dart';
import 'package:analyzer/src/dart/analysis/search.dart';
import 'package:analyzer/src/dart/micro/analysis_context.dart';
import 'package:analyzer/src/dart/micro/library_analyzer.dart';
-import 'package:analyzer/src/dart/micro/library_graph.dart';
import 'package:analyzer/src/dart/micro/utils.dart';
-import 'package:analyzer/src/exception/exception.dart';
import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/summary/api_signature.dart';
@@ -43,6 +44,75 @@
import 'package:meta/meta.dart';
import 'package:yaml/yaml.dart';
+class CiderFileContent implements FileContent {
+ final CiderFileContentStrategy strategy;
+ final String path;
+ final String digestStr;
+
+ CiderFileContent({
+ required this.strategy,
+ required this.path,
+ required this.digestStr,
+ });
+
+ @override
+ String get content {
+ final contentWithDigest = _getContent();
+
+ if (contentWithDigest.digestStr != digestStr) {
+ throw StateError('File was changed, but not invalidated: $path');
+ }
+
+ return contentWithDigest.content;
+ }
+
+ @override
+ String get contentHash => digestStr;
+
+ @override
+ bool get exists => digestStr.isNotEmpty;
+
+ _ContentWithDigest _getContent() {
+ String content;
+ try {
+ final file = strategy.resourceProvider.getFile(path);
+ content = file.readAsStringSync();
+ } catch (_) {
+ content = '';
+ }
+
+ final digestStr = strategy.getFileDigest(path);
+ return _ContentWithDigest(
+ content: content,
+ digestStr: digestStr,
+ );
+ }
+}
+
+class CiderFileContentStrategy implements FileContentStrategy {
+ final ResourceProvider resourceProvider;
+
+ /// A function that returns the digest for a file as a String. The function
+ /// returns a non null value, returns an empty string if file does
+ /// not exist/has no contents.
+ final String Function(String path) getFileDigest;
+
+ CiderFileContentStrategy({
+ required this.resourceProvider,
+ required this.getFileDigest,
+ });
+
+ @override
+ CiderFileContent get(String path) {
+ final digestStr = getFileDigest(path);
+ return CiderFileContent(
+ strategy: this,
+ path: path,
+ digestStr: digestStr,
+ );
+ }
+}
+
class CiderSearchInfo {
final CharacterLocation startPosition;
final int length;
@@ -97,7 +167,7 @@
/// A function that returns true if the given file path is likely to be that
/// of a file that is generated.
- final bool Function(String path)? isGenerated;
+ final bool Function(String path) isGenerated;
/// A function that fetches the given list of files. This function can be used
/// to batch file reads in systems where file fetches are expensive.
@@ -138,38 +208,15 @@
required this.getFileDigest,
required this.prefetchFiles,
required this.workspace,
- this.isGenerated,
+ required this.isGenerated,
required this.byteStore,
});
- @Deprecated('Use the unnamed constructor instead')
- FileResolver.from({
- required this.logger,
- required this.resourceProvider,
- required this.sourceFactory,
- required this.getFileDigest,
- required this.prefetchFiles,
- required this.workspace,
- this.isGenerated,
- required this.byteStore,
- });
-
- /// Update the resolver to reflect the fact that the file with the given
- /// [path] was changed. We need to make sure that when this file, of any file
- /// that directly or indirectly referenced it, is resolved, we used the new
- /// state of the file. Updates [removedCacheKeys] with the ids of the invalidated
- /// items, used in [releaseAndClearRemovedIds] to release the cache items.
- /// TODO(scheglov) Remove [releaseKeys] when removing [changeFile].
- @Deprecated('Use changeFiles() instead')
- void changeFile(String path) {
- changeFiles([path], releaseKeys: false);
- }
-
/// Update the resolver to reflect the fact that the files with the given
/// [paths] were changed. For each specified file we need to make sure that
/// when the file, of any file that directly or indirectly referenced it,
/// is resolved, we use the new state of the file.
- void changeFiles(List<String> paths, {bool releaseKeys = true}) {
+ void changeFiles(List<String> paths) {
if (fsState == null) {
return;
}
@@ -192,9 +239,7 @@
// If we need these libraries later, we will relink and reattach them.
libraryContext?.remove(removedFiles, removedCacheKeys);
- if (releaseKeys) {
- releaseAndClearRemovedIds();
- }
+ releaseAndClearRemovedIds();
}
/// Collects all the cached artifacts and add all the cache id's for the
@@ -276,8 +321,8 @@
var file = fileContext.file;
final errorsSignatureBuilder = ApiSignature();
- errorsSignatureBuilder.addBytes(file.libraryCycle.signature);
- errorsSignatureBuilder.addBytes(file.digest);
+ errorsSignatureBuilder.addString(file.libraryCycle.apiSignature);
+ errorsSignatureBuilder.addString(file.contentHash);
final errorsKey = '${errorsSignatureBuilder.toHex()}.errors';
final List<AnalysisError> errors;
@@ -330,7 +375,7 @@
});
var file = performance.run('fileForPath', (performance) {
- return fsState!.getFileForPath(
+ return fsState!.getFileForPath2(
path: path,
performance: performance,
);
@@ -368,7 +413,8 @@
);
var file = fileContext.file;
- if (file.partOfLibrary != null) {
+ final kind = file.kind;
+ if (kind is! LibraryFileStateKind) {
throw ArgumentError('$uri is not a library.');
}
@@ -388,12 +434,12 @@
}) {
_throwIfNotAbsoluteNormalizedPath(path);
- var file = fsState!.getFileForPath(
+ var file = fsState!.getFileForPath2(
path: path,
performance: performance,
);
- return file.libraryCycle.signatureStr;
+ return file.libraryCycle.apiSignature;
}
/// Ensure that libraries necessary for resolving [path] are linked.
@@ -427,7 +473,7 @@
performance: performance,
);
var file = fileContext.file;
- var libraryFile = file.partOfLibrary ?? file;
+ var libraryFile = file.kind.library!.file;
// Load the library, link if necessary.
await libraryContext!.load(
@@ -486,15 +532,17 @@
);
var file = fileContext.file;
- // If we have a `part of` directive, we want to analyze this library.
- // But the library must include the file, so have its element.
- var libraryFile = file;
- var partOfLibrary = file.partOfLibrary;
- if (partOfLibrary != null) {
- if (partOfLibrary.files().ofLibrary.contains(file)) {
- libraryFile = partOfLibrary;
- }
- }
+ // // If we have a `part of` directive, we want to analyze this library.
+ // // But the library must include the file, so have its element.
+ // var libraryFile = file;
+ // var partOfLibrary = file.partOfLibrary;
+ // if (partOfLibrary != null) {
+ // if (partOfLibrary.files().ofLibrary.contains(file)) {
+ // libraryFile = partOfLibrary;
+ // }
+ // }
+ final libraryKind = file.kind.library ?? file.kind.asLibrary;
+ final libraryFile = libraryKind.file;
var libraryResult = await resolveLibrary2(
completionLine: completionLine,
@@ -533,15 +581,17 @@
);
var file = fileContext.file;
- // If we have a `part of` directive, we want to analyze this library.
- // But the library must include the file, so have its element.
- var libraryFile = file;
- var partOfLibrary = file.partOfLibrary;
- if (partOfLibrary != null) {
- if (partOfLibrary.files().ofLibrary.contains(file)) {
- libraryFile = partOfLibrary;
- }
- }
+ // // If we have a `part of` directive, we want to analyze this library.
+ // // But the library must include the file, so have its element.
+ // var libraryFile = file;
+ // var partOfLibrary = file.partOfLibrary;
+ // if (partOfLibrary != null) {
+ // if (partOfLibrary.files().ofLibrary.contains(file)) {
+ // libraryFile = partOfLibrary;
+ // }
+ // }
+ final libraryKind = file.kind.library ?? file.kind.asLibrary;
+ final libraryFile = libraryKind.file;
int? completionOffset;
if (completionLine != null && completionColumn != null) {
@@ -573,26 +623,13 @@
(file) => file.getContent(),
);
- try {
- results = performance!.run('analyze', (performance) {
- return libraryAnalyzer.analyze(
- completionPath: completionOffset != null ? completionPath : null,
- completionOffset: completionOffset,
- performance: performance,
- );
- });
- } catch (exception, stackTrace) {
- var fileContentMap = <String, String>{};
- for (var file in libraryFile.files().ofLibrary) {
- var path = file.path;
- fileContentMap[path] = _getFileContent(path);
- }
- throw CaughtExceptionWithFiles(
- exception,
- stackTrace,
- fileContentMap,
+ results = performance!.run('analyze', (performance) {
+ return libraryAnalyzer.analyze(
+ completionPath: completionOffset != null ? completionPath : null,
+ completionOffset: completionOffset,
+ performance: performance,
);
- }
+ });
});
var resolvedUnits = results.values.map((fileResult) {
@@ -655,15 +692,23 @@
);
fsState = FileSystemState(
- resourceProvider,
+ logger,
byteStore,
+ resourceProvider,
+ 'contextName',
sourceFactory,
workspace,
- Uint32List(0), // linkedSalt
+ AnalysisOptionsImpl(), // TODO(scheglov) remove it
+ DeclaredVariables.fromMap({}),
+ Uint32List(0), // _saltForUnlinked
+ Uint32List(0), // _saltForElements
featureSetProvider,
- getFileDigest,
- prefetchFiles,
- isGenerated,
+ fileContentStrategy: CiderFileContentStrategy(
+ resourceProvider: resourceProvider,
+ getFileDigest: getFileDigest,
+ ),
+ prefetchFiles: prefetchFiles,
+ isGenerated: isGenerated,
)..testData = testView?.fileSystemTestData;
}
@@ -779,15 +824,6 @@
return options;
}
- /// Return the file content, the empty string if any exception.
- String _getFileContent(String path) {
- try {
- return resourceProvider.getFile(path).readAsStringSync();
- } catch (_) {
- return '';
- }
- }
-
Future<List<CiderSearchMatch>> _searchReferences_Import(
ImportElement element) async {
var results = <CiderSearchMatch>[];
@@ -897,14 +933,13 @@
await loadBundle(directDependency);
}
- var resolutionKey = '${cycle.signatureStr}.resolution';
+ var resolutionKey = '${cycle.apiSignature}.linked_bundle';
var resolutionBytes = byteStore.get(resolutionKey);
var unitsInformativeBytes = <Uri, Uint8List>{};
for (var library in cycle.libraries) {
- for (var file in library.files().ofLibrary) {
- var informativeBytes = file.unlinkedUnit.informativeBytes;
- unitsInformativeBytes[file.uri] = informativeBytes;
+ for (var file in library.libraryFiles) {
+ unitsInformativeBytes[file.uri] = file.unlinked2.informativeBytes;
}
}
@@ -918,21 +953,21 @@
var inputUnits = <LinkInputUnit>[];
var partIndex = -1;
- for (var file in libraryFile.files().ofLibrary) {
+ for (var file in libraryFile.libraryFiles) {
var isSynthetic = !file.exists;
var content = file.getContent();
performance.getDataInt('parseCount').increment();
performance.getDataInt('parseLength').add(content.length);
- var unit = file.parse(
+ var unit = file.parse2(
AnalysisErrorListener.NULL_LISTENER,
content,
);
String? partUriStr;
if (partIndex >= 0) {
- partUriStr = libraryFile.unlinkedUnit.parts[partIndex];
+ partUriStr = libraryFile.unlinked2.parts[partIndex];
}
partIndex++;
@@ -1069,3 +1104,13 @@
final List<String> getKeys = [];
final List<String> putKeys = [];
}
+
+class _ContentWithDigest {
+ final String content;
+ final String digestStr;
+
+ _ContentWithDigest({
+ required this.content,
+ required this.digestStr,
+ });
+}
diff --git a/pkg/analyzer/lib/src/generated/engine.dart b/pkg/analyzer/lib/src/generated/engine.dart
index ebe16a8..0fc9372 100644
--- a/pkg/analyzer/lib/src/generated/engine.dart
+++ b/pkg/analyzer/lib/src/generated/engine.dart
@@ -70,22 +70,12 @@
/// context. Clients should not modify the returned set of options.
AnalysisOptions get analysisOptions;
- /// Set the set of analysis options controlling the behavior of this context to
- /// the given [options]. Clients can safely assume that all necessary analysis
- /// results have been invalidated.
- set analysisOptions(AnalysisOptions options);
-
/// Return the set of declared variables used when computing constant values.
DeclaredVariables get declaredVariables;
/// Return the source factory used to create the sources that can be analyzed
/// in this context.
SourceFactory get sourceFactory;
-
- /// Set the source factory used to create the sources that can be analyzed in
- /// this context to the given source [factory]. Clients can safely assume that
- /// all analysis results have been invalidated.
- set sourceFactory(SourceFactory factory);
}
/// The entry point for the functionality provided by the analysis engine. There
diff --git a/pkg/analyzer/lib/src/services/lint.dart b/pkg/analyzer/lib/src/services/lint.dart
index 49603d43..05dff99 100644
--- a/pkg/analyzer/lib/src/services/lint.dart
+++ b/pkg/analyzer/lib/src/services/lint.dart
@@ -7,7 +7,6 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/error/lint_codes.dart';
-import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/lint/linter.dart';
/// Current linter version.
@@ -16,19 +15,6 @@
/// Shared lint registry.
LintRegistry lintRegistry = LintRegistry();
-/// Return lints associated with this [context], or an empty list if there are
-/// none.
-List<Linter> getLints(AnalysisContext context) =>
- context.analysisOptions.lintRules;
-
-/// Associate these [lints] with the given [context].
-void setLints(AnalysisContext context, List<Linter> lints) {
- AnalysisOptionsImpl options =
- AnalysisOptionsImpl.from(context.analysisOptions);
- options.lintRules = lints;
- context.analysisOptions = options;
-}
-
/// Implementers contribute lint warnings via the provided error [reporter].
abstract class Linter implements NodeLintRule {
/// Used to report lint warnings.
diff --git a/pkg/analyzer/test/source/error_processor_test.dart b/pkg/analyzer/test/source/error_processor_test.dart
index c9ec408..20bbac0 100644
--- a/pkg/analyzer/test/source/error_processor_test.dart
+++ b/pkg/analyzer/test/source/error_processor_test.dart
@@ -2,15 +2,11 @@
// 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:analyzer/dart/analysis/declared_variables.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/source/error_processor.dart';
import 'package:analyzer/src/analysis_options/analysis_options_provider.dart';
-import 'package:analyzer/src/context/context.dart';
-import 'package:analyzer/src/dart/analysis/session.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/engine.dart';
-import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/task/options.dart';
import 'package:collection/collection.dart';
import 'package:test/test.dart';
@@ -44,13 +40,15 @@
AnalysisError annotate_overrides =
AnalysisError(TestSource(), 0, 1, LintCode('annotate_overrides', ''));
- setUp(() {
- context = TestContext();
- });
-
group('ErrorProcessor', () {
+ late _TestContext context;
+
+ setUp(() {
+ context = _TestContext();
+ });
+
test('configureOptions', () {
- configureOptions('''
+ context.configureOptions('''
analyzer:
errors:
invalid_assignment: error # severity ERROR
@@ -58,18 +56,19 @@
unused_local_variable: true # skipped
use_of_void_result: unsupported_action # skipped
''');
- expect(getProcessor(invalid_assignment)!.severity, ErrorSeverity.ERROR);
- expect(getProcessor(missing_return)!.severity, isNull);
- expect(getProcessor(unused_local_variable), isNull);
- expect(getProcessor(use_of_void_result), isNull);
+ expect(context.getProcessor(invalid_assignment)!.severity,
+ ErrorSeverity.ERROR);
+ expect(context.getProcessor(missing_return)!.severity, isNull);
+ expect(context.getProcessor(unused_local_variable), isNull);
+ expect(context.getProcessor(use_of_void_result), isNull);
});
test('does not upgrade other warnings to errors in strong mode', () {
- configureOptions('''
+ context.configureOptions('''
analyzer:
strong-mode: true
''');
- expect(getProcessor(unused_local_variable), isNull);
+ expect(context.getProcessor(unused_local_variable), isNull);
});
});
@@ -84,7 +83,7 @@
group('processing', () {
test('yaml map', () {
- var options = optionsProvider.getOptionsFromString(config);
+ var options = AnalysisOptionsProvider().getOptionsFromString(config);
var errorConfig =
ErrorConfig((options['analyzer'] as YamlMap)['errors']);
expect(errorConfig.processors, hasLength(2));
@@ -132,7 +131,7 @@
});
test('configure lints', () {
- var options = optionsProvider.getOptionsFromString(
+ var options = AnalysisOptionsProvider().getOptionsFromString(
'analyzer:\n errors:\n annotate_overrides: warning\n');
var errorConfig = ErrorConfig((options['analyzer'] as YamlMap)['errors']);
expect(errorConfig.processors, hasLength(1));
@@ -144,30 +143,15 @@
});
}
-late TestContext context;
+class _TestContext {
+ final analysisOptions = AnalysisOptionsImpl();
-AnalysisOptionsProvider optionsProvider = AnalysisOptionsProvider();
+ void configureOptions(String options) {
+ final optionMap = AnalysisOptionsProvider().getOptionsFromString(options);
+ applyToAnalysisOptions(analysisOptions, optionMap);
+ }
-void configureOptions(String options) {
- YamlMap optionMap = optionsProvider.getOptionsFromString(options);
- applyToAnalysisOptions(context.analysisOptions, optionMap);
-}
-
-ErrorProcessor? getProcessor(AnalysisError error) =>
- ErrorProcessor.getProcessor(context.analysisOptions, error);
-
-class TestContext extends AnalysisContextImpl {
- TestContext()
- : super(
- SynchronousSession(
- AnalysisOptionsImpl(),
- DeclaredVariables(),
- ),
- _SourceFactoryMock(),
- );
-}
-
-class _SourceFactoryMock implements SourceFactory {
- @override
- noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
+ ErrorProcessor? getProcessor(AnalysisError error) {
+ return ErrorProcessor.getProcessor(analysisOptions, error);
+ }
}
diff --git a/pkg/analyzer/test/src/dart/analysis/file_state_test.dart b/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
index 690c472..403a89e 100644
--- a/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
@@ -1136,10 +1136,11 @@
});
}
+ /// TODO(scheglov) Test discovery of a sibling library
test_newFile_partOfName() async {
- final a = newFile('$testPackageLibPath/a.dart', r'''
+ final a = newFile('$testPackageLibPath/nested/a.dart', r'''
library my.lib;
-part 'b.dart';
+part '../b.dart';
''');
final b = newFile('$testPackageLibPath/b.dart', r'''
@@ -2239,7 +2240,11 @@
Uint32List(0),
Uint32List(0),
featureSetProvider,
- fileContentCache: FileContentCache.ephemeral(resourceProvider),
+ fileContentStrategy: StoredFileContentStrategy(
+ FileContentCache.ephemeral(resourceProvider),
+ ),
+ prefetchFiles: null,
+ isGenerated: (_) => false,
);
}
diff --git a/pkg/analyzer/test/src/dart/micro/file_resolution.dart b/pkg/analyzer/test/src/dart/micro/file_resolution.dart
index 4aedd00..db6d5af 100644
--- a/pkg/analyzer/test/src/dart/micro/file_resolution.dart
+++ b/pkg/analyzer/test/src/dart/micro/file_resolution.dart
@@ -7,8 +7,8 @@
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/dart/analysis/byte_store.dart';
+import 'package:analyzer/src/dart/analysis/file_state.dart';
import 'package:analyzer/src/dart/analysis/performance_logger.dart';
-import 'package:analyzer/src/dart/micro/library_graph.dart';
import 'package:analyzer/src/dart/micro/resolve_file.dart';
import 'package:analyzer/src/dart/sdk/sdk.dart';
import 'package:analyzer/src/test_utilities/find_element.dart';
@@ -96,7 +96,7 @@
getFileDigest: (String path) => _getDigest(path),
workspace: workspace,
prefetchFiles: null,
- isGenerated: null,
+ isGenerated: (_) => false,
);
fileResolver.testView = testData;
}
diff --git a/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart b/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
index 1ad2d13..91d48d2a9 100644
--- a/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
+++ b/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
@@ -5,6 +5,7 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/source/line_info.dart';
+import 'package:analyzer/src/dart/analysis/file_state.dart';
import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/dart/error/syntactic_errors.dart';
import 'package:analyzer/src/dart/micro/resolve_file.dart';
@@ -362,9 +363,12 @@
}
''');
- expect(() async {
+ try {
await resolveFile(a.path);
- }, throwsStateError);
+ fail('Expected StateError');
+ } on StateError {
+ // OK
+ }
// Notify the resolver about b.dart, it is OK now.
fileResolver.changeFiles([b.path]);
@@ -2465,8 +2469,7 @@
}
test_resolve_libraryWithPart_noLibraryDiscovery() async {
- var partPath = '/workspace/dart/test/lib/a.dart';
- newFile(partPath, r'''
+ newFile('/workspace/dart/test/lib/a.dart', r'''
part of 'test.dart';
class A {}
@@ -2480,11 +2483,12 @@
// We started resolution from the library, and then followed to the part.
// So, the part knows its library, there is no need to discover it.
- _assertDiscoveredLibraryForParts([]);
+ // TODO(scheglov) Use textual dump
+ // _assertDiscoveredLibraryForParts([]);
}
test_resolve_part_of_name() async {
- newFile('/workspace/dart/test/lib/a.dart', r'''
+ final a = newFile('/workspace/dart/test/lib/a.dart', r'''
library my.lib;
part 'test.dart';
@@ -2503,11 +2507,15 @@
}
''');
- _assertDiscoveredLibraryForParts([result.path]);
+ // TODO(scheglov) Use textual dump
+ final fsState = fileResolver.fsState!;
+ final testState = fsState.getExistingFileForResource(testFile)!;
+ final testKind = testState.kind as PartFileStateKind;
+ expect(testKind.library?.file, fsState.getExistingFileForResource(a));
}
test_resolve_part_of_uri() async {
- newFile('/workspace/dart/test/lib/a.dart', r'''
+ final a = newFile('/workspace/dart/test/lib/a.dart', r'''
part 'test.dart';
class A {
@@ -2524,7 +2532,11 @@
}
''');
- _assertDiscoveredLibraryForParts([result.path]);
+ // TODO(scheglov) Use textual dump
+ final fsState = fileResolver.fsState!;
+ final testState = fsState.getExistingFileForResource(testFile)!;
+ final testKind = testState.kind as PartFileStateKind;
+ expect(testKind.library?.file, fsState.getExistingFileForResource(a));
}
test_resolveFile_cache() async {
@@ -2699,10 +2711,6 @@
]);
}
- void _assertDiscoveredLibraryForParts(List<String> expected) {
- expect(fileResolver.fsState!.testView.partsDiscoveredLibraries, expected);
- }
-
void _assertResolvedFiles(
List<File> expected, {
bool andClear = true,
diff --git a/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart b/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart
index 38b8304..1a89f2a 100644
--- a/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart
+++ b/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart
@@ -47,14 +47,16 @@
return node;
}
-/// Gets the outer-most node with the same offset/length as node.
+/// Gets the outer-most node with the same offset as node.
+///
+/// This reduces the number of nodes the visitor needs to walk when collecting
+/// navigation for a specific location in the file.
AstNode _getOutermostNode(AstNode node) {
AstNode? current = node;
while (current != null &&
current.parent != null &&
current != current.parent &&
- current.offset == current.parent!.offset &&
- current.length == current.parent!.length) {
+ current.offset == current.parent!.offset) {
current = current.parent;
}
return current ?? node;
@@ -91,12 +93,6 @@
collector.addRegion(offset, length, kind, location, targetElement: element);
}
- void _addRegion_nodeStart_nodeEnd(AstNode a, AstNode b, Element? element) {
- var offset = a.offset;
- var length = b.end - offset;
- _addRegion(offset, length, element);
- }
-
void _addRegionForNode(AstNode? node, Element? element) {
if (node == null) {
return;
@@ -265,15 +261,19 @@
@override
void visitConstructorDeclaration(ConstructorDeclaration node) {
- // associate constructor with "T" or "T.name"
- {
- AstNode firstNode = node.returnType;
- AstNode? lastNode = node.name;
- lastNode ??= firstNode;
- computer._addRegion_nodeStart_nodeEnd(
- firstNode, lastNode, node.declaredElement);
+ // For a default constructor, override the class name to be the declaration
+ // itself rather than linking to the class.
+ var name = node.name;
+ if (name == null) {
+ computer._addRegionForNode(node.returnType, node.declaredElement);
+ } else {
+ node.returnType.accept(this);
+ name.accept(this);
}
- super.visitConstructorDeclaration(node);
+ node.parameters.accept(this);
+ node.initializers.accept(this);
+ node.redirectedConstructor?.accept(this);
+ node.body.accept(this);
}
@override
@@ -292,7 +292,10 @@
name.prefix.accept(this);
className = name.identifier;
}
- computer._addRegionForNode(className, element);
+ // For a named constructor, the class name points at the class.
+ var classNameTargetElement =
+ node.name != null ? className.staticElement : element;
+ computer._addRegionForNode(className, classNameTargetElement);
}
// <TypeA, TypeB>
typeName.typeArguments?.accept(this);
@@ -418,9 +421,6 @@
@override
void visitSimpleIdentifier(SimpleIdentifier node) {
- if (node.parent is ConstructorDeclaration) {
- return;
- }
var element = node.writeOrReadElement;
computer._addRegionForNode(node, element);
}
diff --git a/pkg/compiler/lib/src/js_backend/native_data.dart b/pkg/compiler/lib/src/js_backend/native_data.dart
index 67f6246..4226f5a 100644
--- a/pkg/compiler/lib/src/js_backend/native_data.dart
+++ b/pkg/compiler/lib/src/js_backend/native_data.dart
@@ -15,7 +15,7 @@
import '../js_model/js_world_builder.dart' show identity, JsToFrontendMap;
import '../kernel/element_map.dart';
import '../native/behavior.dart' show NativeBehavior;
-import '../serialization/serialization.dart';
+import '../serialization/serialization_interfaces.dart';
import '../util/util.dart';
import 'native_data_interfaces.dart' as interfaces;
diff --git a/pkg/compiler/lib/src/native/behavior.dart b/pkg/compiler/lib/src/native/behavior.dart
index 20d3d53..545440e 100644
--- a/pkg/compiler/lib/src/native/behavior.dart
+++ b/pkg/compiler/lib/src/native/behavior.dart
@@ -17,6 +17,9 @@
import '../universe/side_effects.dart' show SideEffects;
import 'js.dart';
+import 'native_throw_behavior.dart';
+export 'native_throw_behavior.dart';
+
typedef TypeLookup = Object /*DartType|SpecialType*/
Function(String typeString, {bool required});
@@ -43,94 +46,6 @@
}
}
-/// Description of the exception behaviour of native code.
-class NativeThrowBehavior {
- static const NativeThrowBehavior NEVER = NativeThrowBehavior._(0);
- static const NativeThrowBehavior MAY = NativeThrowBehavior._(1);
-
- /// Throws only if first argument is null.
- static const NativeThrowBehavior NULL_NSM = NativeThrowBehavior._(2);
-
- /// Throws if first argument is null, then may throw.
- static const NativeThrowBehavior NULL_NSM_THEN_MAY = NativeThrowBehavior._(3);
-
- final int _bits;
- const NativeThrowBehavior._(this._bits);
-
- bool get canThrow => this != NEVER;
-
- /// Does this behavior always throw a noSuchMethod check on a null first
- /// argument before any side effect or other exception?
- bool get isNullNSMGuard => this == NULL_NSM || this == NULL_NSM_THEN_MAY;
-
- /// Does this behavior always act as a null noSuchMethod check, and has no
- /// other throwing behavior?
- bool get isOnlyNullNSMGuard => this == NULL_NSM;
-
- /// Returns the behavior if we assume the first argument is not null.
- NativeThrowBehavior get onNonNull {
- if (this == NULL_NSM) return NEVER;
- if (this == NULL_NSM_THEN_MAY) return MAY;
- return this;
- }
-
- @override
- String toString() {
- if (this == NEVER) return 'never';
- if (this == MAY) return 'may';
- if (this == NULL_NSM) return 'null(1)';
- if (this == NULL_NSM_THEN_MAY) return 'null(1)+may';
- return 'NativeThrowBehavior($_bits)';
- }
-
- /// Canonical list of marker values.
- ///
- /// Added to make [NativeThrowBehavior] enum-like.
- static const List<NativeThrowBehavior> values = [
- NEVER,
- MAY,
- NULL_NSM,
- NULL_NSM_THEN_MAY,
- ];
-
- /// Index to this marker within [values].
- ///
- /// Added to make [NativeThrowBehavior] enum-like.
- int get index => values.indexOf(this);
-
- /// Deserializer helper.
- static NativeThrowBehavior _bitsToValue(int bits) {
- switch (bits) {
- case 0:
- return NEVER;
- case 1:
- return MAY;
- case 2:
- return NULL_NSM;
- case 3:
- return NULL_NSM_THEN_MAY;
- default:
- return null;
- }
- }
-
- /// Sequence operator.
- NativeThrowBehavior then(NativeThrowBehavior second) {
- if (this == NEVER) return second;
- if (this == MAY) return MAY;
- if (this == NULL_NSM_THEN_MAY) return NULL_NSM_THEN_MAY;
- assert(this == NULL_NSM);
- if (second == NEVER) return this;
- return NULL_NSM_THEN_MAY;
- }
-
- /// Choice operator.
- NativeThrowBehavior or(NativeThrowBehavior other) {
- if (this == other) return this;
- return MAY;
- }
-}
-
/// A summary of the behavior of a native element.
///
/// Native code can return values of one type and cause native subtypes of
@@ -227,8 +142,8 @@
behavior.codeTemplateText = codeTemplateText;
behavior.codeTemplate = js.js.parseForeignJS(codeTemplateText);
}
- behavior.throwBehavior = NativeThrowBehavior._bitsToValue(throwBehavior);
- assert(behavior.throwBehavior._bits == throwBehavior);
+ behavior.throwBehavior = NativeThrowBehavior.bitsToValue(throwBehavior);
+ assert(behavior.throwBehavior.valueToBits() == throwBehavior);
behavior.isAllocation = isAllocation;
behavior.useGvn = useGvn;
return behavior;
@@ -259,7 +174,7 @@
writeTypes(typesInstantiated);
sink.writeStringOrNull(codeTemplateText);
sideEffects.writeToDataSink(sink);
- sink.writeInt(throwBehavior._bits);
+ sink.writeInt(throwBehavior.valueToBits());
sink.writeBool(isAllocation);
sink.writeBool(useGvn);
sink.end(tag);
diff --git a/pkg/compiler/lib/src/native/js.dart b/pkg/compiler/lib/src/native/js.dart
index 9c69ae7..9f44343 100644
--- a/pkg/compiler/lib/src/native/js.dart
+++ b/pkg/compiler/lib/src/native/js.dart
@@ -2,11 +2,9 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-// @dart = 2.10
-
import '../js/js.dart' as js;
import '../universe/side_effects.dart' show SideEffects;
-import 'behavior.dart';
+import 'native_throw_behavior.dart' show NativeThrowBehavior;
class HasCapturedPlaceholders extends js.BaseVisitorVoid {
HasCapturedPlaceholders._();
diff --git a/pkg/compiler/lib/src/native/native_throw_behavior.dart b/pkg/compiler/lib/src/native/native_throw_behavior.dart
new file mode 100644
index 0000000..cf50ba8
--- /dev/null
+++ b/pkg/compiler/lib/src/native/native_throw_behavior.dart
@@ -0,0 +1,93 @@
+// Copyright (c) 2014, 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.
+
+/// Description of the exception behaviour of native code.
+class NativeThrowBehavior {
+ static const NativeThrowBehavior NEVER = NativeThrowBehavior._(0);
+ static const NativeThrowBehavior MAY = NativeThrowBehavior._(1);
+
+ /// Throws only if first argument is null.
+ static const NativeThrowBehavior NULL_NSM = NativeThrowBehavior._(2);
+
+ /// Throws if first argument is null, then may throw.
+ static const NativeThrowBehavior NULL_NSM_THEN_MAY = NativeThrowBehavior._(3);
+
+ final int _bits;
+ const NativeThrowBehavior._(this._bits);
+
+ bool get canThrow => this != NEVER;
+
+ /// Does this behavior always throw a noSuchMethod check on a null first
+ /// argument before any side effect or other exception?
+ bool get isNullNSMGuard => this == NULL_NSM || this == NULL_NSM_THEN_MAY;
+
+ /// Does this behavior always act as a null noSuchMethod check, and has no
+ /// other throwing behavior?
+ bool get isOnlyNullNSMGuard => this == NULL_NSM;
+
+ /// Returns the behavior if we assume the first argument is not null.
+ NativeThrowBehavior get onNonNull {
+ if (this == NULL_NSM) return NEVER;
+ if (this == NULL_NSM_THEN_MAY) return MAY;
+ return this;
+ }
+
+ @override
+ String toString() {
+ if (this == NEVER) return 'never';
+ if (this == MAY) return 'may';
+ if (this == NULL_NSM) return 'null(1)';
+ if (this == NULL_NSM_THEN_MAY) return 'null(1)+may';
+ return 'NativeThrowBehavior($_bits)';
+ }
+
+ /// Canonical list of marker values.
+ ///
+ /// Added to make [NativeThrowBehavior] enum-like.
+ static const List<NativeThrowBehavior> values = [
+ NEVER,
+ MAY,
+ NULL_NSM,
+ NULL_NSM_THEN_MAY,
+ ];
+
+ /// Index to this marker within [values].
+ ///
+ /// Added to make [NativeThrowBehavior] enum-like.
+ int get index => values.indexOf(this);
+
+ /// Deserializer helper.
+ static NativeThrowBehavior bitsToValue(int bits) {
+ switch (bits) {
+ case 0:
+ return NEVER;
+ case 1:
+ return MAY;
+ case 2:
+ return NULL_NSM;
+ case 3:
+ return NULL_NSM_THEN_MAY;
+ default:
+ throw StateError('Unknown serialized NativeThrowBehavior: $bits');
+ }
+ }
+
+ int valueToBits() => _bits;
+
+ /// Sequence operator.
+ NativeThrowBehavior then(NativeThrowBehavior second) {
+ if (this == NEVER) return second;
+ if (this == MAY) return MAY;
+ if (this == NULL_NSM_THEN_MAY) return NULL_NSM_THEN_MAY;
+ assert(this == NULL_NSM);
+ if (second == NEVER) return this;
+ return NULL_NSM_THEN_MAY;
+ }
+
+ /// Choice operator.
+ NativeThrowBehavior or(NativeThrowBehavior other) {
+ if (this == other) return this;
+ return MAY;
+ }
+}
diff --git a/pkg/compiler/lib/src/serialization/serialization_interfaces.dart b/pkg/compiler/lib/src/serialization/serialization_interfaces.dart
index 5c2e61b..8a4ec2b 100644
--- a/pkg/compiler/lib/src/serialization/serialization_interfaces.dart
+++ b/pkg/compiler/lib/src/serialization/serialization_interfaces.dart
@@ -79,6 +79,8 @@
void writeLibrary(covariant LibraryEntity value); // IndexedLibrary
void writeLibraryOrNull(covariant LibraryEntity? value); // IndexedLibrary
+ void writeLibraryMap<V>(Map<LibraryEntity, V>? map, void f(V value),
+ {bool allowNull = false});
void writeDartTypeNode(ir.DartType value);
void writeDartTypeNodeOrNull(ir.DartType? value);
@@ -151,6 +153,8 @@
LibraryEntity readLibrary(); // IndexedLibrary;
LibraryEntity? readLibraryOrNull(); // IndexedLibrary;
+ Map<K, V> readLibraryMap<K extends LibraryEntity, V>(V f());
+ Map<K, V>? readLibraryMapOrNull<K extends LibraryEntity, V>(V f());
ir.DartType readDartTypeNode();
ir.DartType? readDartTypeNodeOrNull();
diff --git a/pkg/compiler/lib/src/serialization/sink.dart b/pkg/compiler/lib/src/serialization/sink.dart
index 0d3bef6..c3d96ab 100644
--- a/pkg/compiler/lib/src/serialization/sink.dart
+++ b/pkg/compiler/lib/src/serialization/sink.dart
@@ -729,6 +729,7 @@
///
/// This is a convenience method to be used together with
/// [DataSourceReader.readLibraryMap].
+ @override
void writeLibraryMap<V>(Map<LibraryEntity, V> map, void f(V value),
{bool allowNull = false}) {
if (map == null) {
diff --git a/pkg/compiler/lib/src/serialization/source.dart b/pkg/compiler/lib/src/serialization/source.dart
index 6136b59..287a402 100644
--- a/pkg/compiler/lib/src/serialization/source.dart
+++ b/pkg/compiler/lib/src/serialization/source.dart
@@ -945,17 +945,26 @@
return null;
}
- /// Reads a library from library entities to [V] values
- /// from this data source, calling [f] to read each value from the data
- /// source. If [emptyAsNull] is `true`, `null` is returned instead of an empty
- /// map.
+ /// Reads a library from library entities to [V] values from this data source,
+ /// calling [f] to read each value from the data source.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeLibraryMap].
- Map<K, V> readLibraryMap<K extends LibraryEntity, V>(V f(),
- {bool emptyAsNull = false}) {
+ @override
+ Map<K, V> readLibraryMap<K extends LibraryEntity, V>(V f()) {
+ return readLibraryMapOrNull<K, V>(f) ?? {};
+ }
+
+ /// Reads a library from library entities to [V] values from this data source,
+ /// calling [f] to read each value from the data source. `null` is returned
+ /// instead of an empty map.
+ ///
+ /// This is a convenience method to be used together with
+ /// [DataSinkWriter.writeLibraryMap].
+ @override
+ Map<K, V> /*?*/ readLibraryMapOrNull<K extends LibraryEntity, V>(V f()) {
int count = readInt();
- if (count == 0 && emptyAsNull) return null;
+ if (count == 0) return null;
Map<K, V> map = {};
for (int i = 0; i < count; i++) {
LibraryEntity library = readLibrary();
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index 573444a..b9c10ba 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -159,7 +159,7 @@
}
visitPostDominatorTree(HGraph graph) {
- // Recusion free version of:
+ // Recursion-free version of:
//
// void visitBasicBlockAndSuccessors(HBasicBlock block) {
// List dominated = block.dominatedBlocks;
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index 8fcda3b..e608892 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -2422,7 +2422,6 @@
final JClosedWorld closedWorld;
final SsaOptimizerTask optimizer;
HGraph _graph;
- SsaLiveBlockAnalyzer analyzer;
Map<HInstruction, bool> trivialDeadStoreReceivers = Maplet();
bool eliminatedSideEffects = false;
bool newGvnCandidates = false;
@@ -2432,14 +2431,10 @@
AbstractValueDomain get _abstractValueDomain =>
closedWorld.abstractValueDomain;
- HInstruction zapInstructionCache;
- HInstruction get zapInstruction {
- if (zapInstructionCache == null) {
- // A constant with no type does not pollute types at phi nodes.
- zapInstructionCache = analyzer.graph.addConstantUnreachable(closedWorld);
- }
- return zapInstructionCache;
- }
+ // A constant with no type does not pollute types at phi nodes.
+ HInstruction _zapInstruction;
+ HInstruction get zapInstruction =>
+ _zapInstruction ??= _graph.addConstantUnreachable(closedWorld);
/// Determines whether we can delete [instruction] because the only thing it
/// does is throw the same exception as the next instruction that throws or
@@ -2479,7 +2474,7 @@
successor = condition.constant is TrueConstantValue
? current.thenBlock
: current.elseBlock;
- assert(!analyzer.isDeadBlock(successor));
+ assert(successor.isLive);
}
}
if (successor != null && successor.id > current.block.id) {
@@ -2538,10 +2533,17 @@
@override
void visitGraph(HGraph graph) {
_graph = graph;
- analyzer = SsaLiveBlockAnalyzer(graph, closedWorld, optimizer);
- analyzer.analyze();
+ _zapInstruction = null;
+ _computeLiveness();
visitPostDominatorTree(graph);
- cleanPhis();
+ }
+
+ void _computeLiveness() {
+ var analyzer = SsaLiveBlockAnalyzer(_graph, closedWorld, optimizer);
+ analyzer.analyze();
+ for (HBasicBlock block in _graph.blocks) {
+ block.isLive = analyzer.isLiveBlock(block);
+ }
}
@override
@@ -2551,14 +2553,12 @@
@override
void visitBasicBlock(HBasicBlock block) {
- bool isDeadBlock = analyzer.isDeadBlock(block);
- block.isLive = !isDeadBlock;
simplifyControlFlow(block);
// Start from the last non-control flow instruction in the block.
HInstruction instruction = block.last.previous;
while (instruction != null) {
var previous = instruction.previous;
- if (isDeadBlock) {
+ if (!block.isLive) {
eliminatedSideEffects =
eliminatedSideEffects || instruction.sideEffects.hasSideEffects();
removeUsers(instruction);
@@ -2574,10 +2574,48 @@
void simplifyPhi(HPhi phi) {
// Remove an unused HPhi so that the inputs can become potentially dead.
+ if (!phi.block.isLive) {
+ removeUsers(phi);
+ }
+
if (phi.usedBy.isEmpty) {
phi.block.removePhi(phi);
return;
}
+
+ // Run through the phis of the block and replace them with their input that
+ // comes from the only live predecessor if that dominates the phi.
+ //
+ // TODO(sra): If the input is directly in the only live predecessor, it
+ // might be possible to move it into [block] (e.g. all its inputs are
+ // dominating.)
+ // Find the index of the single live predecessor if it exists.
+ List<HBasicBlock> predecessors = phi.block.predecessors;
+ int indexOfLive = -1;
+ for (int i = 0; i < predecessors.length; i++) {
+ if (predecessors[i].isLive) {
+ if (indexOfLive >= 0) {
+ indexOfLive = -1;
+ break;
+ }
+ indexOfLive = i;
+ }
+ }
+
+ if (indexOfLive >= 0) {
+ HInstruction replacement = phi.inputs[indexOfLive];
+ if (replacement.dominates(phi)) {
+ phi.block.rewrite(phi, replacement);
+ phi.block.removePhi(phi);
+ if (replacement.sourceElement == null &&
+ phi.sourceElement != null &&
+ replacement is! HThis) {
+ replacement.sourceElement = phi.sourceElement;
+ }
+ return;
+ }
+ }
+
// If the phi is of the form `phi(x, HTypeKnown(x))`, it does not strengthen
// `x`. We can replace the phi with `x` to potentially make the HTypeKnown
// refinement node dead and potentially make a HIf control no HPhis.
@@ -2681,49 +2719,6 @@
}
}
- void cleanPhis() {
- L:
- for (HBasicBlock block in _graph.blocks) {
- List<HBasicBlock> predecessors = block.predecessors;
- // Zap all inputs to phis that correspond to dead blocks.
- block.forEachPhi((HPhi phi) {
- for (int i = 0; i < phi.inputs.length; ++i) {
- if (!predecessors[i].isLive && phi.inputs[i] != zapInstruction) {
- phi.replaceInput(i, zapInstruction);
- }
- }
- });
- if (predecessors.length < 2) continue L;
- // Find the index of the single live predecessor if it exists.
- int indexOfLive = -1;
- for (int i = 0; i < predecessors.length; i++) {
- if (predecessors[i].isLive) {
- if (indexOfLive >= 0) continue L;
- indexOfLive = i;
- }
- }
- // Run through the phis of the block and replace them with their input
- // that comes from the only live predecessor if that dominates the phi.
- //
- // TODO(sra): If the input is directly in the only live predecessor, it
- // might be possible to move it into [block] (e.g. all its inputs are
- // dominating.)
- block.forEachPhi((HPhi phi) {
- HInstruction replacement =
- (indexOfLive >= 0) ? phi.inputs[indexOfLive] : zapInstruction;
- if (replacement.dominates(phi)) {
- block.rewrite(phi, replacement);
- block.removePhi(phi);
- if (replacement.sourceElement == null &&
- phi.sourceElement != null &&
- replacement is! HThis) {
- replacement.sourceElement = phi.sourceElement;
- }
- }
- });
- }
- }
-
void removeUsers(HInstruction instruction) {
instruction.usedBy.forEach((user) {
removeInput(user, instruction);
@@ -2756,7 +2751,7 @@
Map<HInstruction, Range> get ranges => optimizer.ranges;
- bool isDeadBlock(HBasicBlock block) => !live.contains(block);
+ bool isLiveBlock(HBasicBlock block) => live.contains(block);
void analyze() {
markBlockLive(graph.entry);
diff --git a/pkg/dart2js_info/lib/proto_info_codec.dart b/pkg/dart2js_info/lib/proto_info_codec.dart
index 7007203..e81cb1a 100644
--- a/pkg/dart2js_info/lib/proto_info_codec.dart
+++ b/pkg/dart2js_info/lib/proto_info_codec.dart
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-// @dart = 2.11
-
/// Converters and codecs for converting between Protobuf and [Info] classes.
import 'dart:convert';
@@ -31,7 +29,6 @@
final Set<int> usedIds = <int>{};
Id idFor(Info info) {
- if (info == null) return null;
var serializedId = ids[info];
if (serializedId != null) return serializedId;
@@ -45,7 +42,6 @@
// No name and no parent, so `longName` isn't helpful
assert(info.name.isEmpty);
assert(info.parent == null);
- assert(info.code != null);
// Instead, use the content of the code.
id = info.code.first.text.hashCode;
} else {
@@ -62,10 +58,11 @@
AllInfoPB convert(AllInfo input) => _convertToAllInfoPB(input);
DependencyInfoPB _convertToDependencyInfoPB(DependencyInfo info) {
- var result = DependencyInfoPB()
- ..targetId = idFor(info.target)?.serializedId;
+ var result = DependencyInfoPB();
+ final targetId = idFor(info.target).serializedId;
+ result.targetId = targetId;
if (info.mask != null) {
- result.mask = info.mask;
+ result.mask = info.mask!;
}
return result;
}
@@ -123,21 +120,13 @@
..functionModifiers = _convertToFunctionModifiers(info.modifiers)
..inlinedCount = info.inlinedCount ?? 0;
- if (info.returnType != null) {
- proto.returnType = info.returnType;
- }
+ proto.returnType = info.returnType;
- if (info.inferredReturnType != null) {
- proto.inferredReturnType = info.inferredReturnType;
- }
+ proto.inferredReturnType = info.inferredReturnType;
- if (info.code != null) {
- proto.code = info.code.map((c) => c.text).join('\n');
- }
+ proto.code = info.code.map((c) => c.text).join('\n');
- if (info.sideEffects != null) {
- proto.sideEffects = info.sideEffects;
- }
+ proto.sideEffects = info.sideEffects;
proto.childrenIds
.addAll(info.closures.map(((closure) => idFor(closure).serializedId)));
@@ -152,12 +141,10 @@
..inferredType = info.inferredType
..isConst = info.isConst;
- if (info.code != null) {
- proto.code = info.code.map((c) => c.text).join('\n');
- }
+ proto.code = info.code.map((c) => c.text).join('\n');
if (info.initializer != null) {
- proto.initializerId = idFor(info.initializer).serializedId;
+ proto.initializerId = idFor(info.initializer!).serializedId;
}
proto.childrenIds
@@ -172,7 +159,7 @@
static OutputUnitInfoPB _convertToOutputUnitInfoPB(OutputUnitInfo info) {
final proto = OutputUnitInfoPB();
- proto.imports.addAll(info.imports.where((import) => import != null));
+ proto.imports.addAll(info.imports);
return proto;
}
@@ -190,23 +177,21 @@
..serializedId = idFor(info).serializedId
..size = info.size;
- if (info.name != null) {
- proto.name = info.name;
- }
+ proto.name = info.name;
if (info.parent != null) {
- proto.parentId = idFor(info.parent).serializedId;
+ proto.parentId = idFor(info.parent!).serializedId;
}
if (info.coverageId != null) {
- proto.coverageId = info.coverageId;
+ proto.coverageId = info.coverageId!;
}
if (info is BasicInfo && info.outputUnit != null) {
// TODO(lorenvs): Similar to the JSON codec, omit this for the default
// output unit. At the moment, there is no easy way to identify which
// output unit is the default on [OutputUnitInfo].
- proto.outputUnitId = idFor(info.outputUnit).serializedId;
+ proto.outputUnitId = idFor(info.outputUnit!).serializedId;
}
if (info is CodeInfo) {
@@ -244,15 +229,15 @@
..compilationDuration = Int64(info.compilationDuration.inMicroseconds)
..toProtoDuration = Int64(info.toJsonDuration.inMicroseconds)
..dumpInfoDuration = Int64(info.dumpInfoDuration.inMicroseconds)
- ..noSuchMethodEnabled = info.noSuchMethodEnabled ?? false
- ..isRuntimeTypeUsed = info.isRuntimeTypeUsed ?? false
- ..isIsolateUsed = info.isIsolateInUse ?? false
- ..isFunctionApplyUsed = info.isFunctionApplyUsed ?? false
- ..isMirrorsUsed = info.isMirrorsUsed ?? false
- ..minified = info.minified ?? false;
+ ..noSuchMethodEnabled = info.noSuchMethodEnabled
+ ..isRuntimeTypeUsed = info.isRuntimeTypeUsed
+ ..isIsolateUsed = info.isIsolateInUse
+ ..isFunctionApplyUsed = info.isFunctionApplyUsed
+ ..isMirrorsUsed = info.isMirrorsUsed
+ ..minified = info.minified;
if (info.dart2jsVersion != null) {
- result.dart2jsVersion = info.dart2jsVersion;
+ result.dart2jsVersion = info.dart2jsVersion!;
}
return result;
}
@@ -283,7 +268,7 @@
}
AllInfoPB _convertToAllInfoPB(AllInfo info) {
- final proto = AllInfoPB()..program = _convertToProgramInfoPB(info.program);
+ final proto = AllInfoPB()..program = _convertToProgramInfoPB(info.program!);
proto.allInfos.addEntries(_convertToAllInfosEntries(info.libraries));
proto.allInfos.addEntries(_convertToAllInfosEntries(info.classes));
diff --git a/pkg/dart2wasm/lib/code_generator.dart b/pkg/dart2wasm/lib/code_generator.dart
index bf65127..de13bd0 100644
--- a/pkg/dart2wasm/lib/code_generator.dart
+++ b/pkg/dart2wasm/lib/code_generator.dart
@@ -422,8 +422,8 @@
StringLiteral(expected),
translator
.translateType(translator.coreTypes.stringNonNullableRawType));
- _call(translator.stackTraceCurrent.reference);
- _call(translator.throwWasmRefError.reference);
+ call(translator.stackTraceCurrent.reference);
+ call(translator.throwWasmRefError.reference);
b.unreachable();
}
@@ -436,7 +436,7 @@
return expectedType;
}
- w.ValueType _call(Reference target) {
+ w.ValueType call(Reference target) {
w.BaseFunction targetFunction = translator.functions.getFunction(target);
if (translator.shouldInline(target)) {
List<w.Local> inlinedLocals =
@@ -492,7 +492,7 @@
this, TypeParameterType(typeParam, Nullability.nonNullable));
}
_visitArguments(node.arguments, node.targetReference, 1);
- _call(node.targetReference);
+ call(node.targetReference);
}
@override
@@ -511,7 +511,7 @@
}
_visitArguments(node.arguments, node.targetReference,
1 + supertype.typeArguments.length);
- _call(node.targetReference);
+ call(node.targetReference);
}
@override
@@ -910,7 +910,7 @@
nonNullableType =
translator.classInfo[translator.stringBaseClass]!.nonNullableType;
nullableType = nonNullableType.withNullability(true);
- compare = () => _call(translator.stringEquals.reference);
+ compare = () => call(translator.stringEquals.reference);
} else {
// Object switch
assert(check<InvalidExpression, InstanceConstant>());
@@ -1108,7 +1108,7 @@
b.ref_as_non_null();
}
_visitArguments(node.arguments, node.targetReference, 1);
- _call(node.targetReference);
+ call(node.targetReference);
if (expectedType != voidMarker) {
b.local_get(temp);
return temp.type;
@@ -1124,7 +1124,7 @@
if (intrinsicResult != null) return intrinsicResult;
_visitArguments(node.arguments, node.targetReference, 0);
- return _call(node.targetReference);
+ return call(node.targetReference);
}
Member _lookupSuperTarget(Member interfaceTarget, {required bool setter}) {
@@ -1143,7 +1143,7 @@
w.ValueType thisType = visitThis(receiverType);
translator.convertType(function, thisType, receiverType);
_visitArguments(node.arguments, target, 1);
- return _call(target);
+ return call(target);
}
@override
@@ -1182,7 +1182,7 @@
translator.functions.getFunction(singleTarget.reference);
wrap(node.receiver, targetFunction.type.inputs.first);
_visitArguments(node.arguments, node.interfaceTargetReference, 1);
- return _call(singleTarget.reference);
+ return call(singleTarget.reference);
}
return _virtualCall(node, target,
(signature) => wrap(node.receiver, signature.inputs.first), (_) {
@@ -1258,7 +1258,7 @@
if (singleTarget != null) {
left();
right();
- _call(singleTarget.reference);
+ call(singleTarget.reference);
} else {
_virtualCall(node, node.interfaceTarget, left, right,
getter: false, setter: false);
@@ -1307,7 +1307,7 @@
assert(selector.targetCount <= 1);
if (selector.targetCount == 1) {
pushArguments(selector.signature);
- return _call(selector.singularTarget!);
+ return call(selector.singularTarget!);
} else {
b.comment("Virtual call of ${selector.name} with no targets"
" at ${node.location}");
@@ -1367,7 +1367,7 @@
b.i32_const(id);
b.i32_eq();
b.if_(selector.signature.inputs, selector.signature.inputs);
- _call(target);
+ call(target);
b.br(block);
b.end();
implementations.remove(id);
@@ -1387,7 +1387,7 @@
b.i32_const(pivotId);
b.i32_lt_u();
b.if_(selector.signature.inputs, selector.signature.inputs);
- _call(target);
+ call(target);
b.br(block);
b.end();
for (int id in sorted) {
@@ -1398,7 +1398,7 @@
}
// Call remaining implementation.
Reference target = implementations.values.first;
- _call(target);
+ call(target);
b.end();
}
@@ -1468,7 +1468,7 @@
if (target is Field) {
return translator.globals.readGlobal(b, target);
} else {
- return _call(target.reference);
+ return call(target.reference);
}
}
@@ -1502,7 +1502,7 @@
temp = addLocal(translateType(dartTypeOf(node.value)));
b.local_tee(temp);
}
- _call(target.reference);
+ call(target.reference);
if (preserved) {
b.local_get(temp!);
return temp.type;
@@ -1622,7 +1622,7 @@
w.BaseFunction targetFunction =
translator.functions.getFunction(target.reference);
wrap(receiver, targetFunction.type.inputs.single);
- return _call(target.reference);
+ return call(target.reference);
}
}
@@ -1688,7 +1688,7 @@
b.local_tee(temp);
translator.convertType(function, temp.type, paramType);
}
- _call(target.reference);
+ call(target.reference);
}
if (preserved) {
b.local_get(temp!);
@@ -1853,8 +1853,8 @@
// We lower a null check to a br_on_non_null, throwing a [TypeError] in the
// null case.
b.br_on_non_null(nullCheckBlock);
- _call(translator.stackTraceCurrent.reference);
- _call(translator.throwNullCheckError.reference);
+ call(translator.stackTraceCurrent.reference);
+ call(translator.throwNullCheckError.reference);
b.unreachable();
b.end();
return nonNullOperandType;
@@ -1905,13 +1905,13 @@
StringConcatenation node, w.ValueType expectedType) {
makeList(node.expressions, translator.fixedLengthListClass,
InterfaceType(translator.stringBaseClass, Nullability.nonNullable));
- return _call(translator.stringInterpolate.reference);
+ return call(translator.stringInterpolate.reference);
}
@override
w.ValueType visitThrow(Throw node, w.ValueType expectedType) {
wrap(node.expression, translator.topInfo.nonNullableType);
- _call(translator.stackTraceCurrent.reference);
+ call(translator.stackTraceCurrent.reference);
// At this point, we have the exception and the current stack trace on the
// stack, so just throw them using the exception tag.
@@ -2101,8 +2101,8 @@
b.br_if(asCheckBlock);
b.local_get(operand);
types.makeType(this, node.type);
- _call(translator.stackTraceCurrent.reference);
- _call(translator.throwAsCheckError.reference);
+ call(translator.stackTraceCurrent.reference);
+ call(translator.throwAsCheckError.reference);
b.unreachable();
b.end();
b.local_get(operand);
diff --git a/pkg/dart2wasm/lib/constants.dart b/pkg/dart2wasm/lib/constants.dart
index fbf5eeb..e62bf7f 100644
--- a/pkg/dart2wasm/lib/constants.dart
+++ b/pkg/dart2wasm/lib/constants.dart
@@ -775,7 +775,6 @@
@override
ConstantInfo? visitTypeLiteralConstant(TypeLiteralConstant constant) {
DartType type = constant.type;
- assert(type is! TypeParameterType);
ClassInfo info = translator.classInfo[types.classForType(type)]!;
translator.functions.allocateClass(info.classId);
@@ -796,6 +795,18 @@
} else {
return _makeFunctionType(constant, type, info);
}
+ } else if (type is TypeParameterType) {
+ // TODO(joshualitt): Handle generic function types.
+ assert(!types.isGenericFunctionTypeParameter(type));
+ int environmentIndex =
+ types.interfaceTypeEnvironment.lookup(type.parameter);
+ return createConstant(constant, info.nonNullableType, (function, b) {
+ b.i32_const(info.classId);
+ b.i32_const(initialIdentityHash);
+ types.encodeNullability(b, type);
+ b.i32_const(environmentIndex);
+ translator.struct_new(b, info);
+ });
} else {
assert(type is VoidType ||
type is NeverType ||
diff --git a/pkg/dart2wasm/lib/intrinsics.dart b/pkg/dart2wasm/lib/intrinsics.dart
index 5d166ca..e59f140 100644
--- a/pkg/dart2wasm/lib/intrinsics.dart
+++ b/pkg/dart2wasm/lib/intrinsics.dart
@@ -639,8 +639,10 @@
codeGen.wrap(stackTrace, stackTraceType);
b.throw_(translator.exceptionTag);
return codeGen.voidMarker;
- case "_getSubtypeMap":
- return translator.types.makeSubtypeMap(b);
+ case "_getTypeRulesSupers":
+ return translator.types.makeTypeRulesSupers(b);
+ case "_getTypeRulesSubstitutions":
+ return translator.types.makeTypeRulesSubstitutions(b);
}
}
diff --git a/pkg/dart2wasm/lib/translator.dart b/pkg/dart2wasm/lib/translator.dart
index 47a9670..ce2a835 100644
--- a/pkg/dart2wasm/lib/translator.dart
+++ b/pkg/dart2wasm/lib/translator.dart
@@ -97,6 +97,8 @@
late final Class interfaceTypeClass;
late final Class functionTypeClass;
late final Class genericFunctionTypeClass;
+ late final Class interfaceTypeParameterTypeClass;
+ late final Class genericFunctionTypeParameterTypeClass;
late final Class namedParameterClass;
late final Class stackTraceClass;
late final Class ffiCompoundClass;
@@ -119,7 +121,6 @@
late final Procedure setFactory;
late final Procedure setAdd;
late final Procedure hashImmutableIndexNullable;
- // TODO(joshualitt): Wire up runtime type checks.
late final Procedure isSubtype;
late final Map<Class, w.StorageType> builtinTypes;
late final Map<w.ValueType, Class> boxedClasses;
@@ -213,6 +214,9 @@
interfaceTypeClass = lookupCore("_InterfaceType");
functionTypeClass = lookupCore("_FunctionType");
genericFunctionTypeClass = lookupCore("_GenericFunctionType");
+ interfaceTypeParameterTypeClass = lookupCore("_InterfaceTypeParameterType");
+ genericFunctionTypeParameterTypeClass =
+ lookupCore("_GenericFunctionTypeParameterType");
namedParameterClass = lookupCore("_NamedParameter");
stackTraceClass = lookupCore("StackTrace");
typeUniverseClass = lookupCore("_TypeUniverse");
diff --git a/pkg/dart2wasm/lib/types.dart b/pkg/dart2wasm/lib/types.dart
index 53df180..371f1c9 100644
--- a/pkg/dart2wasm/lib/types.dart
+++ b/pkg/dart2wasm/lib/types.dart
@@ -7,9 +7,27 @@
import 'package:dart2wasm/translator.dart';
import 'package:kernel/ast.dart';
+import 'package:kernel/core_types.dart';
import 'package:wasm_builder/wasm_builder.dart' as w;
+class InterfaceTypeEnvironment {
+ final Map<TypeParameter, int> typeOffsets = {};
+
+ void _add(InterfaceType type) {
+ Class cls = type.classNode;
+ if (typeOffsets.containsKey(cls)) {
+ return;
+ }
+ int i = 0;
+ for (TypeParameter typeParameter in cls.typeParameters) {
+ typeOffsets[typeParameter] = i++;
+ }
+ }
+
+ int lookup(TypeParameter typeParameter) => typeOffsets[typeParameter]!;
+}
+
/// Helper class for building runtime types.
class Types {
final Translator translator;
@@ -19,6 +37,32 @@
late final w.ValueType namedParametersExpectedType = classAndFieldToType(
translator.functionTypeClass, FieldIndex.functionTypeNamedParameters);
+ /// A mapping from concrete subclass `classID` to [Map]s of superclass
+ /// `classID` and the necessary substitutions which must be performed to test
+ /// for a valid subtyping relationship.
+ late final Map<int, Map<int, List<DartType>>> typeRules = _buildTypeRules();
+
+ /// We will build the [interfaceTypeEnvironment] when building the
+ /// [typeRules].
+ final InterfaceTypeEnvironment interfaceTypeEnvironment =
+ InterfaceTypeEnvironment();
+
+ /// Because we can't currently support [Map]s in our `TypeUniverse`, we have
+ /// to decompose [typeRules] into two [Map]s based on [List]s.
+ ///
+ /// [typeRulesSupers] is a [List] where the index in the list is a subclasses'
+ /// `classID` and the value at that index is a [List] of superclass
+ /// `classID`s.
+ late final List<List<int>> typeRulesSupers = _buildTypeRulesSupers();
+
+ /// [typeRulesSubstitutions] is a [List] where the index in the list is a
+ /// subclasses' `classID` and the value at that index is a [List] indexed by
+ /// the index of the superclasses' `classID` in [typeRulesSuper] and the value
+ /// at that index is a [List] of [DartType]s which must be substituted for the
+ /// subtyping relationship to be valid.
+ late final List<List<List<DartType>>> typeRulesSubstitutions =
+ _buildTypeRulesSubstitutions();
+
Types(this.translator);
w.ValueType classAndFieldToType(Class cls, int fieldIndex) =>
@@ -34,46 +78,135 @@
InterfaceType get namedParameterType =>
InterfaceType(translator.namedParameterClass, Nullability.nonNullable);
- /// Build a [Map<int, List<int>>] to store subtype information.
- Map<int, List<int>> _buildSubtypeMap() {
- List<ClassInfo> classes = translator.classes;
- Map<int, List<int>> subtypeMap = {};
- for (ClassInfo classInfo in classes) {
- if (classInfo.cls == null) continue;
- List<int> classIds = _getConcreteSubtypes(classInfo.cls!)
- .map((cls) => translator.classInfo[cls]!.classId)
- .where((classId) => classId != classInfo.classId)
- .toList();
+ CoreTypes get coreTypes => translator.coreTypes;
- if (classIds.isEmpty) continue;
- subtypeMap[classInfo.classId] = classIds;
+ /// Builds a [Map<int, Map<int, List<DartType>>>] to store subtype
+ /// information. The first key is the class id of a subtype. This returns a
+ /// map where each key is the class id of a transitively implemented super
+ /// type and each value is a list of the necessary type substitutions required
+ /// for the subtyping relationship to be valid.
+ Map<int, Map<int, List<DartType>>> _buildTypeRules() {
+ List<ClassInfo> classes = translator.classes;
+ Map<int, Map<int, List<DartType>>> subtypeMap = {};
+ for (ClassInfo classInfo in classes) {
+ ClassInfo superclassInfo = classInfo;
+
+ // We don't need type rules for any class without a superclass, or for
+ // classes whose supertype is [Object]. The latter case will be handled
+ // directly in the subtype checking algorithm.
+ if (superclassInfo.cls == null ||
+ superclassInfo.cls == coreTypes.objectClass) continue;
+ Class superclass = superclassInfo.cls!;
+ Iterable<Class> subclasses =
+ _getConcreteSubtypes(superclass).where((cls) => cls != superclass);
+ Iterable<InterfaceType> subtypes = subclasses.map(
+ (Class cls) => cls.getThisType(coreTypes, Nullability.nonNullable));
+ for (InterfaceType subtype in subtypes) {
+ interfaceTypeEnvironment._add(subtype);
+ List<DartType>? typeArguments = translator.hierarchy
+ .getTypeArgumentsAsInstanceOf(subtype, superclass);
+ ClassInfo subclassInfo = translator.classInfo[subtype.classNode]!;
+ Map<int, List<DartType>> substitutionMap =
+ subtypeMap[subclassInfo.classId] ??= {};
+ substitutionMap[superclassInfo.classId] = typeArguments ?? const [];
+ }
}
return subtypeMap;
}
- /// Builds the subtype map and pushes it onto the stack.
- w.ValueType makeSubtypeMap(w.Instructions b) {
- // Instantiate subtype map constant.
- Map<int, List<int>> subtypeMap = _buildSubtypeMap();
- ClassInfo immutableMapInfo =
- translator.classInfo[translator.immutableMapClass]!;
- w.ValueType expectedType = immutableMapInfo.nonNullableType;
- DartType mapAndSetKeyType = translator.coreTypes.intNonNullableRawType;
- DartType mapValueType = InterfaceType(translator.immutableListClass,
- Nullability.nonNullable, [mapAndSetKeyType]);
- List<ConstantMapEntry> entries = subtypeMap.entries.map((mapEntry) {
- return ConstantMapEntry(
- IntConstant(mapEntry.key),
- ListConstant(mapAndSetKeyType,
- mapEntry.value.map((i) => IntConstant(i)).toList()));
- }).toList();
- translator.constants.instantiateConstant(null, b,
- MapConstant(mapAndSetKeyType, mapValueType, entries), expectedType);
+ List<List<int>> _buildTypeRulesSupers() {
+ List<List<int>> typeRulesSupers = [];
+ for (int i = 0; i < translator.classInfoCollector.nextClassId; i++) {
+ List<int>? superclassIds = typeRules[i]?.keys.toList();
+ if (superclassIds == null) {
+ typeRulesSupers.add(const []);
+ } else {
+ superclassIds.sort();
+ typeRulesSupers.add(superclassIds);
+ }
+ }
+ return typeRulesSupers;
+ }
+
+ List<List<List<DartType>>> _buildTypeRulesSubstitutions() {
+ List<List<List<DartType>>> typeRulesSubstitutions = [];
+ for (int i = 0; i < translator.classInfoCollector.nextClassId; i++) {
+ List<int> supers = typeRulesSupers[i];
+ typeRulesSubstitutions.add(supers.isEmpty ? const [] : []);
+ for (int j = 0; j < supers.length; j++) {
+ int superId = supers[j];
+ typeRulesSubstitutions.last.add(typeRules[i]![superId]!);
+ }
+ }
+ return typeRulesSubstitutions;
+ }
+
+ /// Builds a map of subclasses to the transitive set of superclasses they
+ /// implement.
+ /// TODO(joshualitt): This implementation is just temporary. Eventually we
+ /// should move to a data structure more closely resembling [typeRules].
+ w.ValueType makeTypeRulesSupers(w.Instructions b) {
+ w.ValueType expectedType =
+ translator.classInfo[translator.immutableListClass]!.nonNullableType;
+ DartType listIntType = InterfaceType(translator.immutableListClass,
+ Nullability.nonNullable, [translator.coreTypes.intNonNullableRawType]);
+ List<ListConstant> listIntConstant = [];
+ for (List<int> supers in typeRulesSupers) {
+ listIntConstant.add(ListConstant(
+ listIntType, supers.map((i) => IntConstant(i)).toList()));
+ }
+ DartType listListIntType = InterfaceType(
+ translator.immutableListClass, Nullability.nonNullable, [listIntType]);
+ translator.constants.instantiateConstant(
+ null, b, ListConstant(listListIntType, listIntConstant), expectedType);
+ return expectedType;
+ }
+
+ /// Similar to the above, but provides the substitutions required for each
+ /// supertype.
+ /// TODO(joshualitt): Like [makeTypeRulesSupers], this is just temporary.
+ w.ValueType makeTypeRulesSubstitutions(w.Instructions b) {
+ w.ValueType expectedType =
+ translator.classInfo[translator.immutableListClass]!.nonNullableType;
+ DartType listTypeType = InterfaceType(
+ translator.immutableListClass,
+ Nullability.nonNullable,
+ [translator.typeClass.getThisType(coreTypes, Nullability.nonNullable)]);
+ DartType listListTypeType = InterfaceType(
+ translator.immutableListClass, Nullability.nonNullable, [listTypeType]);
+ DartType listListListTypeType = InterfaceType(translator.immutableListClass,
+ Nullability.nonNullable, [listListTypeType]);
+ List<ListConstant> substitutionsConstantL0 = [];
+ for (List<List<DartType>> substitutionsL1 in typeRulesSubstitutions) {
+ List<ListConstant> substitutionsConstantL1 = [];
+ for (List<DartType> substitutionsL2 in substitutionsL1) {
+ substitutionsConstantL1.add(ListConstant(
+ listTypeType,
+ substitutionsL2.map((t) {
+ // TODO(joshualitt): implement generic functions
+ if (t is FunctionType && isGenericFunction(t)) {
+ return TypeLiteralConstant(DynamicType());
+ } else {
+ return TypeLiteralConstant(t);
+ }
+ }).toList()));
+ }
+ substitutionsConstantL0
+ .add(ListConstant(listListTypeType, substitutionsConstantL1));
+ }
+ translator.constants.instantiateConstant(
+ null,
+ b,
+ ListConstant(listListListTypeType, substitutionsConstantL0),
+ expectedType);
return expectedType;
}
bool isGenericFunction(FunctionType type) => type.typeParameters.isNotEmpty;
+ bool isGenericFunctionTypeParameter(TypeParameterType type) =>
+ type.parameter.parent == null;
+
bool _isTypeConstant(DartType type) {
return type is DynamicType ||
type is VoidType ||
@@ -113,6 +246,12 @@
} else {
return translator.functionTypeClass;
}
+ } else if (type is TypeParameterType) {
+ if (isGenericFunctionTypeParameter(type)) {
+ return translator.genericFunctionTypeParameterTypeClass;
+ } else {
+ return translator.interfaceTypeParameterTypeClass;
+ }
}
throw "Unexpected DartType: $type";
}
@@ -251,7 +390,9 @@
TreeNode node) {
w.Instructions b = codeGen.b;
if (type is! InterfaceType) {
- // TODO(askesc): Implement type test for remaining types
+ // TODO(joshualitt): We can enable this after fixing `.runtimeType`.
+ // makeType(codeGen, type);
+ // codeGen.call(translator.isSubtype.reference);
print("Not implemented: Type test with non-interface type $type"
" at ${node.location}");
b.drop();
@@ -284,7 +425,7 @@
}
}
List<Class> concrete = _getConcreteSubtypes(type.classNode).toList();
- if (type.classNode == translator.coreTypes.functionClass) {
+ if (type.classNode == coreTypes.functionClass) {
ClassInfo functionInfo = translator.classInfo[translator.functionClass]!;
translator.ref_test(b, functionInfo);
} else if (concrete.isEmpty) {
diff --git a/pkg/dev_compiler/tool/ddb b/pkg/dev_compiler/tool/ddb
index 8cfc2e2..799e176 100755
--- a/pkg/dev_compiler/tool/ddb
+++ b/pkg/dev_compiler/tool/ddb
@@ -174,31 +174,28 @@
mode: ProcessStartMode.inheritStdio, environment: environment);
}
- Future<void> runDdc(List<String> args) async {
- if (debug) {
- // Use unbuilt script. This only works from a source checkout.
- var vmServicePort = options.wasParsed('vm-service-port')
- ? '=${options['vm-service-port']}'
- : '';
- var observe =
- options.wasParsed('vm-service-port') || options['observe'] as bool;
- args.insertAll(0, [
+ Future<void> runDdc(List<String> ddcArgs) async {
+ var observe =
+ options.wasParsed('vm-service-port') || options['observe'] as bool;
+ var vmServicePort = options.wasParsed('vm-service-port')
+ ? '=${options['vm-service-port']}'
+ : '';
+ var args = <String>[
+ ...?options['compile-vm-options']?.split(' '),
+ if (debug) ...[
if (observe) ...[
'--enable-vm-service$vmServicePort',
'--pause-isolates-on-start',
],
'--enable-asserts',
- p.join(ddcPath, 'bin', 'dartdevc.dart')
- ]);
- } else {
- // Use built snapshot.
- args.insertAll(
- 0, [p.join(dartSdk, 'bin', 'snapshots', 'dartdevc.dart.snapshot')]);
- }
- var process = await startProcess('DDC', dartBinary, args, <String, String>{
- if (options['compile-vm-options'] != null)
- 'DART_VM_OPTIONS': options['compile-vm-options'] as String
- });
+ // Use unbuilt script. This only works from a source checkout.
+ p.join(ddcPath, 'bin', 'dartdevc.dart'),
+ ] else
+ // Use built snapshot.
+ p.join(dartSdk, 'bin', 'snapshots', 'dartdevc.dart.snapshot'),
+ ...ddcArgs,
+ ];
+ var process = await startProcess('DDC', dartBinary, args);
if (await process.exitCode != 0) exit(await process.exitCode);
}
diff --git a/runtime/vm/simulator_riscv.cc b/runtime/vm/simulator_riscv.cc
index 73cdb72..2229c31 100644
--- a/runtime/vm/simulator_riscv.cc
+++ b/runtime/vm/simulator_riscv.cc
@@ -383,6 +383,28 @@
}
void Simulator::Execute() {
+ if (LIKELY(FLAG_trace_sim_after == ULLONG_MAX)) {
+ ExecuteNoTrace();
+ } else {
+ ExecuteTrace();
+ }
+}
+
+void Simulator::ExecuteNoTrace() {
+ while (pc_ != kEndSimulatingPC) {
+ uint16_t parcel = *reinterpret_cast<uint16_t*>(pc_);
+ if (IsCInstruction(parcel)) {
+ CInstr instr(parcel);
+ Interpret(instr);
+ } else {
+ Instr instr(*reinterpret_cast<uint32_t*>(pc_));
+ Interpret(instr);
+ }
+ instret_++;
+ }
+}
+
+void Simulator::ExecuteTrace() {
while (pc_ != kEndSimulatingPC) {
uint16_t parcel = *reinterpret_cast<uint16_t*>(pc_);
if (IsCInstruction(parcel)) {
@@ -482,6 +504,7 @@
}
}
+DART_FORCE_INLINE
void Simulator::Interpret(Instr instr) {
switch (instr.opcode()) {
case LUI:
@@ -552,6 +575,7 @@
}
}
+DART_FORCE_INLINE
void Simulator::Interpret(CInstr instr) {
switch (instr.opcode()) {
case C_LWSP: {
@@ -812,21 +836,25 @@
pc_ += instr.length();
}
+DART_FORCE_INLINE
void Simulator::InterpretLUI(Instr instr) {
set_xreg(instr.rd(), sign_extend(instr.utype_imm()));
pc_ += instr.length();
}
+DART_FORCE_INLINE
void Simulator::InterpretAUIPC(Instr instr) {
set_xreg(instr.rd(), pc_ + sign_extend(instr.utype_imm()));
pc_ += instr.length();
}
+DART_FORCE_INLINE
void Simulator::InterpretJAL(Instr instr) {
set_xreg(instr.rd(), pc_ + instr.length());
pc_ += sign_extend(instr.jtype_imm());
}
+DART_FORCE_INLINE
void Simulator::InterpretJALR(Instr instr) {
uintx_t base = get_xreg(instr.rs1());
uintx_t offset = static_cast<uintx_t>(instr.itype_imm());
@@ -834,6 +862,7 @@
pc_ = base + offset;
}
+DART_FORCE_INLINE
void Simulator::InterpretBRANCH(Instr instr) {
switch (instr.funct3()) {
case BEQ:
@@ -887,6 +916,7 @@
}
}
+DART_FORCE_INLINE
void Simulator::InterpretLOAD(Instr instr) {
uintx_t addr = get_xreg(instr.rs1()) + instr.itype_imm();
switch (instr.funct3()) {
@@ -919,6 +949,7 @@
pc_ += instr.length();
}
+DART_FORCE_INLINE
void Simulator::InterpretLOADFP(Instr instr) {
uintx_t addr = get_xreg(instr.rs1()) + instr.itype_imm();
switch (instr.funct3()) {
@@ -934,6 +965,7 @@
pc_ += instr.length();
}
+DART_FORCE_INLINE
void Simulator::InterpretSTORE(Instr instr) {
uintx_t addr = get_xreg(instr.rs1()) + instr.stype_imm();
switch (instr.funct3()) {
@@ -957,6 +989,7 @@
pc_ += instr.length();
}
+DART_FORCE_INLINE
void Simulator::InterpretSTOREFP(Instr instr) {
uintx_t addr = get_xreg(instr.rs1()) + instr.stype_imm();
switch (instr.funct3()) {
@@ -972,6 +1005,7 @@
pc_ += instr.length();
}
+DART_FORCE_INLINE
void Simulator::InterpretOPIMM(Instr instr) {
switch (instr.funct3()) {
case ADDI:
@@ -1018,6 +1052,7 @@
pc_ += instr.length();
}
+DART_FORCE_INLINE
void Simulator::InterpretOPIMM32(Instr instr) {
switch (instr.funct3()) {
case ADDI: {
@@ -1049,6 +1084,7 @@
pc_ += instr.length();
}
+DART_FORCE_INLINE
void Simulator::InterpretOP(Instr instr) {
switch (instr.funct7()) {
case 0:
@@ -1065,6 +1101,7 @@
}
}
+DART_FORCE_INLINE
void Simulator::InterpretOP_0(Instr instr) {
switch (instr.funct3()) {
case ADD:
@@ -1247,6 +1284,7 @@
}
#endif // XLEN >= 64
+DART_FORCE_INLINE
void Simulator::InterpretOP_MULDIV(Instr instr) {
switch (instr.funct3()) {
case MUL:
@@ -1280,6 +1318,7 @@
pc_ += instr.length();
}
+DART_FORCE_INLINE
void Simulator::InterpretOP_SUB(Instr instr) {
switch (instr.funct3()) {
case ADD:
@@ -1296,6 +1335,7 @@
pc_ += instr.length();
}
+DART_FORCE_INLINE
void Simulator::InterpretOP32(Instr instr) {
switch (instr.funct7()) {
#if XLEN >= 64
@@ -1314,6 +1354,7 @@
}
}
+DART_FORCE_INLINE
void Simulator::InterpretOP32_0(Instr instr) {
switch (instr.funct3()) {
#if XLEN >= 64
@@ -1342,6 +1383,7 @@
pc_ += instr.length();
}
+DART_FORCE_INLINE
void Simulator::InterpretOP32_SUB(Instr instr) {
switch (instr.funct3()) {
#if XLEN >= 64
@@ -1364,6 +1406,7 @@
pc_ += instr.length();
}
+DART_FORCE_INLINE
void Simulator::InterpretOP32_MULDIV(Instr instr) {
switch (instr.funct3()) {
#if XLEN >= 64
diff --git a/runtime/vm/simulator_riscv.h b/runtime/vm/simulator_riscv.h
index 9a5ca23..d49fed9 100644
--- a/runtime/vm/simulator_riscv.h
+++ b/runtime/vm/simulator_riscv.h
@@ -325,6 +325,8 @@
// Executes RISC-V instructions until the PC reaches kEndSimulatingPC.
void Execute();
+ void ExecuteNoTrace();
+ void ExecuteTrace();
// Returns true if tracing of executed instructions is enabled.
bool IsTracingExecution() const;
diff --git a/sdk/lib/_internal/wasm/lib/class_id.dart b/sdk/lib/_internal/wasm/lib/class_id.dart
index 2e16f69..08f2006 100644
--- a/sdk/lib/_internal/wasm/lib/class_id.dart
+++ b/sdk/lib/_internal/wasm/lib/class_id.dart
@@ -38,6 +38,10 @@
external static int get cidFunctionType;
@pragma("wasm:class-id", "dart.core#_GenericFunctionType")
external static int get cidGenericFunctionType;
+ @pragma("wasm:class-id", "dart.core#_GenericFunctionTypeParameterType")
+ external static int get cidGenericFunctionTypeParameterType;
+ @pragma("wasm:class-id", "dart.core#_InterfaceTypeParameterType")
+ external static int get cidInterfaceTypeParameterType;
// Dummy, only used by VM-specific hash table code.
static final int numPredefinedCids = 1;
diff --git a/sdk/lib/_internal/wasm/lib/type.dart b/sdk/lib/_internal/wasm/lib/type.dart
index 046c15b..fcee927 100644
--- a/sdk/lib/_internal/wasm/lib/type.dart
+++ b/sdk/lib/_internal/wasm/lib/type.dart
@@ -26,14 +26,18 @@
bool get isNull => _testID(ClassID.cidNullType);
bool get isFutureOr => _testID(ClassID.cidFutureOrType);
bool get isInterface => _testID(ClassID.cidInterfaceType);
+ bool get isInterfaceTypeParameterType =>
+ _testID(ClassID.cidInterfaceTypeParameterType);
bool get isFunction => _testID(ClassID.cidFunctionType);
bool get isGenericFunction => _testID(ClassID.cidGenericFunctionType);
T as<T>() => unsafeCast<T>(this);
_Type get asNonNullable => isNullable ? _asNonNullable : this;
+ _Type get asNullable => isNullable ? this : _asNullable;
_Type get _asNonNullable;
+ _Type get _asNullable;
@override
bool operator ==(Object other) => ClassID.getID(this) == ClassID.getID(other);
@@ -49,6 +53,10 @@
@override
_Type get _asNonNullable => this;
+ /// Never? normalizes to Null.
+ @override
+ _Type get _asNullable => const _NullType();
+
@override
String toString() => 'Never';
}
@@ -61,6 +69,9 @@
_Type get _asNonNullable => throw '`dynamic` type is always nullable.';
@override
+ _Type get _asNullable => this;
+
+ @override
String toString() => 'dynamic';
}
@@ -72,6 +83,9 @@
_Type get _asNonNullable => throw '`void` type is always nullable.';
@override
+ _Type get _asNullable => this;
+
+ @override
String toString() => 'void';
}
@@ -83,9 +97,52 @@
_Type get _asNonNullable => const _NeverType();
@override
+ _Type get _asNullable => this;
+
+ @override
String toString() => 'Null';
}
+/// Because Interface type parameters are fundamentally different from Generic
+/// function type parameters, we are keeping these classes separate for the time
+/// being.
+@pragma("wasm:entry-point")
+class _InterfaceTypeParameterType extends _Type {
+ final int environmentIndex;
+
+ const _InterfaceTypeParameterType(super.isNullable, this.environmentIndex);
+
+ @override
+ _Type get _asNonNullable =>
+ throw 'Type parameter should have been substituted already.';
+
+ @override
+ _Type get _asNullable =>
+ throw 'Type parameter should have been substituted already.';
+
+ @override
+ String toString() => '$environmentIndex';
+}
+
+@pragma("wasm:entry-point")
+class _GenericFunctionTypeParameterType extends _Type {
+ final int environmentIndex;
+
+ const _GenericFunctionTypeParameterType(
+ super.isNullable, this.environmentIndex);
+
+ @override
+ _Type get _asNonNullable =>
+ throw 'Type parameter should have been substituted already..';
+
+ @override
+ _Type get _asNullable =>
+ throw 'Type parameter should have been substituted already.';
+
+ @override
+ String toString() => '$environmentIndex';
+}
+
@pragma("wasm:entry-point")
class _FutureOrType extends _Type {
final _Type typeArgument;
@@ -103,6 +160,9 @@
}
@override
+ _Type get _asNullable => _FutureOrType(true, typeArgument);
+
+ @override
bool operator ==(Object o) {
if (!(super == o)) return false;
_FutureOrType other = unsafeCast<_FutureOrType>(o);
@@ -142,6 +202,9 @@
_Type get _asNonNullable => _InterfaceType(classId, false, typeArguments);
@override
+ _Type get _asNullable => _InterfaceType(classId, true, typeArguments);
+
+ @override
bool operator ==(Object o) {
if (!(super == o)) return false;
_InterfaceType other = unsafeCast<_InterfaceType>(o);
@@ -234,6 +297,10 @@
_Type get _asNonNullable => _FunctionType(returnType, positionalParameters,
requiredParameterCount, namedParameters, false);
+ @override
+ _Type get _asNullable => _FunctionType(returnType, positionalParameters,
+ requiredParameterCount, namedParameters, true);
+
bool operator ==(Object o) {
if (!(super == o)) return false;
_FunctionType other = unsafeCast<_FunctionType>(o);
@@ -305,19 +372,63 @@
_Type get _asNonNullable => throw 'unimplemented';
@override
+ _Type get _asNullable => throw 'unimplemented';
+
+ @override
String toString() => 'GenericFunctionType';
}
-external Map<int, List<int>> _getSubtypeMap();
+external List<List<int>> _getTypeRulesSupers();
+external List<List<List<_Type>>> _getTypeRulesSubstitutions();
+
+class _Environment {
+ List<List<_Type>> scopes = [];
+
+ _Environment();
+
+ factory _Environment.from(List<_Type> initialScope) {
+ final env = _Environment();
+ env.push(initialScope);
+ return env;
+ }
+
+ void push(List<_Type> scope) => scopes.add(scope);
+
+ void pop() => scopes.removeLast();
+
+ _Type _substituteTypeParameter(bool declaredNullable, _Type type) {
+ // If the type parameter is non-nullable, or the substitution type is
+ // nullable, then just return the substitution type. Otherwise, we return
+ // [type] as nullable.
+ // Note: This will throw if the required nullability is impossible to
+ // generate.
+ if (!declaredNullable || type.isNullable) {
+ return type;
+ }
+ return type.asNullable;
+ }
+
+ _Type lookup(_InterfaceTypeParameterType typeParameter) {
+ // Lookup `InterfaceType` parameters in the top environment.
+ // TODO(joshualitt): When we implement generic functions be sure to keep the
+ // environments distinct.
+ return _substituteTypeParameter(
+ typeParameter.isNullable, scopes.last[typeParameter.environmentIndex]);
+ }
+}
class _TypeUniverse {
- /// 'Map' of classId to range of subclasses.
- final Map<int, List<int>> _subtypeMap;
+ /// 'Map' of classId to the transitive set of super classes it implements.
+ final List<List<int>> typeRulesSupers;
- const _TypeUniverse._(this._subtypeMap);
+ /// 'Map' of classId, and super offset(from [typeRulesSupers]) to a list of
+ /// type substitutions.
+ final List<List<List<_Type>>> typeRulesSubstitutions;
+
+ const _TypeUniverse._(this.typeRulesSupers, this.typeRulesSubstitutions);
factory _TypeUniverse.create() {
- return _TypeUniverse._(_getSubtypeMap());
+ return _TypeUniverse._(_getTypeRulesSupers(), _getTypeRulesSubstitutions());
}
bool isSpecificInterfaceType(_Type t, int classId) {
@@ -341,27 +452,55 @@
bool isFunctionType(_Type t) =>
isSpecificInterfaceType(t, ClassID.cidFunction);
- bool isInterfaceSubtype(_InterfaceType s, _InterfaceType t) {
- int sId = s.classId;
- int tId = t.classId;
- if (sId == tId) {
- assert(s.typeArguments.length == t.typeArguments.length);
- for (int i = 0; i < s.typeArguments.length; i++) {
- if (!isSubtype(s.typeArguments[i], t.typeArguments[i])) {
- return false;
- }
+ bool areTypeArgumentsSubtypes(List<_Type> sArgs, _Environment? sEnv,
+ List<_Type> tArgs, _Environment? tEnv) {
+ assert(sArgs.length == tArgs.length);
+ for (int i = 0; i < sArgs.length; i++) {
+ if (!isSubtype(sArgs[i], sEnv, tArgs[i], tEnv)) {
+ return false;
}
- return true;
}
- List<int>? subtypes = _subtypeMap[tId];
- if (subtypes == null) return false;
- if (!subtypes.contains(sId)) return false;
- // TODO(joshualitt): Compare type arguments.
return true;
}
- bool isFunctionSubtype(_FunctionType s, _FunctionType t) {
- if (!isSubtype(s.returnType, t.returnType)) return false;
+ bool isInterfaceSubtype(_InterfaceType s, _Environment? sEnv,
+ _InterfaceType t, _Environment? tEnv) {
+ int sId = s.classId;
+ int tId = t.classId;
+
+ // If we have the same class, simply compare type arguments.
+ if (sId == tId) {
+ return areTypeArgumentsSubtypes(
+ s.typeArguments, sEnv, t.typeArguments, tEnv);
+ }
+
+ // Otherwise, check if [s] is a subtype of [t], and if it is then compare
+ // [s]'s type substitutions with [t]'s type arguments.
+ List<int> sSupers = typeRulesSupers[sId];
+ if (sSupers.isEmpty) return false;
+ int sSuperIndexOfT = sSupers.indexOf(tId);
+ if (sSuperIndexOfT == -1) return false;
+ assert(sSuperIndexOfT < typeRulesSubstitutions[sId].length);
+
+ List<_Type> substitutions = typeRulesSubstitutions[sId][sSuperIndexOfT];
+
+ // If [sEnv] is null, then create a new environment. Otherwise, we are doing
+ // a recursive type check, so extend the existing environment with [s]'s
+ // type arguments.
+ if (sEnv == null) {
+ sEnv = _Environment.from(s.typeArguments);
+ } else {
+ sEnv.push(s.typeArguments);
+ }
+ bool result =
+ areTypeArgumentsSubtypes(substitutions, sEnv, t.typeArguments, tEnv);
+ sEnv.pop();
+ return result;
+ }
+
+ bool isFunctionSubtype(_FunctionType s, _Environment? sEnv, _FunctionType t,
+ _Environment? tEnv) {
+ if (!isSubtype(s.returnType, sEnv, t.returnType, tEnv)) return false;
// Check [s] does not have more required positional arguments than [t].
int sRequiredCount = s.requiredParameterCount;
@@ -385,7 +524,7 @@
for (int i = 0; i < tPositionalLength; i++) {
_Type sParameter = sPositional[i];
_Type tParameter = tPositional[i];
- if (!isSubtype(tParameter, sParameter)) {
+ if (!isSubtype(tParameter, tEnv, sParameter, sEnv)) {
return false;
}
}
@@ -414,7 +553,8 @@
}
bool tIsRequired = tNamedParameter.isRequired;
if (sIsRequired && !tIsRequired) return false;
- if (!isSubtype(tNamedParameter.type, sNamedParameter.type)) {
+ if (!isSubtype(
+ tNamedParameter.type, tEnv, sNamedParameter.type, sEnv)) {
return false;
}
break;
@@ -428,7 +568,7 @@
// Subtype check based off of sdk/lib/_internal/js_runtime/lib/rti.dart.
// Returns true if [s] is a subtype of [t], false otherwise.
- bool isSubtype(_Type s, _Type t) {
+ bool isSubtype(_Type s, _Environment? sEnv, _Type t, _Environment? tEnv) {
// Reflexivity:
if (identical(s, t)) return true;
@@ -442,7 +582,11 @@
if (isBottomType(s)) return true;
// Left Type Variable Bound 1:
- // TODO(joshualitt): Implement.
+ // TODO(joshualitt): Implement for generic function type parameters.
+ if (s.isInterfaceTypeParameterType) {
+ return isSubtype(
+ sEnv!.lookup(s.as<_InterfaceTypeParameterType>()), sEnv, t, tEnv);
+ }
// Left Null:
// TODO(joshualitt): Combine with 'Right Null', and this can just be:
@@ -459,7 +603,7 @@
// Left FutureOr:
if (s.isFutureOr) {
_FutureOrType sFutureOr = s.as<_FutureOrType>();
- if (!isSubtype(sFutureOr.typeArgument, t)) {
+ if (!isSubtype(sFutureOr.typeArgument, sEnv, t, tEnv)) {
return false;
}
return _isSubtype(sFutureOr.asFuture, t);
@@ -467,7 +611,7 @@
// Left Nullable:
if (s.isNullable) {
- return t.isNullable && isSubtype(s.asNonNullable, t);
+ return t.isNullable && isSubtype(s.asNonNullable, sEnv, t, tEnv);
}
// Type Variable Reflexivity 1 is subsumed by Reflexivity and therefore
@@ -478,15 +622,15 @@
// Right FutureOr:
if (t.isFutureOr) {
_FutureOrType tFutureOr = t.as<_FutureOrType>();
- if (isSubtype(s, tFutureOr.typeArgument)) {
+ if (isSubtype(s, sEnv, tFutureOr.typeArgument, tEnv)) {
return true;
}
- return isSubtype(s, tFutureOr.asFuture);
+ return isSubtype(s, sEnv, tFutureOr.asFuture, tEnv);
}
// Right Nullable:
if (t.isNullable) {
- return isSubtype(s, t.asNonNullable);
+ return isSubtype(s, sEnv, t.asNonNullable, tEnv);
}
// Left Promoted Variable does not apply at runtime.
@@ -506,13 +650,15 @@
}
if (s.isFunction && t.isFunction) {
- return isFunctionSubtype(s.as<_FunctionType>(), t.as<_FunctionType>());
+ return isFunctionSubtype(
+ s.as<_FunctionType>(), sEnv, t.as<_FunctionType>(), tEnv);
}
// Interface Compositionality + Super-Interface:
if (s.isInterface &&
t.isInterface &&
- isInterfaceSubtype(s.as<_InterfaceType>(), t.as<_InterfaceType>())) {
+ isInterfaceSubtype(
+ s.as<_InterfaceType>(), sEnv, t.as<_InterfaceType>(), tEnv)) {
return true;
}
return false;
@@ -523,5 +669,6 @@
@pragma("wasm:entry-point")
bool _isSubtype(Object? s, _Type t) {
- return _typeUniverse.isSubtype(unsafeCast<_Type>(s.runtimeType), t);
+ return _typeUniverse.isSubtype(
+ unsafeCast<_Type>(s.runtimeType), null, t, null);
}
diff --git a/sdk/lib/math/math.dart b/sdk/lib/math/math.dart
index d9e3f75..118d95d 100644
--- a/sdk/lib/math/math.dart
+++ b/sdk/lib/math/math.dart
@@ -12,7 +12,7 @@
/// ## Random
/// [Random] is a generator of [bool], [int] or [double] values.
/// ```dart
-/// var intValue = Random().nextInt(10); // Value is >= 0.0 and < 1.0.
+/// var intValue = Random().nextInt(10); // Value is >= 0 and < 10.
/// var doubleValue = Random().nextDouble(); // Value is >= 0.0 and < 1.0.
/// var boolValue = Random().nextBool(); // true or false, with equal chance.
/// ```
diff --git a/tools/VERSION b/tools/VERSION
index d6a09da..aac23b3 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 18
PATCH 0
-PRERELEASE 198
+PRERELEASE 199
PRERELEASE_PATCH 0
\ No newline at end of file