Chromium Code Reviews| 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..43deeafd6866a074299ed1d74f90d9d64fd7e6ff 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 extends `C` through a list of additional |
|
karlklose
2015/09/08 12:09:21
'extends' -> 'extend'.
Johnni Winther
2015/09/09 17:19:20
Done.
|
| +/// [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 model because they are implied |
|
karlklose
2015/09/08 12:09:21
'model' -> 'modeled'?
Johnni Winther
2015/09/09 17:19:20
Done.
|
| +/// 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> iterables; |
|
karlklose
2015/09/08 12:09:21
Maybe change this to describe what the iterator ho
Johnni Winther
2015/09/09 17:19:20
Done.
|
| + |
| + 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 && iterables == 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 (iterables == null) { |
| + // Start iterating through subtypes. |
| + iterables = iterable.subtypeSet._directSubtypes.iterator; |
| + } |
| + while (iterables.moveNext()) { |
| + elements = iterables.current.subclasses( |
| + includeDirectlyInstantiated: includeDirectlyInstantiated, |
| + includeIndirectlyInstantiated: includeIndirectlyInstantiated, |
| + includeUninstantiated: includeUninstantiated).iterator; |
| + if (elements.moveNext()) { |
| + return true; |
| + } |
| + } |
| + return false; |
| + } |
| +} |