|
3 | 3 | // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
|
5 | 5 | import 'dart:async'; |
| 6 | +import 'dart:collection'; |
6 | 7 |
|
7 | 8 | import 'package:analyzer/dart/ast/ast.dart'; |
8 | 9 | import 'package:analyzer/dart/element/element.dart'; |
@@ -79,7 +80,7 @@ class PackageGraph { |
79 | 80 | for (var package in documentedPackages) { |
80 | 81 | package.libraries.sort((a, b) => compareNatural(a.name, b.name)); |
81 | 82 | for (var library in package.libraries) { |
82 | | - library.allClasses.forEach(_addToImplementors); |
| 83 | + _addToImplementors(library.allClasses); |
83 | 84 | _extensions.addAll(library.extensions); |
84 | 85 | } |
85 | 86 | } |
@@ -202,7 +203,9 @@ class PackageGraph { |
202 | 203 | allInheritableElements = {}; |
203 | 204 |
|
204 | 205 | /// A mapping of the list of classes which implement each class. |
205 | | - final Map<Class, List<Class>> _implementors = {}; |
| 206 | + final Map<Class, List<Class>> _implementors = LinkedHashMap( |
| 207 | + equals: (Class a, Class b) => a.definingClass == b.definingClass, |
| 208 | + hashCode: (Class class_) => class_.definingClass.hashCode); |
206 | 209 |
|
207 | 210 | /// A list of extensions that exist in the package graph. |
208 | 211 | final List<Extension> _extensions = []; |
@@ -576,26 +579,46 @@ class PackageGraph { |
576 | 579 | return hrefMap; |
577 | 580 | } |
578 | 581 |
|
579 | | - void _addToImplementors(Class class_) { |
| 582 | + void _addToImplementors(Iterable<Class> classes) { |
580 | 583 | assert(!allImplementorsAdded); |
581 | | - _implementors.putIfAbsent(class_, () => []); |
582 | | - void checkAndAddClass(Class key) { |
583 | | - _implementors.putIfAbsent(key, () => []); |
584 | | - var list = _implementors[key]; |
585 | 584 |
|
586 | | - if (!list.any((l) => l.element == class_.element)) { |
587 | | - list.add(class_); |
| 585 | + // Private classes may not be included in [classes], but may still be |
| 586 | + // necessary links in the implementation chain. They are added here as they |
| 587 | + // are found, then processed after [classes]. |
| 588 | + var privates = <Class>[]; |
| 589 | + |
| 590 | + void checkAndAddClass(Class implemented, Class implementor) { |
| 591 | + if (!implemented.isPublic) { |
| 592 | + privates.add(implemented); |
| 593 | + } |
| 594 | + implemented = implemented.canonicalModelElement ?? implemented; |
| 595 | + _implementors.putIfAbsent(implemented, () => []); |
| 596 | + var list = _implementors[implemented]; |
| 597 | + // TODO(srawlins): This would be more efficient if we created a |
| 598 | + // SplayTreeSet keyed off of `.element`. |
| 599 | + if (!list.any((l) => l.element == implementor.element)) { |
| 600 | + list.add(implementor); |
588 | 601 | } |
589 | 602 | } |
590 | 603 |
|
591 | | - for (var type in class_.mixins) { |
592 | | - checkAndAddClass(type.element); |
593 | | - } |
594 | | - if (class_.supertype != null) { |
595 | | - checkAndAddClass(class_.supertype.element); |
| 604 | + void addImplementor(Class class_) { |
| 605 | + for (var type in class_.mixins) { |
| 606 | + checkAndAddClass(type.element, class_); |
| 607 | + } |
| 608 | + if (class_.supertype != null) { |
| 609 | + checkAndAddClass(class_.supertype.element, class_); |
| 610 | + } |
| 611 | + for (var type in class_.interfaces) { |
| 612 | + checkAndAddClass(type.element, class_); |
| 613 | + } |
596 | 614 | } |
597 | | - for (var type in class_.interfaces) { |
598 | | - checkAndAddClass(type.element); |
| 615 | + |
| 616 | + classes.forEach(addImplementor); |
| 617 | + |
| 618 | + // [privates] may grow while processing; use a for loop, rather than a |
| 619 | + // for-each loop, to avoid concurrent modification errors. |
| 620 | + for (var i = 0; i < privates.length; i++) { |
| 621 | + addImplementor(privates[i]); |
599 | 622 | } |
600 | 623 | } |
601 | 624 |
|
|
0 commit comments