Index: pkg/front_end/lib/dependency_grapher.dart |
diff --git a/pkg/front_end/lib/dependency_grapher.dart b/pkg/front_end/lib/dependency_grapher.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b256a78e7db77c0abeffeb74a625f143a64be842 |
--- /dev/null |
+++ b/pkg/front_end/lib/dependency_grapher.dart |
@@ -0,0 +1,163 @@ |
+// Copyright (c) 2016, 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 'dart:async'; |
+ |
+import 'package:analyzer/dart/ast/ast.dart'; |
+import 'package:analyzer/error/listener.dart'; |
+import 'package:analyzer/src/dart/scanner/reader.dart'; |
+import 'package:analyzer/src/generated/parser.dart'; |
+import 'package:front_end/file_system.dart'; |
+import 'package:front_end/src/async_dependency_walker.dart'; |
+import 'package:front_end/src/base/uri_resolver.dart'; |
+import 'package:front_end/src/scanner/scanner.dart'; |
+ |
+import 'compiler_options.dart'; |
+ |
+/// Generates a representation of the dependency graph of a program. |
+/// |
+/// Given the Uri of one or more files, this function follows `import`, |
+/// `export`, and `part` declarations to discover a graph of all files involved |
+/// in the program. |
+Future<Graph> graphForProgram( |
+ List<Uri> sources, CompilerOptions options) async { |
+ var packages = <String, Uri>{}; // TODO(paulberry): support packages |
Siggi Cherem (dart-lang)
2016/12/15 23:51:33
should we have a "digested options" at some point
Paul Berry
2016/12/16 02:46:45
Perhaps, I'm not sure yet. I kind of want to fill
|
+ var sdkLibraries = <String, Uri>{}; // TODO(paulberry): support SDK libraries |
+ var uriResolver = |
+ new UriResolver(packages, sdkLibraries, options.fileSystem.context); |
+ var walker = new _Walker(options.fileSystem, uriResolver); |
+ var startingPoint = new _StartingPoint(walker, sources); |
+ await walker.walk(startingPoint); |
+ return walker.graph; |
+} |
+ |
+/// A representation of the dependency graph of a program. |
+/// |
+/// Not intended to be extended, implemented, or mixed in by clients. |
+class Graph { |
Siggi Cherem (dart-lang)
2016/12/15 23:51:33
as we start looking into integrating alternative i
Paul Berry
2016/12/16 02:46:45
Acknowledged.
|
+ /// A list of all library cycles in the program, in topologically sorted order |
+ /// (each cycle only depends on libraries in the cycles that precede it). |
+ final topologicallySortedCycles = <LibraryCycleNode>[]; |
+ |
+ Graph._(); |
+} |
+ |
+/// A representation of a single library cycle in the dependency graph of a |
+/// program. |
+/// |
+/// Not intended to be extended, implemented, or mixed in by clients. |
+class LibraryCycleNode { |
+ /// A map of all the libraries in the program, keyed by the URI of their |
Siggi Cherem (dart-lang)
2016/12/15 23:51:33
all libraries in the program or in the cycle?
Paul Berry
2016/12/16 02:46:45
Whoops, I meant to say "in the cycle". Fixed.
|
+ /// defining compilation unit. |
+ final libraries = <Uri, LibraryNode>{}; |
+ |
+ LibraryCycleNode._(); |
+} |
+ |
+/// A representation of a single library in the dependency graph of a program. |
+/// |
+/// Not intended to be extended, implemented, or mixed in by clients. |
+class LibraryNode { |
+ /// A list of all the other libraries this library directly depends on. |
+ final dependencies = <LibraryNode>[]; |
Siggi Cherem (dart-lang)
2016/12/15 23:51:33
do we want to split imports and exports or in gene
Paul Berry
2016/12/16 02:46:45
My intuition is no--any client that needs to disti
|
+ |
+ /// A list of the URIs of all of this library's "part" files. |
+ final parts = <Uri>[]; |
+ |
+ /// The URI of this library's defining compilation unit. |
Siggi Cherem (dart-lang)
2016/12/15 23:51:33
you may be using an alphabetic order, but I rather
Paul Berry
2016/12/16 02:46:45
Done.
|
+ final Uri uri; |
+ |
+ LibraryNode._(this.uri); |
+} |
+ |
+class _Scanner extends Scanner { |
+ _Scanner(String contents) : super(new CharSequenceReader(contents)) { |
+ preserveComments = false; |
+ } |
+ |
+ @override |
+ void reportError(errorCode, int offset, List<Object> arguments) { |
+ // TODO(paulberry): report errors. |
+ } |
+} |
+ |
+class _StartingPoint extends _WalkerNode { |
+ final List<Uri> sources; |
+ |
+ _StartingPoint(_Walker walker, this.sources) : super(walker, null); |
+ |
+ @override |
+ Future<List<_WalkerNode>> computeDependencies() async => |
+ sources.map(walker.nodeForUri).toList(); |
+} |
+ |
+class _Walker extends AsyncDependencyWalker<_WalkerNode> { |
+ final FileSystem fileSystem; |
+ final UriResolver uriResolver; |
+ final _nodesByUri = <Uri, _WalkerNode>{}; |
+ final graph = new Graph._(); |
+ |
+ _Walker(this.fileSystem, this.uriResolver); |
+ |
+ @override |
+ Future<Null> evaluate(_WalkerNode v) { |
+ if (v is _StartingPoint) return new Future.value(); |
+ return evaluateScc([v]); |
+ } |
+ |
+ @override |
+ Future<Null> evaluateScc(List<_WalkerNode> scc) { |
+ var cycle = new LibraryCycleNode._(); |
+ for (var walkerNode in scc) { |
+ cycle.libraries[walkerNode.uri] = walkerNode.library; |
+ } |
+ graph.topologicallySortedCycles.add(cycle); |
+ return new Future.value(); |
+ } |
+ |
+ _WalkerNode nodeForUri(Uri referencedUri) { |
+ var dependencyNode = _nodesByUri.putIfAbsent( |
+ referencedUri, () => new _WalkerNode(this, referencedUri)); |
+ return dependencyNode; |
+ } |
+} |
+ |
+class _WalkerNode extends Node<_WalkerNode> { |
+ final _Walker walker; |
+ final Uri uri; |
+ final LibraryNode library; |
+ |
+ _WalkerNode(this.walker, Uri uri) |
+ : uri = uri, |
+ library = new LibraryNode._(uri); |
+ |
+ @override |
+ Future<List<_WalkerNode>> computeDependencies() async { |
+ var dependencies = <_WalkerNode>[]; |
+ // TODO(paulberry): add error recovery if the file can't be read. |
+ var contents = await walker.fileSystem |
+ .entityForPath(walker.uriResolver.resolve(uri)) |
+ .readAsString(); |
+ var scanner = new _Scanner(contents); |
+ var token = scanner.tokenize(); |
+ // TODO(paulberry): report errors. |
+ var parser = new Parser(null, AnalysisErrorListener.NULL_LISTENER); |
+ var unit = parser.parseDirectives(token); |
Siggi Cherem (dart-lang)
2016/12/15 23:51:33
I'm OK delaying this until later to avoid over-eng
Paul Berry
2016/12/16 02:46:45
Acknowledged.
|
+ for (var directive in unit.directives) { |
+ if (directive is UriBasedDirective) { |
+ // TODO(paulberry): when we support SDK libraries, we'll need more |
+ // complex logic here to find SDK parts correctly. |
+ var referencedUri = uri.resolve(directive.uri.stringValue); |
+ if (directive is PartDirective) { |
+ library.parts.add(referencedUri); |
+ } else { |
+ _WalkerNode dependencyNode = walker.nodeForUri(referencedUri); |
+ dependencies.add(dependencyNode); |
+ library.dependencies.add(dependencyNode.library); |
+ } |
+ } |
+ } |
+ return dependencies; |
+ } |
+} |