Index: pkg/front_end/lib/src/incremental/file_state.dart |
diff --git a/pkg/front_end/lib/src/incremental/file_state.dart b/pkg/front_end/lib/src/incremental/file_state.dart |
index c1bcfeb17b0ff2dc245f361a5f7f13aff77f5ead..3012f6db3a807085b248cad0ad6e1c2216a0a84a 100644 |
--- a/pkg/front_end/lib/src/incremental/file_state.dart |
+++ b/pkg/front_end/lib/src/incremental/file_state.dart |
@@ -6,6 +6,7 @@ import 'dart:async'; |
import 'dart:typed_data'; |
import 'package:front_end/file_system.dart'; |
+import 'package:front_end/src/dependency_walker.dart' as graph; |
import 'package:front_end/src/fasta/parser/top_level_parser.dart'; |
import 'package:front_end/src/fasta/scanner.dart'; |
import 'package:front_end/src/fasta/source/directive_listener.dart'; |
@@ -27,37 +28,40 @@ class FileState { |
bool _exists; |
List<int> _content; |
- List<FileState> _importedFiles; |
- List<FileState> _exportedFiles; |
+ List<FileState> _importedLibraries; |
+ List<FileState> _exportedLibraries; |
List<FileState> _partFiles; |
- Set<FileState> _directReferencedFiles = new Set<FileState>(); |
+ List<FileState> _directReferencedLibraries = <FileState>[]; |
FileState._(this._fsState, this.fileUri); |
/// The content of the file. |
List<int> get content => _content; |
+ /// Libraries that this library file directly imports or exports. |
+ List<FileState> get directReferencedLibraries => _directReferencedLibraries; |
+ |
/// Whether the file exists. |
bool get exists => _exists; |
+ /// The list of the libraries exported by this library. |
+ List<FileState> get exportedLibraries => _exportedLibraries; |
+ |
@override |
int get hashCode => fileUri.hashCode; |
- /// Return the set of transitive files - the file itself and all of the |
- /// directly or indirectly referenced files. |
- Set<FileState> get transitiveFiles { |
- // TODO(scheglov) add caching. |
- var transitiveFiles = new Set<FileState>(); |
+ /// The list of the libraries imported by this library. |
+ List<FileState> get importedLibraries => _importedLibraries; |
- void appendReferenced(FileState file) { |
- if (transitiveFiles.add(file)) { |
- file._directReferencedFiles.forEach(appendReferenced); |
- } |
- } |
+ /// The list of files this library file references as parts. |
+ List<FileState> get partFiles => _partFiles; |
- appendReferenced(this); |
- return transitiveFiles; |
+ /// Return topologically sorted cycles of dependencies for this library. |
+ List<LibraryCycle> get topologicalOrder { |
+ var libraryWalker = new _LibraryWalker(); |
+ libraryWalker.walk(libraryWalker.getNode(this)); |
+ return libraryWalker.topologicallySortedCycles; |
} |
@override |
@@ -84,25 +88,31 @@ class FileState { |
new TopLevelParser(listener).parseUnit(scannerResults.tokens); |
// Build the graph. |
- _importedFiles = <FileState>[]; |
- _exportedFiles = <FileState>[]; |
+ _importedLibraries = <FileState>[]; |
+ _exportedLibraries = <FileState>[]; |
_partFiles = <FileState>[]; |
- await _addFileForRelativeUri(_importedFiles, 'dart:core'); |
+ await _addFileForRelativeUri(_importedLibraries, 'dart:core'); |
for (String uri in listener.imports) { |
- await _addFileForRelativeUri(_importedFiles, uri); |
+ await _addFileForRelativeUri(_importedLibraries, uri); |
} |
for (String uri in listener.exports) { |
- await _addFileForRelativeUri(_exportedFiles, uri); |
+ await _addFileForRelativeUri(_exportedLibraries, uri); |
} |
for (String uri in listener.parts) { |
await _addFileForRelativeUri(_partFiles, uri); |
} |
- // Compute referenced files. |
- _directReferencedFiles = new Set<FileState>() |
- ..addAll(_importedFiles) |
- ..addAll(_exportedFiles) |
- ..addAll(_partFiles); |
+ // Compute referenced libraries. |
+ _directReferencedLibraries = (new Set<FileState>() |
+ ..addAll(_importedLibraries) |
+ ..addAll(_exportedLibraries)) |
+ .toList(); |
+ } |
+ |
+ @override |
+ String toString() { |
+ if (fileUri.scheme == 'file') return fileUri.path; |
+ return fileUri.toString(); |
} |
/// Add the [FileState] for the given [relativeUri] to the [files]. |
@@ -178,6 +188,14 @@ class FileSystemState { |
} |
} |
+/// List of libraries that reference each other, so form a cycle. |
+class LibraryCycle { |
+ final List<FileState> libraries = <FileState>[]; |
+ |
+ @override |
+ String toString() => '[' + libraries.join(', ') + ']'; |
+} |
+ |
/// [FileSystemState] based implementation of [FileSystem]. |
/// It provides a consistent view on the known file system state. |
class _FileSystemView implements FileSystem { |
@@ -226,3 +244,45 @@ class _FileSystemViewEntry implements FileSystemEntity { |
throw new StateError('The method should not be invoked.'); |
} |
} |
+ |
+/// Node in [_LibraryWalker]. |
+class _LibraryNode extends graph.Node<_LibraryNode> { |
+ final _LibraryWalker walker; |
+ final FileState file; |
+ |
+ @override |
+ bool isEvaluated = false; |
+ |
+ _LibraryNode(this.walker, this.file); |
+ |
+ @override |
+ List<_LibraryNode> computeDependencies() { |
+ return file.directReferencedLibraries.map(walker.getNode).toList(); |
+ } |
+} |
+ |
+/// Helper that organizes dependencies of a library into topologically |
+/// sorted [LibraryCycle]s. |
+class _LibraryWalker extends graph.DependencyWalker<_LibraryNode> { |
+ final nodesOfFiles = <FileState, _LibraryNode>{}; |
+ final topologicallySortedCycles = <LibraryCycle>[]; |
+ |
+ @override |
+ void evaluate(_LibraryNode v) { |
+ evaluateScc([v]); |
+ } |
+ |
+ @override |
+ void evaluateScc(List<_LibraryNode> scc) { |
+ var cycle = new LibraryCycle(); |
+ for (var node in scc) { |
+ node.isEvaluated = true; |
+ cycle.libraries.add(node.file); |
+ } |
+ topologicallySortedCycles.add(cycle); |
+ } |
+ |
+ _LibraryNode getNode(FileState file) { |
+ return nodesOfFiles.putIfAbsent(file, () => new _LibraryNode(this, file)); |
+ } |
+} |