Index: pkg/compiler/lib/src/universe/class_set.dart |
diff --git a/pkg/compiler/lib/src/universe/class_set.dart b/pkg/compiler/lib/src/universe/class_set.dart |
index 11d9e090ac21eb237b2408f099a50cf162f7dfe4..d19b2c1ceff8cd1cc655b6d0d016c9761c89a1ac 100644 |
--- a/pkg/compiler/lib/src/universe/class_set.dart |
+++ b/pkg/compiler/lib/src/universe/class_set.dart |
@@ -12,7 +12,27 @@ import '../util/util.dart' show Link; |
/// |
/// This is used by the [ClassWorld] to perform queries on subclass and subtype |
/// relations. |
-// TODO(johnniwinther): Use this for `ClassWorld.subtypesOf`. |
+/// |
+/// For this class hierarchy: |
+/// |
+/// class A {} |
+/// class B extends A {} |
+/// class C extends A {} |
+/// class D extends B {} |
+/// class E extends D {} |
+/// |
+/// the [ClassHierarchyNode]s form this subclass tree: |
+/// |
+/// Object |
+/// | |
+/// A |
+/// / \ |
+/// B C |
+/// | |
+/// D |
+/// | |
+/// E |
+/// |
class ClassHierarchyNode { |
final ClassElement cls; |
@@ -47,39 +67,240 @@ class ClassHierarchyNode { |
_directSubclasses = _directSubclasses.prepend(subclass); |
} |
+ /// Returns `true` if [other] is contained in the subtree of this node. |
+ /// |
+ /// This means that [other] is a subclass of [cls]. |
+ bool contains(ClassElement other) { |
+ while (other != null) { |
+ if (cls == other) return true; |
+ if (cls.hierarchyDepth >= other.hierarchyDepth) return false; |
+ other = other.superclass; |
+ } |
+ return false; |
+ } |
+ |
/// `true` if [cls] has been directly or indirectly instantiated. |
bool get isInstantiated => isDirectlyInstantiated || isIndirectlyInstantiated; |
/// Returns an [Iterable] of the subclasses of [cls] possibly including [cls]. |
- /// If [directlyInstantiated] is `true`, the iterable only returns the |
- /// directly instantiated subclasses of [cls]. |
- Iterable<ClassElement> subclasses({bool directlyInstantiated: true}) { |
+ /// |
+ /// The directly instantiated, indirectly instantiated and uninstantiated |
+ /// subclasses of [cls] are returned if [includeDirectlyInstantiated], |
+ /// [includeIndirectlyInstantiated], and [includeUninstantiated] are `true`, |
+ /// respectively. If [strict] is `true`, [cls] itself is _not_ returned. |
+ Iterable<ClassElement> subclasses( |
+ {bool includeDirectlyInstantiated: true, |
+ bool includeIndirectlyInstantiated: true, |
+ bool includeUninstantiated: true, |
+ bool strict: false}) { |
return new ClassHierarchyNodeIterable( |
- this, directlyInstantiatedOnly: directlyInstantiated); |
+ this, |
+ includeRoot: !strict, |
+ includeDirectlyInstantiated: includeDirectlyInstantiated, |
+ includeIndirectlyInstantiated: includeIndirectlyInstantiated, |
+ includeUninstantiated: includeUninstantiated); |
} |
- /// Returns an [Iterable] of the strict subclasses of [cls] _not_ including |
- /// [cls] itself. If [directlyInstantiated] is `true`, the iterable only |
- /// returns the directly instantiated subclasses of [cls]. |
- Iterable<ClassElement> strictSubclasses( |
- {bool directlyInstantiated: true}) { |
- return new ClassHierarchyNodeIterable(this, |
- includeRoot: false, directlyInstantiatedOnly: directlyInstantiated); |
+ void dump(StringBuffer sb, String indentation) { |
+ sb.write('$indentation$cls:['); |
+ if (_directSubclasses.isEmpty) { |
+ sb.write(']'); |
+ } else { |
+ sb.write('\n'); |
+ bool needsComma = false; |
+ for (Link<ClassHierarchyNode> link = _directSubclasses; |
+ !link.isEmpty; |
+ link = link.tail) { |
+ if (needsComma) { |
+ sb.write(',\n'); |
+ } |
+ link.head.dump(sb, '$indentation '); |
+ needsComma = true; |
+ } |
+ sb.write('\n'); |
+ sb.write('$indentation]'); |
+ } |
} |
String toString() => cls.toString(); |
} |
+/// Object holding the subclass and subtype relation for a single |
+/// [ClassElement]. |
+/// |
+/// The subclass relation for a class `C` is modelled through a reference to |
+/// the [ClassHierarchyNode] for `C` in the global [ClassHierarchyNode] tree |
+/// computed in [World]. |
+/// |
+/// The subtype relation for a class `C` is modelled through a collection of |
+/// disjoint [ClassHierarchyNode] subtrees. The subclasses of `C`, modelled |
+/// through the aforementioned [ClassHierarchyNode] pointer, are extended with |
+/// the subtypes that do not extend `C` through a list of additional |
+/// [ClassHierarchyNode] nodes. This list is normalized to contain only the |
+/// nodes for the topmost subtypes and is furthermore ordered in increasing |
+/// hierarchy depth order. |
+/// |
+/// For this class hierarchy: |
+/// |
+/// class A {} |
+/// class B extends A {} |
+/// class C implements B {} |
+/// class D implements A {} |
+/// class E extends D {} |
+/// class F implements D {} |
+/// |
+/// the [ClassHierarchyNode] tree is |
+/// |
+/// Object |
+/// / | | \ |
+/// A C D F |
+/// | | |
+/// B E |
+/// |
+/// and the [ClassSet] for `A` holds these [ClassHierarchyNode] nodes: |
+/// |
+/// A -> [C, D, F] |
+/// |
+/// The subtypes `B` and `E` are not directly modeled because they are implied |
+/// by their subclass relation to `A` and `D`, repectively. This can be seen |
+/// if we expand the subclass subtrees: |
+/// |
+/// A -> [C, D, F] |
+/// | | |
+/// B E |
+/// |
+class ClassSet { |
+ final ClassHierarchyNode node; |
+ |
+ List<ClassHierarchyNode> _directSubtypes; |
+ |
+ ClassSet(this.node); |
+ |
+ ClassElement get cls => node.cls; |
+ |
+ /// Returns an [Iterable] of the subclasses of [cls] possibly including [cls]. |
+ /// |
+ /// The directly instantiated, indirectly instantiated and uninstantiated |
+ /// subclasses of [cls] are returned if [includeDirectlyInstantiated], |
+ /// [includeIndirectlyInstantiated], and [includeUninstantiated] are `true`, |
+ /// respectively. If [strict] is `true`, [cls] itself is _not_ returned. |
+ Iterable<ClassElement> subclasses( |
+ {bool includeDirectlyInstantiated: true, |
+ bool includeIndirectlyInstantiated: true, |
+ bool includeUninstantiated: true, |
+ bool strict: false}) { |
+ return node.subclasses( |
+ strict: strict, |
+ includeDirectlyInstantiated: includeDirectlyInstantiated, |
+ includeIndirectlyInstantiated: includeIndirectlyInstantiated, |
+ includeUninstantiated: includeUninstantiated); |
+ } |
+ |
+ /// Returns an [Iterable] of the subtypes of [cls] possibly including [cls]. |
+ /// |
+ /// The directly instantiated, indirectly instantiated and uninstantiated |
+ /// subtypes of [cls] are returned if [includeDirectlyInstantiated], |
+ /// [includeIndirectlyInstantiated], and [includeUninstantiated] are `true`, |
+ /// respectively. If [strict] is `true`, [cls] itself is _not_ returned. |
+ Iterable<ClassElement> subtypes( |
+ {bool includeDirectlyInstantiated: true, |
+ bool includeIndirectlyInstantiated: true, |
+ bool includeUninstantiated: true, |
+ bool strict: false}) { |
+ if (_directSubtypes == null) { |
+ return node.subclasses( |
+ strict: strict, |
+ includeDirectlyInstantiated: includeDirectlyInstantiated, |
+ includeIndirectlyInstantiated: includeIndirectlyInstantiated, |
+ includeUninstantiated: includeUninstantiated); |
+ } |
+ return new SubtypesIterable.SubtypesIterator(this, |
+ includeRoot: !strict, |
+ includeDirectlyInstantiated: includeDirectlyInstantiated, |
+ includeIndirectlyInstantiated: includeIndirectlyInstantiated, |
+ includeUninstantiated: includeUninstantiated); |
+ } |
+ |
+ /// Adds [subtype] as a subtype of [cls]. |
+ void addSubtype(ClassHierarchyNode subtype) { |
+ if (node.contains(subtype.cls)) { |
+ return; |
+ } |
+ if (_directSubtypes == null) { |
+ _directSubtypes = <ClassHierarchyNode>[subtype]; |
+ } else { |
+ int hierarchyDepth = subtype.cls.hierarchyDepth; |
+ List<ClassHierarchyNode> newSubtypes = <ClassHierarchyNode>[]; |
+ bool added = false; |
+ for (ClassHierarchyNode otherSubtype in _directSubtypes) { |
+ int otherHierarchyDepth = otherSubtype.cls.hierarchyDepth; |
+ if (hierarchyDepth == otherHierarchyDepth) { |
+ if (subtype == otherSubtype) { |
+ return; |
+ } else { |
+ // [otherSubtype] is unrelated to [subtype]. |
+ newSubtypes.add(otherSubtype); |
+ } |
+ } else if (hierarchyDepth > otherSubtype.cls.hierarchyDepth) { |
+ // [otherSubtype] could be a superclass of [subtype]. |
+ if (otherSubtype.contains(subtype.cls)) { |
+ // [subtype] is already in this set. |
+ return; |
+ } else { |
+ // [otherSubtype] is unrelated to [subtype]. |
+ newSubtypes.add(otherSubtype); |
+ } |
+ } else { |
+ if (!added) { |
+ // Insert [subtype] before other subtypes of higher hierarchy depth. |
+ newSubtypes.add(subtype); |
+ added = true; |
+ } |
+ // [subtype] could be a superclass of [otherSubtype]. |
+ if (subtype.contains(otherSubtype.cls)) { |
+ // Replace [otherSubtype]. |
+ } else { |
+ newSubtypes.add(otherSubtype); |
+ } |
+ } |
+ } |
+ if (!added) { |
+ newSubtypes.add(subtype); |
+ } |
+ _directSubtypes = newSubtypes; |
+ } |
+ } |
+ |
+ String toString() { |
+ StringBuffer sb = new StringBuffer(); |
+ sb.write('[\n'); |
+ node.dump(sb, ' '); |
+ sb.write('\n'); |
+ if (_directSubtypes != null) { |
+ for (ClassHierarchyNode node in _directSubtypes) { |
+ node.dump(sb, ' '); |
+ sb.write('\n'); |
+ } |
+ } |
+ sb.write(']'); |
+ return sb.toString(); |
+ } |
+} |
+ |
/// Iterable for subclasses of a [ClassHierarchyNode]. |
class ClassHierarchyNodeIterable extends IterableBase<ClassElement> { |
final ClassHierarchyNode root; |
final bool includeRoot; |
- final bool directlyInstantiatedOnly; |
+ final bool includeDirectlyInstantiated; |
+ final bool includeIndirectlyInstantiated; |
+ final bool includeUninstantiated; |
ClassHierarchyNodeIterable( |
this.root, |
{this.includeRoot: true, |
- this.directlyInstantiatedOnly: false}) { |
+ this.includeDirectlyInstantiated: true, |
+ this.includeIndirectlyInstantiated: true, |
+ this.includeUninstantiated: true}) { |
if (root == null) throw new StateError("No root for iterable."); |
} |
@@ -112,7 +333,13 @@ class ClassHierarchyNodeIterator implements Iterator<ClassElement> { |
bool get includeRoot => iterable.includeRoot; |
- bool get directlyInstantiatedOnly => iterable.directlyInstantiatedOnly; |
+ bool get includeDirectlyInstantiated => iterable.includeDirectlyInstantiated; |
+ |
+ bool get includeIndirectlyInstantiated { |
+ return iterable.includeIndirectlyInstantiated; |
+ } |
+ |
+ bool get includeUninstantiated => iterable.includeUninstantiated; |
@override |
ClassElement get current { |
@@ -143,6 +370,11 @@ class ClassHierarchyNodeIterator implements Iterator<ClassElement> { |
} |
currentNode = stack.head; |
stack = stack.tail; |
+ if (!includeUninstantiated && !currentNode.isInstantiated) { |
+ // We're only iterating instantiated classes so there is no use in |
+ // visiting the current node and its subtree. |
+ continue; |
+ } |
for (Link<ClassHierarchyNode> link = currentNode._directSubclasses; |
!link.isEmpty; |
link = link.tail) { |
@@ -157,9 +389,90 @@ class ClassHierarchyNodeIterator implements Iterator<ClassElement> { |
/// Returns `true` if the class of [node] is a valid result for this iterator. |
bool _isValid(ClassHierarchyNode node) { |
if (!includeRoot && node == root) return false; |
- if (directlyInstantiatedOnly && !node.isDirectlyInstantiated) return false; |
- return true; |
+ if (includeDirectlyInstantiated && node.isDirectlyInstantiated) { |
+ return true; |
+ } |
+ if (includeIndirectlyInstantiated && node.isIndirectlyInstantiated) { |
+ return true; |
+ } |
+ if (includeUninstantiated && !node.isInstantiated) { |
+ return true; |
+ } |
+ return false; |
} |
} |
+/// Iterable for the subtypes in a [ClassSet]. |
+class SubtypesIterable extends IterableBase<ClassElement> { |
+ final ClassSet subtypeSet; |
+ final bool includeRoot; |
+ final bool includeDirectlyInstantiated; |
+ final bool includeIndirectlyInstantiated; |
+ final bool includeUninstantiated; |
+ |
+ SubtypesIterable.SubtypesIterator( |
+ this.subtypeSet, |
+ {this.includeRoot: true, |
+ this.includeDirectlyInstantiated: true, |
+ this.includeIndirectlyInstantiated: true, |
+ this.includeUninstantiated: true}); |
+ |
+ @override |
+ Iterator<ClassElement> get iterator => new SubtypesIterator(this); |
+} |
+ |
+/// Iterator for the subtypes in a [ClassSet]. |
+class SubtypesIterator extends Iterator<ClassElement> { |
+ final SubtypesIterable iterable; |
+ Iterator<ClassElement> elements; |
+ Iterator<ClassHierarchyNode> hierarchyNodes; |
+ |
+ SubtypesIterator(this.iterable); |
+ bool get includeRoot => iterable.includeRoot; |
+ |
+ bool get includeDirectlyInstantiated => iterable.includeDirectlyInstantiated; |
+ |
+ bool get includeIndirectlyInstantiated { |
+ return iterable.includeIndirectlyInstantiated; |
+ } |
+ |
+ bool get includeUninstantiated => iterable.includeUninstantiated; |
+ |
+ @override |
+ ClassElement get current { |
+ if (elements != null) { |
+ return elements.current; |
+ } |
+ return null; |
+ } |
+ |
+ @override |
+ bool moveNext() { |
+ if (elements == null && hierarchyNodes == null) { |
+ // Initial state. Iterate through subclasses. |
+ elements = iterable.subtypeSet.node.subclasses( |
+ strict: !includeRoot, |
+ includeDirectlyInstantiated: includeDirectlyInstantiated, |
+ includeIndirectlyInstantiated: includeIndirectlyInstantiated, |
+ includeUninstantiated: includeUninstantiated).iterator; |
+ } |
+ if (elements != null && elements.moveNext()) { |
+ return true; |
+ } |
+ if (hierarchyNodes == null) { |
+ // Start iterating through subtypes. |
+ hierarchyNodes = iterable.subtypeSet._directSubtypes.iterator; |
+ } |
+ while (hierarchyNodes.moveNext()) { |
+ elements = hierarchyNodes.current.subclasses( |
+ includeDirectlyInstantiated: includeDirectlyInstantiated, |
+ includeIndirectlyInstantiated: includeIndirectlyInstantiated, |
+ includeUninstantiated: includeUninstantiated).iterator; |
+ if (elements.moveNext()) { |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+} |