Index: pkg/analyzer/lib/src/generated/element.dart |
diff --git a/pkg/analyzer/lib/src/generated/element.dart b/pkg/analyzer/lib/src/generated/element.dart |
index 30aa17ebcaff5fdaf4c9843c6b2181ac3e5b6dd5..9e7aeb4825871cb6b0c974897742ebf56df78838 100644 |
--- a/pkg/analyzer/lib/src/generated/element.dart |
+++ b/pkg/analyzer/lib/src/generated/element.dart |
@@ -5,6 +5,7 @@ |
library engine.element; |
import 'dart:collection'; |
+import 'dart:math' show min; |
import 'package:analyzer/src/generated/utilities_general.dart'; |
import 'package:analyzer/src/task/dart.dart'; |
@@ -7340,6 +7341,15 @@ class LibraryElementImpl extends ElementImpl implements LibraryElement { |
List<ExportElement> _exports = ExportElement.EMPTY_LIST; |
/** |
+ * A list containing the strongly connected component in the import/export |
+ * graph in which the current library resides. Computed on demand, null |
+ * if not present. If _libraryCycle is set, then the _libraryCycle field |
+ * for all libraries reachable from this library in the import/export graph |
+ * is also set. |
+ */ |
+ List<LibraryElement> _libraryCycle = null; |
+ |
+ /** |
* A list containing all of the compilation units that are included in this |
* library using a `part` directive. |
*/ |
@@ -7527,6 +7537,69 @@ class LibraryElementImpl extends ElementImpl implements LibraryElement { |
@override |
LibraryElement get library => this; |
+ List<LibraryElement> get libraryCycle { |
+ if (_libraryCycle != null) { |
+ return _libraryCycle; |
+ } |
+ |
+ // Global counter for this run of the algorithm |
+ int counter = 0; |
+ // The discovery times of each library |
+ Map<LibraryElementImpl, int> indices = {}; |
+ // The set of scc candidates |
+ Set<LibraryElementImpl> active = new Set(); |
+ // The stack of discovered elements |
+ List<LibraryElementImpl> stack = []; |
+ // For a given library that has not yet been processed by this run of the |
+ // algorithm, compute the strongly connected components. |
+ int scc(LibraryElementImpl library) { |
+ int index = counter++; |
+ int root = index; |
+ indices[library] = index; |
+ active.add(library); |
+ stack.add(library); |
+ void recurse(LibraryElementImpl child) { |
+ if (!indices.containsKey(child)) { |
+ // We haven't visited this child yet, so recurse on the child, |
+ // returning the lowest numbered node reachable from the child. If |
+ // the child can reach a root which is lower numbered than anything |
+ // we've reached so far, update the root. |
+ root = min(root, scc(child)); |
+ } else if (active.contains(child)) { |
+ // The child has been visited, but has not yet been placed into a |
+ // component. If the child is higher than anything we've seen so far |
+ // update the root appropriately. |
+ root = min(root, indices[child]); |
+ } |
+ } |
+ // Recurse on all of the children in the import/export graph, filtering |
+ // out those for which library cycles have already been computed. |
+ library.exportedLibraries |
+ .where((l) => l._libraryCycle == null) |
+ .forEach(recurse); |
+ library.importedLibraries |
+ .where((l) => l._libraryCycle == null) |
+ .forEach(recurse); |
+ |
+ if (root == index) { |
+ // This is the root of a strongly connected component. |
+ // Pop the elements, and share the component across all |
+ // of the elements. |
+ List<LibraryElement> component = <LibraryElement>[]; |
+ LibraryElementImpl cur = null; |
+ do { |
+ cur = stack.removeLast(); |
+ active.remove(cur); |
+ component.add(cur); |
+ cur._libraryCycle = component; |
+ } while (cur != library); |
+ } |
+ return root; |
+ } |
+ scc(library); |
+ return _libraryCycle; |
+ } |
+ |
@override |
FunctionElement get loadLibraryFunction { |
if (_loadLibraryFunction == null) { |