| // Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| import 'package:analyzer/file_system/file_system.dart'; | |
| import 'package:analyzer/src/dart/analysis/byte_store.dart'; | |
| import 'package:analyzer/src/dart/analysis/driver.dart'; | |
| import 'package:analyzer/src/dart/analysis/file_state.dart'; | |
| import 'package:analyzer/src/dart/analysis/library_context.dart'; | |
| import 'package:analyzer/src/dart/analysis/library_graph.dart'; | |
| import 'package:analyzer/src/dart/analysis/unlinked_unit_store.dart'; | |
| import 'package:analyzer/src/dart/micro/resolve_file.dart'; | |
| import 'package:analyzer/src/utilities/extensions/file_system.dart'; | |
| import 'package:analyzer_utilities/testing/tree_string_sink.dart'; | |
| import 'package:collection/collection.dart'; | |
| import 'package:test/test.dart'; | |
| class AnalyzerStatePrinter { | |
| final ByteStore byteStore; | |
| final UnlinkedUnitStoreImpl unlinkedUnitStore; | |
| final IdProvider idProvider; | |
| final LibraryContext libraryContext; | |
| final AnalyzerStatePrinterConfiguration configuration; | |
| final ResourceProvider resourceProvider; | |
| final TreeStringSink sink; | |
| final bool withKeysGetPut; | |
| final Set<LibraryCycle> _libraryCyclesWithWrittenDetails = Set.identity(); | |
| AnalyzerStatePrinter({ | |
| required this.byteStore, | |
| required this.unlinkedUnitStore, | |
| required this.idProvider, | |
| required this.libraryContext, | |
| required this.configuration, | |
| required this.resourceProvider, | |
| required this.sink, | |
| required this.withKeysGetPut, | |
| }); | |
| FileSystemState get fileSystemState => libraryContext.fileSystemState; | |
| void writeAnalysisDriver(AnalysisDriverTestView testData) { | |
| sink.writelnWithIndent('driver'); | |
| sink.withIndent(() { | |
| _writeWorkState(testData); | |
| _writeFileSystemState(testData.fileSystem); | |
| _writeLibraryContextState(testData.libraryContext); | |
| }); | |
| } | |
| void writeFileResolver(FileResolverTestData testData) { | |
| _writeFiles(testData.fileSystem); | |
| _writeLibraryContext(testData.libraryContext); | |
| _writeElementFactory(); | |
| _writeUnlinkedUnitStore(); | |
| _writeByteStore(); | |
| } | |
| String _stringOfLibraryCycle(LibraryCycle cycle) { | |
| if (configuration.omitSdkFiles) { | |
| var isSdkLibrary = cycle.libraries.any((library) { | |
| return library.file.uri.isScheme('dart'); | |
| }); | |
| if (isSdkLibrary) { | |
| if (cycle.libraries.any((e) => e.file.uriStr == 'dart:core')) { | |
| return 'dart:core'; | |
| } else if (cycle.libraries.any( | |
| (e) => e.file.uriStr == 'dart:collection', | |
| )) { | |
| return 'dart:collection'; | |
| } else if (cycle.libraries.any((e) => e.file.uriStr == 'dart:io')) { | |
| return 'dart:io'; | |
| } else { | |
| throw UnimplementedError('$cycle'); | |
| } | |
| } | |
| } | |
| return idProvider.libraryCycle(cycle); | |
| } | |
| String _stringOfUriStr(String uriStr) { | |
| if (uriStr.trim().isEmpty) { | |
| return "'$uriStr'"; | |
| } else { | |
| return uriStr; | |
| } | |
| } | |
| void _verifyKnownFiles() { | |
| var uriFiles = fileSystemState.test.uriToFile.values.toSet(); | |
| var pathFiles = fileSystemState.test.uriToFile.values.toSet(); | |
| expect(pathFiles.difference(uriFiles), isEmpty); | |
| expect(uriFiles.difference(pathFiles), isEmpty); | |
| var knownFilesNotInUriFiles = fileSystemState.knownFiles.toSet(); | |
| knownFilesNotInUriFiles.removeAll(uriFiles); | |
| expect(knownFilesNotInUriFiles, isEmpty); | |
| } | |
| void _writeByteStore() { | |
| sink.writelnWithIndent('byteStore'); | |
| sink.withIndent(() { | |
| switch (byteStore) { | |
| case CiderByteStore byteStore: | |
| var groups = byteStore.map.entries.groupListsBy((element) { | |
| return element.value.refCount; | |
| }); | |
| for (var groupEntry in groups.entries) { | |
| var keys = groupEntry.value.map((e) => e.key).toList(); | |
| var shortKeys = idProvider.shortKeys(keys)..sort(); | |
| sink.writelnWithIndent('${groupEntry.key}: $shortKeys'); | |
| } | |
| default: | |
| throw UnimplementedError('${byteStore.runtimeType}'); | |
| } | |
| }); | |
| } | |
| void _writeDocImports(FileKind container) { | |
| _writeElements<LibraryImportState>( | |
| 'docLibraryImports', | |
| container.docLibraryImports, | |
| (import) { | |
| expect(import.isDocImport, isTrue); | |
| _writeLibraryImport(container, import); | |
| }, | |
| ); | |
| } | |
| void _writeElementFactory() { | |
| sink.writelnWithIndent('elementFactory'); | |
| sink.withIndent(() { | |
| var elementFactory = libraryContext.elementFactory; | |
| _writeUriList('hasElement', elementFactory.uriListWithLibraryElements); | |
| _writeUriList('hasReader', elementFactory.uriListWithLibraryReaders); | |
| }); | |
| } | |
| void _writeElements<T>(String name, List<T> elements, void Function(T) f) { | |
| if (elements.isNotEmpty) { | |
| sink.writelnWithIndent(name); | |
| sink.withIndent(() { | |
| for (var element in elements) { | |
| f(element); | |
| } | |
| }); | |
| } | |
| } | |
| void _writeFile(FileState file) { | |
| sink.withIndent(() { | |
| sink.writelnWithIndent('id: ${idProvider.fileState(file)}'); | |
| _writeFileContent(file); | |
| _writeFileKind(file); | |
| _writeReferencingFiles(file); | |
| _writeFileUnlinkedKey(file); | |
| }); | |
| } | |
| void _writeFileContent(FileState file) { | |
| if (configuration.filesToPrintContent.any((e) => e.path == file.path)) { | |
| sink.writelnWithIndent('content\n---\n${file.content}---'); | |
| } | |
| } | |
| void _writeFileKind(FileState file) { | |
| var kind = file.kind; | |
| expect(kind.file, same(file)); | |
| sink.writelnWithIndent('kind: ${idProvider.fileKind(kind)}'); | |
| switch (kind) { | |
| case LibraryFileKind(): | |
| expect(kind.library, same(kind)); | |
| sink.withIndent(() { | |
| var name = kind.name; | |
| if (name != null) { | |
| sink.writelnWithIndent('name: $name'); | |
| } | |
| _writeLibraryImports(kind); | |
| _writeLibraryExports(kind); | |
| _writePartIncludes(kind); | |
| _writeDocImports(kind); | |
| var filesIds = kind.fileKinds.map(idProvider.fileKind); | |
| sink.writelnWithIndent('fileKinds: ${filesIds.join(' ')}'); | |
| _writeLibraryCycle(kind); | |
| }); | |
| case PartOfNameFileKind(): | |
| sink.withIndent(() { | |
| var libraries = kind.libraries; | |
| if (libraries.isNotEmpty) { | |
| var keys = libraries | |
| .map(idProvider.fileKind) | |
| .sorted(compareNatural) | |
| .join(' '); | |
| sink.writelnWithIndent('libraries: $keys'); | |
| } | |
| var library = kind.library; | |
| if (library != null) { | |
| var id = idProvider.fileKind(library); | |
| sink.writelnWithIndent('library: $id'); | |
| } else { | |
| sink.writelnWithIndent('name: ${kind.unlinked.name}'); | |
| } | |
| _writeLibraryImports(kind); | |
| _writeLibraryExports(kind); | |
| _writePartIncludes(kind); | |
| _writeDocImports(kind); | |
| }); | |
| case PartOfUriKnownFileKind(): | |
| sink.withIndent(() { | |
| var uriFileId = idProvider.fileState(kind.uriFile); | |
| sink.writelnWithIndent('uriFile: $uriFileId'); | |
| if (kind.library case var library?) { | |
| var id = idProvider.fileKind(library); | |
| sink.writelnWithIndent('library: $id'); | |
| } | |
| _writeLibraryImports(kind); | |
| _writeLibraryExports(kind); | |
| _writePartIncludes(kind); | |
| _writeDocImports(kind); | |
| }); | |
| case PartOfUriUnknownFileKind(): | |
| sink.withIndent(() { | |
| sink.writelnWithIndent('uri: ${kind.unlinked.uri}'); | |
| expect(kind.library, isNull); | |
| }); | |
| default: | |
| throw UnimplementedError('${kind.runtimeType}'); | |
| } | |
| } | |
| void _writeFiles(FileSystemTestData testData) { | |
| fileSystemState.discoverReferencedFiles(); | |
| _verifyKnownFiles(); | |
| // Discover libraries for parts. | |
| // This is required for consistency checking. | |
| for (var fileData in testData.files.values.toList()) { | |
| var current = fileSystemState.getExisting(fileData.file); | |
| if (current != null) { | |
| var kind = current.kind; | |
| if (kind is PartOfNameFileKind) { | |
| kind.discoverLibraries(); | |
| } | |
| } | |
| } | |
| // This is required for consistency checking. | |
| fileSystemState.discoverReferencedFiles(); | |
| // Sort, mostly by path. | |
| // But sort SDK libraries to the end, with `dart:core` first. | |
| var fileDataList = testData.files.values.toList(); | |
| fileDataList.sort((first, second) { | |
| var firstPath = first.file.path; | |
| var secondPath = second.file.path; | |
| if (configuration.omitSdkFiles) { | |
| var firstUri = first.uri; | |
| var secondUri = second.uri; | |
| var firstIsSdk = firstUri.isScheme('dart'); | |
| var secondIsSdk = secondUri.isScheme('dart'); | |
| if (firstIsSdk && !secondIsSdk) { | |
| return 1; | |
| } else if (!firstIsSdk && secondIsSdk) { | |
| return -1; | |
| } else if (firstIsSdk && secondIsSdk) { | |
| if ('$firstUri' == 'dart:core') { | |
| return -1; | |
| } else if ('$secondUri' == 'dart:core') { | |
| return 1; | |
| } | |
| } | |
| } | |
| return firstPath.compareTo(secondPath); | |
| }); | |
| // Ask ID for every file in the sorted order, so that IDs are nice. | |
| // Register objects that can be referenced. | |
| idProvider.resetRegisteredObject(); | |
| for (var fileData in fileDataList) { | |
| var current = fileSystemState.getExisting(fileData.file); | |
| if (current != null) { | |
| idProvider.registerFileState(current); | |
| var kind = current.kind; | |
| idProvider.registerFileKind(kind); | |
| if (kind is LibraryFileKind) { | |
| idProvider.registerLibraryCycle(kind.libraryCycle); | |
| } | |
| } | |
| } | |
| sink.writelnWithIndent('files'); | |
| sink.withIndent(() { | |
| for (var fileData in fileDataList) { | |
| if (configuration.omitSdkFiles && fileData.uri.isScheme('dart')) { | |
| continue; | |
| } | |
| var file = fileData.file; | |
| sink.writelnWithIndent(file.posixPath); | |
| sink.withIndent(() { | |
| sink.writelnWithIndent('uri: ${fileData.uri}'); | |
| var current = fileSystemState.getExisting(file); | |
| if (current != null) { | |
| sink.writelnWithIndent('current'); | |
| _writeFile(current); | |
| } | |
| if (withKeysGetPut) { | |
| var shortGets = idProvider.shortKeys(fileData.unlinkedKeyGet); | |
| var shortPuts = idProvider.shortKeys(fileData.unlinkedKeyPut); | |
| sink.writelnWithIndent('unlinkedGet: $shortGets'); | |
| sink.writelnWithIndent('unlinkedPut: $shortPuts'); | |
| } | |
| }); | |
| } | |
| }); | |
| } | |
| void _writeFileSystemState(FileSystemTestData testData) { | |
| sink.writelnWithIndent('fileSystemState'); | |
| sink.withIndent(() { | |
| _writeFiles(testData); | |
| }); | |
| } | |
| void _writeFileTracker(AnalysisDriverTestView testData) { | |
| var fileTracker = testData.fileTracker; | |
| var fileTrackerTestData = fileTracker.testView; | |
| sink.writelnWithIndent('fileTracker'); | |
| sink.withIndent(() { | |
| sink.writelnWithIndent('anyPendingFile: ${fileTracker.anyPendingFile}'); | |
| sink.writelnWithIndent( | |
| 'numberOfPendingFiles: ${fileTracker.numberOfPendingFiles}', | |
| ); | |
| _writeSortedPathSet('addedFiles', fileTracker.addedFiles); | |
| _writeSortedPathSet('changedFiles', fileTrackerTestData.changedFiles); | |
| _writeSortedPathSet( | |
| 'pendingChangedFiles', | |
| fileTrackerTestData.pendingChangedFiles, | |
| ); | |
| _writeSortedPathSet( | |
| 'pendingImportFiles', | |
| fileTrackerTestData.pendingImportFiles, | |
| ); | |
| _writeSortedPathSet( | |
| 'pendingErrorFiles', | |
| fileTrackerTestData.pendingErrorFiles, | |
| ); | |
| _writeSortedPathSet('pendingFiles', fileTrackerTestData.pendingFiles); | |
| }); | |
| } | |
| void _writeFileUnlinkedKey(FileState file) { | |
| var unlinkedShort = idProvider.shortKey(file.unlinkedKey); | |
| sink.writelnWithIndent('unlinkedKey: $unlinkedShort'); | |
| } | |
| void _writeLibraryContext(LibraryContextTestData testData) { | |
| sink.writelnWithIndent('libraryCycles'); | |
| sink.withIndent(() { | |
| var cyclesToPrint = <_LibraryCycleToPrint>[]; | |
| for (var entry in testData.libraryCycles.entries) { | |
| if (configuration.omitSdkFiles && | |
| entry.key.any((e) => e.uri.isScheme('dart'))) { | |
| continue; | |
| } | |
| cyclesToPrint.add( | |
| _LibraryCycleToPrint( | |
| entry.key.map((e) => e.file.posixPath).join(' '), | |
| entry.value, | |
| ), | |
| ); | |
| } | |
| cyclesToPrint.sortBy((e) => e.pathListStr); | |
| var loadedBundlesMap = Map.fromEntries( | |
| libraryContext.loadedBundles.map((cycle) { | |
| var pathListStr = cycle.libraries | |
| .map((library) => library.file.resource.posixPath) | |
| .sorted() | |
| .join(' '); | |
| return MapEntry(pathListStr, cycle); | |
| }), | |
| ); | |
| for (var cycleToPrint in cyclesToPrint) { | |
| sink.writelnWithIndent(cycleToPrint.pathListStr); | |
| sink.withIndent(() { | |
| var current = loadedBundlesMap[cycleToPrint.pathListStr]; | |
| if (current != null) { | |
| var id = idProvider.libraryCycle(current); | |
| sink.writelnWithIndent('current: $id'); | |
| sink.withIndent(() { | |
| // TODO(scheglov): Print it with the cycle instead? | |
| var short = idProvider.shortKey(current.linkedKey); | |
| sink.writelnWithIndent('key: $short'); | |
| }); | |
| } | |
| var cycleData = cycleToPrint.data; | |
| var shortGets = idProvider.shortKeys(cycleData.getKeys); | |
| var shortPuts = idProvider.shortKeys(cycleData.putKeys); | |
| sink.writelnWithIndent('get: $shortGets'); | |
| sink.writelnWithIndent('put: $shortPuts'); | |
| }); | |
| } | |
| }); | |
| } | |
| void _writeLibraryContextState(LibraryContextTestData testData) { | |
| sink.writelnWithIndent('libraryContext'); | |
| sink.withIndent(() { | |
| _writeLibraryContext(testData); | |
| _writeElementFactory(); | |
| }); | |
| } | |
| void _writeLibraryCycle(LibraryFileKind library) { | |
| var cycle = library.libraryCycle; | |
| sink.writelnWithIndent(idProvider.libraryCycle(cycle)); | |
| if (!_libraryCyclesWithWrittenDetails.add(cycle)) { | |
| return; | |
| } | |
| sink.withIndent(() { | |
| var dependencyIds = cycle.directDependencies | |
| .map(_stringOfLibraryCycle) | |
| .sorted(compareNatural) | |
| .join(' '); | |
| if (dependencyIds.isNotEmpty) { | |
| sink.writelnWithIndent('dependencies: $dependencyIds'); | |
| } else { | |
| sink.writelnWithIndent('dependencies: none'); | |
| } | |
| var libraryIds = cycle.libraries | |
| .map(idProvider.fileKind) | |
| .sorted(compareNatural) | |
| .join(' '); | |
| sink.writelnWithIndent('libraries: $libraryIds'); | |
| sink.writelnWithIndent(idProvider.apiSignature(cycle.apiSignature)); | |
| var userIds = cycle.directUsers | |
| .map(_stringOfLibraryCycle) | |
| .sorted(compareNatural) | |
| .join(' '); | |
| if (userIds.isNotEmpty) { | |
| sink.writelnWithIndent('users: $userIds'); | |
| } | |
| }); | |
| } | |
| void _writeLibraryExport( | |
| FileKind container, | |
| LibraryExportState<DirectiveUri> export, | |
| ) { | |
| expect(export.container, same(container)); | |
| switch (export) { | |
| case LibraryExportWithFile(): | |
| sink.writeIndentedLine(() { | |
| var file = export.exportedFile; | |
| var exportedLibrary = export.exportedLibrary; | |
| if (exportedLibrary != null) { | |
| expect(exportedLibrary.file, file); | |
| sink.write(idProvider.fileKind(exportedLibrary)); | |
| } else { | |
| sink.write('notLibrary ${idProvider.fileState(file)}'); | |
| } | |
| if (configuration.omitSdkFiles && file.uri.isScheme('dart')) { | |
| sink.write(' ${file.uri}'); | |
| } | |
| }); | |
| case LibraryExportWithInSummarySource(): | |
| sink.writeIndentedLine(() { | |
| sink.write('inSummary ${export.exportedSource.uri}'); | |
| var librarySource = export.exportedLibrarySource; | |
| if (librarySource != null) { | |
| expect(librarySource, same(export.exportedSource)); | |
| } else { | |
| sink.write(' notLibrary'); | |
| } | |
| }); | |
| case LibraryExportWithUri(): | |
| sink.writelnWithIndent('uri: ${export.selectedUri.relativeUri}'); | |
| case LibraryExportWithUriStr(): | |
| var uriStr = _stringOfUriStr(export.selectedUri.relativeUriStr); | |
| sink.writelnWithIndent('uriStr: $uriStr'); | |
| default: | |
| sink.writelnWithIndent('noUriStr'); | |
| } | |
| } | |
| void _writeLibraryExports(FileKind container) { | |
| _writeElements<LibraryExportState>( | |
| 'libraryExports', | |
| container.libraryExports, | |
| (export) { | |
| _writeLibraryExport(container, export); | |
| }, | |
| ); | |
| } | |
| void _writeLibraryImport( | |
| FileKind container, | |
| LibraryImportState<DirectiveUri> import, | |
| ) { | |
| expect(import.container, same(container)); | |
| switch (import) { | |
| case LibraryImportWithFile(): | |
| sink.writeIndentedLine(() { | |
| var file = import.importedFile; | |
| var importedLibrary = import.importedLibrary; | |
| if (importedLibrary != null) { | |
| expect(importedLibrary.file, file); | |
| sink.write(idProvider.fileKind(importedLibrary)); | |
| } else { | |
| sink.write('notLibrary ${idProvider.fileState(file)}'); | |
| } | |
| if (configuration.omitSdkFiles && file.uri.isScheme('dart')) { | |
| sink.write(' ${file.uri}'); | |
| } | |
| _writeLibraryImportFlags(import); | |
| }); | |
| case LibraryImportWithInSummarySource(): | |
| sink.writeIndentedLine(() { | |
| sink.write('inSummary ${import.importedSource.uri}'); | |
| var librarySource = import.importedLibrarySource; | |
| if (librarySource != null) { | |
| expect(librarySource, same(import.importedSource)); | |
| } else { | |
| sink.write(' notLibrary'); | |
| } | |
| _writeLibraryImportFlags(import); | |
| }); | |
| case LibraryImportWithUri(): | |
| sink.writeIndentedLine(() { | |
| sink.write('uri: ${import.selectedUri.relativeUri}'); | |
| _writeLibraryImportFlags(import); | |
| }); | |
| case LibraryImportWithUriStr(): | |
| sink.writeIndentedLine(() { | |
| var uriStr = _stringOfUriStr(import.selectedUri.relativeUriStr); | |
| sink.write('uriStr: $uriStr'); | |
| _writeLibraryImportFlags(import); | |
| }); | |
| default: | |
| sink.writelnWithIndent('noUriStr'); | |
| } | |
| } | |
| /// Writes the unlinked properties of [import] that are not visible | |
| /// through the imported file: the prefix, the combinators, and whether | |
| /// it is synthetic. | |
| void _writeLibraryImportFlags(LibraryImportState import) { | |
| if (import.unlinked.prefix case var prefix?) { | |
| if (prefix.deferredOffset != null) { | |
| sink.write(' deferred'); | |
| } | |
| sink.write(' as ${prefix.name?.name ?? '<missing>'}'); | |
| } | |
| for (var combinator in import.unlinked.combinators) { | |
| var keyword = combinator.isShow ? 'show' : 'hide'; | |
| sink.write(' $keyword ${combinator.names.join(', ')}'); | |
| } | |
| if (import.isSyntheticDartCore) { | |
| sink.write(' synthetic'); | |
| } | |
| } | |
| void _writeLibraryImports(FileKind container) { | |
| _writeElements<LibraryImportState>( | |
| 'libraryImports', | |
| container.libraryImports, | |
| (import) { | |
| expect(import.isDocImport, isFalse); | |
| _writeLibraryImport(container, import); | |
| }, | |
| ); | |
| } | |
| void _writePartIncludes(FileKind container) { | |
| _writeElements<PartIncludeState>('partIncludes', container.partIncludes, ( | |
| part, | |
| ) { | |
| expect(part.container, same(container)); | |
| switch (part) { | |
| case PartIncludeWithFile(): | |
| sink.writeIndentedLine(() { | |
| var file = part.includedFile; | |
| var includedPart = part.includedPart; | |
| if (includedPart != null) { | |
| expect(includedPart.file, file); | |
| sink.write(idProvider.fileKind(includedPart)); | |
| } else { | |
| sink.write('notPart ${idProvider.fileState(file)}'); | |
| } | |
| }); | |
| case PartIncludeWithUri(): | |
| var uriStr = _stringOfUriStr(part.selectedUri.relativeUriStr); | |
| sink.writelnWithIndent('uri: $uriStr'); | |
| default: | |
| sink.writelnWithIndent('noUri'); | |
| } | |
| }); | |
| } | |
| void _writeReferencingFiles(FileState file) { | |
| var referencingFiles = file.referencingFiles; | |
| if (referencingFiles.isNotEmpty) { | |
| var fileIds = referencingFiles | |
| .map(idProvider.fileState) | |
| .sorted(compareNatural) | |
| .join(' '); | |
| sink.writelnWithIndent('referencingFiles: $fileIds'); | |
| } | |
| } | |
| void _writeSortedPathSet(String name, Set<String> paths) { | |
| var sorted = paths.map((path) { | |
| return resourceProvider.getFile(path).posixPath; | |
| }).sorted(); | |
| sink.writeElements(name, sorted, (value) { | |
| sink.writelnWithIndent(value); | |
| }); | |
| } | |
| void _writeStringList(String name, List<String> elements) { | |
| sink.writeElements(name, elements, (element) { | |
| sink.writelnWithIndent(element); | |
| }); | |
| } | |
| void _writeUnlinkedUnitStore() { | |
| sink.writelnWithIndent('unlinkedUnitStore'); | |
| sink.withIndent(() { | |
| var groups = unlinkedUnitStore.map.entries.groupListsBy((element) { | |
| return element.value.usageCount; | |
| }); | |
| for (var groupEntry in groups.entries) { | |
| var keys = groupEntry.value.map((e) => e.key).toList(); | |
| var shortKeys = idProvider.shortKeys(keys)..sort(); | |
| sink.writelnWithIndent('${groupEntry.key}: $shortKeys'); | |
| } | |
| }); | |
| } | |
| void _writeUriList(String name, Iterable<Uri> uriIterable) { | |
| var uriStrList = <String>[]; | |
| for (var uri in uriIterable) { | |
| if (configuration.omitSdkFiles && uri.isScheme('dart')) { | |
| continue; | |
| } | |
| uriStrList.add('$uri'); | |
| } | |
| if (uriStrList.isNotEmpty) { | |
| uriStrList.sort(); | |
| sink.writelnWithIndent(name); | |
| sink.withIndent(() { | |
| for (var uriStr in uriStrList) { | |
| sink.writelnWithIndent(uriStr); | |
| } | |
| }); | |
| } | |
| } | |
| void _writeWorkState(AnalysisDriverTestView testData) { | |
| sink.writelnWithIndent('workState'); | |
| sink.withIndent(() { | |
| _writeSortedPathSet('priorityFiles', testData.priorityFiles); | |
| _writeStringList( | |
| 'pendingFileChanges', | |
| testData.pendingFileChanges.map((change) { | |
| var posixPath = resourceProvider.getFile(change.path).posixPath; | |
| return '${change.kind.name} $posixPath'; | |
| }).toList(), | |
| ); | |
| _writeFileTracker(testData); | |
| expect( | |
| testData.numberOfFilesToAnalyze, | |
| testData.fileTracker.numberOfPendingFiles, | |
| ); | |
| sink.writelnWithIndent('workPriority: ${testData.workPriority.name}'); | |
| }); | |
| } | |
| } | |
| class AnalyzerStatePrinterConfiguration { | |
| Set<File> filesToPrintContent = {}; | |
| bool omitSdkFiles = true; | |
| } | |
| /// Encoder of object identifies into short identifiers. | |
| class IdProvider { | |
| final Map<FileState, String> _fileState = Map.identity(); | |
| final Map<LibraryCycle, String> _libraryCycle = Map.identity(); | |
| final Map<FileKind, String> _fileKind = Map.identity(); | |
| final Map<String, String> _keyToShort = {}; | |
| final Map<String, String> _shortToKey = {}; | |
| final Map<String, String> _apiSignature = {}; | |
| Set<FileState> _currentFiles = {}; | |
| Set<FileKind> _currentFileKinds = {}; | |
| Set<LibraryCycle> _currentCycles = {}; | |
| String apiSignature(String signature) { | |
| var length = _apiSignature.length; | |
| return _apiSignature[signature] ??= 'apiSignature_$length'; | |
| } | |
| String fileKind(FileKind kind) { | |
| if (!_currentFileKinds.contains(kind)) { | |
| throw StateError('$kind'); | |
| } | |
| return _fileKind[kind] ??= () { | |
| switch (kind) { | |
| case LibraryFileKind(): | |
| return 'library_${_fileKind.length}'; | |
| case PartOfNameFileKind(): | |
| return 'partOfName_${_fileKind.length}'; | |
| case PartOfUriKnownFileKind(): | |
| return 'partOfUriKnown_${_fileKind.length}'; | |
| case PartFileKind(): | |
| return 'partOfUriUnknown_${_fileKind.length}'; | |
| default: | |
| throw UnimplementedError('${kind.runtimeType}'); | |
| } | |
| }(); | |
| } | |
| String fileState(FileState file) { | |
| if (!_currentFiles.contains(file)) { | |
| throw StateError('$file'); | |
| } | |
| return _fileState[file] ??= 'file_${_fileState.length}'; | |
| } | |
| String libraryCycle(LibraryCycle cycle) { | |
| if (!_currentCycles.contains(cycle)) { | |
| throw StateError('$cycle'); | |
| } | |
| return _libraryCycle[cycle] ??= 'cycle_${_libraryCycle.length}'; | |
| } | |
| /// Register that [kind] is an object that can be referenced. | |
| void registerFileKind(FileKind kind) { | |
| if (_currentFileKinds.contains(kind)) { | |
| throw StateError('Duplicate: $kind'); | |
| } | |
| _currentFileKinds.add(kind); | |
| fileKind(kind); | |
| } | |
| /// Register that [file] is an object that can be referenced. | |
| void registerFileState(FileState file) { | |
| if (_currentFiles.contains(file)) { | |
| throw StateError('Duplicate: $file'); | |
| } | |
| _currentFiles.add(file); | |
| fileState(file); | |
| } | |
| /// Register that [cycle] is an object that can be referenced. | |
| void registerLibraryCycle(LibraryCycle cycle) { | |
| _currentCycles.add(cycle); | |
| libraryCycle(cycle); | |
| } | |
| void resetRegisteredObject() { | |
| _currentFiles = {}; | |
| _currentFileKinds = {}; | |
| _currentCycles = {}; | |
| } | |
| String shortKey(String key) { | |
| var short = _keyToShort[key]; | |
| if (short == null) { | |
| short = 'k${_keyToShort.length.toString().padLeft(2, '0')}'; | |
| _keyToShort[key] = short; | |
| _shortToKey[short] = key; | |
| } | |
| return short; | |
| } | |
| List<String> shortKeys(List<String> keys) { | |
| return keys.map(shortKey).toList(); | |
| } | |
| } | |
| class _LibraryCycleToPrint { | |
| final String pathListStr; | |
| final LibraryCycleTestData data; | |
| _LibraryCycleToPrint(this.pathListStr, this.data); | |
| } |