OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library dart2js.world.class_set; | 5 library dart2js.world.class_set; |
6 | 6 |
7 import 'dart:collection' show IterableBase; | 7 import 'dart:collection' show IterableBase; |
8 import '../elements/elements.dart' show ClassElement; | 8 import '../elements/elements.dart' show ClassElement; |
9 import '../util/util.dart' show Link; | 9 import '../util/util.dart' show Link; |
10 | 10 |
11 /// Node for [cls] in a tree forming the subclass relation of [ClassElement]s. | 11 /// Node for [cls] in a tree forming the subclass relation of [ClassElement]s. |
12 /// | 12 /// |
13 /// This is used by the [ClassWorld] to perform queries on subclass and subtype | 13 /// This is used by the [ClassWorld] to perform queries on subclass and subtype |
14 /// relations. | 14 /// relations. |
15 // TODO(johnniwinther): Use this for `ClassWorld.subtypesOf`. | 15 /// |
| 16 /// For this class hierarchy: |
| 17 /// |
| 18 /// class A {} |
| 19 /// class B extends A {} |
| 20 /// class C extends A {} |
| 21 /// class D extends B {} |
| 22 /// class E extends D {} |
| 23 /// |
| 24 /// the [ClassHierarchyNode]s form this subclass tree: |
| 25 /// |
| 26 /// Object |
| 27 /// | |
| 28 /// A |
| 29 /// / \ |
| 30 /// B C |
| 31 /// | |
| 32 /// D |
| 33 /// | |
| 34 /// E |
| 35 /// |
16 class ClassHierarchyNode { | 36 class ClassHierarchyNode { |
17 final ClassElement cls; | 37 final ClassElement cls; |
18 | 38 |
19 /// `true` if [cls] has been directly instantiated. | 39 /// `true` if [cls] has been directly instantiated. |
20 /// | 40 /// |
21 /// For instance `C` but _not_ `B` in: | 41 /// For instance `C` but _not_ `B` in: |
22 /// class B {} | 42 /// class B {} |
23 /// class C extends B {} | 43 /// class C extends B {} |
24 /// main() => new C(); | 44 /// main() => new C(); |
25 /// | 45 /// |
(...skipping 14 matching lines...) Expand all Loading... |
40 | 60 |
41 ClassHierarchyNode(this.cls); | 61 ClassHierarchyNode(this.cls); |
42 | 62 |
43 /// Adds [subclass] as a direct subclass of [cls]. | 63 /// Adds [subclass] as a direct subclass of [cls]. |
44 void addDirectSubclass(ClassHierarchyNode subclass) { | 64 void addDirectSubclass(ClassHierarchyNode subclass) { |
45 assert(subclass.cls.superclass == cls); | 65 assert(subclass.cls.superclass == cls); |
46 assert(!_directSubclasses.contains(subclass)); | 66 assert(!_directSubclasses.contains(subclass)); |
47 _directSubclasses = _directSubclasses.prepend(subclass); | 67 _directSubclasses = _directSubclasses.prepend(subclass); |
48 } | 68 } |
49 | 69 |
| 70 /// Returns `true` if [other] is contained in the subtree of this node. |
| 71 /// |
| 72 /// This means that [other] is a subclass of [cls]. |
| 73 bool contains(ClassElement other) { |
| 74 while (other != null) { |
| 75 if (cls == other) return true; |
| 76 if (cls.hierarchyDepth >= other.hierarchyDepth) return false; |
| 77 other = other.superclass; |
| 78 } |
| 79 return false; |
| 80 } |
| 81 |
50 /// `true` if [cls] has been directly or indirectly instantiated. | 82 /// `true` if [cls] has been directly or indirectly instantiated. |
51 bool get isInstantiated => isDirectlyInstantiated || isIndirectlyInstantiated; | 83 bool get isInstantiated => isDirectlyInstantiated || isIndirectlyInstantiated; |
52 | 84 |
53 /// Returns an [Iterable] of the subclasses of [cls] possibly including [cls]. | 85 /// Returns an [Iterable] of the subclasses of [cls] possibly including [cls]. |
54 /// If [directlyInstantiated] is `true`, the iterable only returns the | 86 /// |
55 /// directly instantiated subclasses of [cls]. | 87 /// The directly instantiated, indirectly instantiated and uninstantiated |
56 Iterable<ClassElement> subclasses({bool directlyInstantiated: true}) { | 88 /// subclasses of [cls] are returned if [includeDirectlyInstantiated], |
| 89 /// [includeIndirectlyInstantiated], and [includeUninstantiated] are `true`, |
| 90 /// respectively. If [strict] is `true`, [cls] itself is _not_ returned. |
| 91 Iterable<ClassElement> subclasses( |
| 92 {bool includeDirectlyInstantiated: true, |
| 93 bool includeIndirectlyInstantiated: true, |
| 94 bool includeUninstantiated: true, |
| 95 bool strict: false}) { |
57 return new ClassHierarchyNodeIterable( | 96 return new ClassHierarchyNodeIterable( |
58 this, directlyInstantiatedOnly: directlyInstantiated); | 97 this, |
59 } | 98 includeRoot: !strict, |
60 | 99 includeDirectlyInstantiated: includeDirectlyInstantiated, |
61 /// Returns an [Iterable] of the strict subclasses of [cls] _not_ including | 100 includeIndirectlyInstantiated: includeIndirectlyInstantiated, |
62 /// [cls] itself. If [directlyInstantiated] is `true`, the iterable only | 101 includeUninstantiated: includeUninstantiated); |
63 /// returns the directly instantiated subclasses of [cls]. | 102 } |
64 Iterable<ClassElement> strictSubclasses( | 103 |
65 {bool directlyInstantiated: true}) { | 104 void dump(StringBuffer sb, String indentation) { |
66 return new ClassHierarchyNodeIterable(this, | 105 sb.write('$indentation$cls:['); |
67 includeRoot: false, directlyInstantiatedOnly: directlyInstantiated); | 106 if (_directSubclasses.isEmpty) { |
| 107 sb.write(']'); |
| 108 } else { |
| 109 sb.write('\n'); |
| 110 bool needsComma = false; |
| 111 for (Link<ClassHierarchyNode> link = _directSubclasses; |
| 112 !link.isEmpty; |
| 113 link = link.tail) { |
| 114 if (needsComma) { |
| 115 sb.write(',\n'); |
| 116 } |
| 117 link.head.dump(sb, '$indentation '); |
| 118 needsComma = true; |
| 119 } |
| 120 sb.write('\n'); |
| 121 sb.write('$indentation]'); |
| 122 } |
68 } | 123 } |
69 | 124 |
70 String toString() => cls.toString(); | 125 String toString() => cls.toString(); |
71 } | 126 } |
72 | 127 |
| 128 /// Object holding the subclass and subtype relation for a single |
| 129 /// [ClassElement]. |
| 130 /// |
| 131 /// The subclass relation for a class `C` is modelled through a reference to |
| 132 /// the [ClassHierarchyNode] for `C` in the global [ClassHierarchyNode] tree |
| 133 /// computed in [World]. |
| 134 /// |
| 135 /// The subtype relation for a class `C` is modelled through a collection of |
| 136 /// disjoint [ClassHierarchyNode] subtrees. The subclasses of `C`, modelled |
| 137 /// through the aforementioned [ClassHierarchyNode] pointer, are extended with |
| 138 /// the subtypes that do not extend `C` through a list of additional |
| 139 /// [ClassHierarchyNode] nodes. This list is normalized to contain only the |
| 140 /// nodes for the topmost subtypes and is furthermore ordered in increasing |
| 141 /// hierarchy depth order. |
| 142 /// |
| 143 /// For this class hierarchy: |
| 144 /// |
| 145 /// class A {} |
| 146 /// class B extends A {} |
| 147 /// class C implements B {} |
| 148 /// class D implements A {} |
| 149 /// class E extends D {} |
| 150 /// class F implements D {} |
| 151 /// |
| 152 /// the [ClassHierarchyNode] tree is |
| 153 /// |
| 154 /// Object |
| 155 /// / | | \ |
| 156 /// A C D F |
| 157 /// | | |
| 158 /// B E |
| 159 /// |
| 160 /// and the [ClassSet] for `A` holds these [ClassHierarchyNode] nodes: |
| 161 /// |
| 162 /// A -> [C, D, F] |
| 163 /// |
| 164 /// The subtypes `B` and `E` are not directly modeled because they are implied |
| 165 /// by their subclass relation to `A` and `D`, repectively. This can be seen |
| 166 /// if we expand the subclass subtrees: |
| 167 /// |
| 168 /// A -> [C, D, F] |
| 169 /// | | |
| 170 /// B E |
| 171 /// |
| 172 class ClassSet { |
| 173 final ClassHierarchyNode node; |
| 174 |
| 175 List<ClassHierarchyNode> _directSubtypes; |
| 176 |
| 177 ClassSet(this.node); |
| 178 |
| 179 ClassElement get cls => node.cls; |
| 180 |
| 181 /// Returns an [Iterable] of the subclasses of [cls] possibly including [cls]. |
| 182 /// |
| 183 /// The directly instantiated, indirectly instantiated and uninstantiated |
| 184 /// subclasses of [cls] are returned if [includeDirectlyInstantiated], |
| 185 /// [includeIndirectlyInstantiated], and [includeUninstantiated] are `true`, |
| 186 /// respectively. If [strict] is `true`, [cls] itself is _not_ returned. |
| 187 Iterable<ClassElement> subclasses( |
| 188 {bool includeDirectlyInstantiated: true, |
| 189 bool includeIndirectlyInstantiated: true, |
| 190 bool includeUninstantiated: true, |
| 191 bool strict: false}) { |
| 192 return node.subclasses( |
| 193 strict: strict, |
| 194 includeDirectlyInstantiated: includeDirectlyInstantiated, |
| 195 includeIndirectlyInstantiated: includeIndirectlyInstantiated, |
| 196 includeUninstantiated: includeUninstantiated); |
| 197 } |
| 198 |
| 199 /// Returns an [Iterable] of the subtypes of [cls] possibly including [cls]. |
| 200 /// |
| 201 /// The directly instantiated, indirectly instantiated and uninstantiated |
| 202 /// subtypes of [cls] are returned if [includeDirectlyInstantiated], |
| 203 /// [includeIndirectlyInstantiated], and [includeUninstantiated] are `true`, |
| 204 /// respectively. If [strict] is `true`, [cls] itself is _not_ returned. |
| 205 Iterable<ClassElement> subtypes( |
| 206 {bool includeDirectlyInstantiated: true, |
| 207 bool includeIndirectlyInstantiated: true, |
| 208 bool includeUninstantiated: true, |
| 209 bool strict: false}) { |
| 210 if (_directSubtypes == null) { |
| 211 return node.subclasses( |
| 212 strict: strict, |
| 213 includeDirectlyInstantiated: includeDirectlyInstantiated, |
| 214 includeIndirectlyInstantiated: includeIndirectlyInstantiated, |
| 215 includeUninstantiated: includeUninstantiated); |
| 216 } |
| 217 return new SubtypesIterable.SubtypesIterator(this, |
| 218 includeRoot: !strict, |
| 219 includeDirectlyInstantiated: includeDirectlyInstantiated, |
| 220 includeIndirectlyInstantiated: includeIndirectlyInstantiated, |
| 221 includeUninstantiated: includeUninstantiated); |
| 222 } |
| 223 |
| 224 /// Adds [subtype] as a subtype of [cls]. |
| 225 void addSubtype(ClassHierarchyNode subtype) { |
| 226 if (node.contains(subtype.cls)) { |
| 227 return; |
| 228 } |
| 229 if (_directSubtypes == null) { |
| 230 _directSubtypes = <ClassHierarchyNode>[subtype]; |
| 231 } else { |
| 232 int hierarchyDepth = subtype.cls.hierarchyDepth; |
| 233 List<ClassHierarchyNode> newSubtypes = <ClassHierarchyNode>[]; |
| 234 bool added = false; |
| 235 for (ClassHierarchyNode otherSubtype in _directSubtypes) { |
| 236 int otherHierarchyDepth = otherSubtype.cls.hierarchyDepth; |
| 237 if (hierarchyDepth == otherHierarchyDepth) { |
| 238 if (subtype == otherSubtype) { |
| 239 return; |
| 240 } else { |
| 241 // [otherSubtype] is unrelated to [subtype]. |
| 242 newSubtypes.add(otherSubtype); |
| 243 } |
| 244 } else if (hierarchyDepth > otherSubtype.cls.hierarchyDepth) { |
| 245 // [otherSubtype] could be a superclass of [subtype]. |
| 246 if (otherSubtype.contains(subtype.cls)) { |
| 247 // [subtype] is already in this set. |
| 248 return; |
| 249 } else { |
| 250 // [otherSubtype] is unrelated to [subtype]. |
| 251 newSubtypes.add(otherSubtype); |
| 252 } |
| 253 } else { |
| 254 if (!added) { |
| 255 // Insert [subtype] before other subtypes of higher hierarchy depth. |
| 256 newSubtypes.add(subtype); |
| 257 added = true; |
| 258 } |
| 259 // [subtype] could be a superclass of [otherSubtype]. |
| 260 if (subtype.contains(otherSubtype.cls)) { |
| 261 // Replace [otherSubtype]. |
| 262 } else { |
| 263 newSubtypes.add(otherSubtype); |
| 264 } |
| 265 } |
| 266 } |
| 267 if (!added) { |
| 268 newSubtypes.add(subtype); |
| 269 } |
| 270 _directSubtypes = newSubtypes; |
| 271 } |
| 272 } |
| 273 |
| 274 String toString() { |
| 275 StringBuffer sb = new StringBuffer(); |
| 276 sb.write('[\n'); |
| 277 node.dump(sb, ' '); |
| 278 sb.write('\n'); |
| 279 if (_directSubtypes != null) { |
| 280 for (ClassHierarchyNode node in _directSubtypes) { |
| 281 node.dump(sb, ' '); |
| 282 sb.write('\n'); |
| 283 } |
| 284 } |
| 285 sb.write(']'); |
| 286 return sb.toString(); |
| 287 } |
| 288 } |
| 289 |
73 /// Iterable for subclasses of a [ClassHierarchyNode]. | 290 /// Iterable for subclasses of a [ClassHierarchyNode]. |
74 class ClassHierarchyNodeIterable extends IterableBase<ClassElement> { | 291 class ClassHierarchyNodeIterable extends IterableBase<ClassElement> { |
75 final ClassHierarchyNode root; | 292 final ClassHierarchyNode root; |
76 final bool includeRoot; | 293 final bool includeRoot; |
77 final bool directlyInstantiatedOnly; | 294 final bool includeDirectlyInstantiated; |
| 295 final bool includeIndirectlyInstantiated; |
| 296 final bool includeUninstantiated; |
78 | 297 |
79 ClassHierarchyNodeIterable( | 298 ClassHierarchyNodeIterable( |
80 this.root, | 299 this.root, |
81 {this.includeRoot: true, | 300 {this.includeRoot: true, |
82 this.directlyInstantiatedOnly: false}) { | 301 this.includeDirectlyInstantiated: true, |
| 302 this.includeIndirectlyInstantiated: true, |
| 303 this.includeUninstantiated: true}) { |
83 if (root == null) throw new StateError("No root for iterable."); | 304 if (root == null) throw new StateError("No root for iterable."); |
84 } | 305 } |
85 | 306 |
86 @override | 307 @override |
87 Iterator<ClassElement> get iterator { | 308 Iterator<ClassElement> get iterator { |
88 return new ClassHierarchyNodeIterator(this); | 309 return new ClassHierarchyNodeIterator(this); |
89 } | 310 } |
90 } | 311 } |
91 | 312 |
92 /// Iterator for subclasses of a [ClassHierarchyNode]. | 313 /// Iterator for subclasses of a [ClassHierarchyNode]. |
(...skipping 12 matching lines...) Expand all Loading... |
105 /// | 326 /// |
106 /// This is `null` before the first call to [moveNext]. | 327 /// This is `null` before the first call to [moveNext]. |
107 Link<ClassHierarchyNode> stack; | 328 Link<ClassHierarchyNode> stack; |
108 | 329 |
109 ClassHierarchyNodeIterator(this.iterable); | 330 ClassHierarchyNodeIterator(this.iterable); |
110 | 331 |
111 ClassHierarchyNode get root => iterable.root; | 332 ClassHierarchyNode get root => iterable.root; |
112 | 333 |
113 bool get includeRoot => iterable.includeRoot; | 334 bool get includeRoot => iterable.includeRoot; |
114 | 335 |
115 bool get directlyInstantiatedOnly => iterable.directlyInstantiatedOnly; | 336 bool get includeDirectlyInstantiated => iterable.includeDirectlyInstantiated; |
| 337 |
| 338 bool get includeIndirectlyInstantiated { |
| 339 return iterable.includeIndirectlyInstantiated; |
| 340 } |
| 341 |
| 342 bool get includeUninstantiated => iterable.includeUninstantiated; |
116 | 343 |
117 @override | 344 @override |
118 ClassElement get current { | 345 ClassElement get current { |
119 return currentNode != null ? currentNode.cls : null; | 346 return currentNode != null ? currentNode.cls : null; |
120 } | 347 } |
121 | 348 |
122 @override | 349 @override |
123 bool moveNext() { | 350 bool moveNext() { |
124 if (stack == null) { | 351 if (stack == null) { |
125 // First call to moveNext | 352 // First call to moveNext |
(...skipping 10 matching lines...) Expand all Loading... |
136 bool _findNext() { | 363 bool _findNext() { |
137 while (true) { | 364 while (true) { |
138 if (stack.isEmpty) { | 365 if (stack.isEmpty) { |
139 // No more classes. Set [currentNode] to `null` to signal the end of | 366 // No more classes. Set [currentNode] to `null` to signal the end of |
140 // iteration. | 367 // iteration. |
141 currentNode = null; | 368 currentNode = null; |
142 return false; | 369 return false; |
143 } | 370 } |
144 currentNode = stack.head; | 371 currentNode = stack.head; |
145 stack = stack.tail; | 372 stack = stack.tail; |
| 373 if (!includeUninstantiated && !currentNode.isInstantiated) { |
| 374 // We're only iterating instantiated classes so there is no use in |
| 375 // visiting the current node and its subtree. |
| 376 continue; |
| 377 } |
146 for (Link<ClassHierarchyNode> link = currentNode._directSubclasses; | 378 for (Link<ClassHierarchyNode> link = currentNode._directSubclasses; |
147 !link.isEmpty; | 379 !link.isEmpty; |
148 link = link.tail) { | 380 link = link.tail) { |
149 stack = stack.prepend(link.head); | 381 stack = stack.prepend(link.head); |
150 } | 382 } |
151 if (_isValid(currentNode)) { | 383 if (_isValid(currentNode)) { |
152 return true; | 384 return true; |
153 } | 385 } |
154 } | 386 } |
155 } | 387 } |
156 | 388 |
157 /// Returns `true` if the class of [node] is a valid result for this iterator. | 389 /// Returns `true` if the class of [node] is a valid result for this iterator. |
158 bool _isValid(ClassHierarchyNode node) { | 390 bool _isValid(ClassHierarchyNode node) { |
159 if (!includeRoot && node == root) return false; | 391 if (!includeRoot && node == root) return false; |
160 if (directlyInstantiatedOnly && !node.isDirectlyInstantiated) return false; | 392 if (includeDirectlyInstantiated && node.isDirectlyInstantiated) { |
161 return true; | 393 return true; |
| 394 } |
| 395 if (includeIndirectlyInstantiated && node.isIndirectlyInstantiated) { |
| 396 return true; |
| 397 } |
| 398 if (includeUninstantiated && !node.isInstantiated) { |
| 399 return true; |
| 400 } |
| 401 return false; |
162 } | 402 } |
163 } | 403 } |
164 | 404 |
| 405 /// Iterable for the subtypes in a [ClassSet]. |
| 406 class SubtypesIterable extends IterableBase<ClassElement> { |
| 407 final ClassSet subtypeSet; |
| 408 final bool includeRoot; |
| 409 final bool includeDirectlyInstantiated; |
| 410 final bool includeIndirectlyInstantiated; |
| 411 final bool includeUninstantiated; |
165 | 412 |
| 413 SubtypesIterable.SubtypesIterator( |
| 414 this.subtypeSet, |
| 415 {this.includeRoot: true, |
| 416 this.includeDirectlyInstantiated: true, |
| 417 this.includeIndirectlyInstantiated: true, |
| 418 this.includeUninstantiated: true}); |
| 419 |
| 420 @override |
| 421 Iterator<ClassElement> get iterator => new SubtypesIterator(this); |
| 422 } |
| 423 |
| 424 /// Iterator for the subtypes in a [ClassSet]. |
| 425 class SubtypesIterator extends Iterator<ClassElement> { |
| 426 final SubtypesIterable iterable; |
| 427 Iterator<ClassElement> elements; |
| 428 Iterator<ClassHierarchyNode> hierarchyNodes; |
| 429 |
| 430 SubtypesIterator(this.iterable); |
| 431 |
| 432 bool get includeRoot => iterable.includeRoot; |
| 433 |
| 434 bool get includeDirectlyInstantiated => iterable.includeDirectlyInstantiated; |
| 435 |
| 436 bool get includeIndirectlyInstantiated { |
| 437 return iterable.includeIndirectlyInstantiated; |
| 438 } |
| 439 |
| 440 bool get includeUninstantiated => iterable.includeUninstantiated; |
| 441 |
| 442 @override |
| 443 ClassElement get current { |
| 444 if (elements != null) { |
| 445 return elements.current; |
| 446 } |
| 447 return null; |
| 448 } |
| 449 |
| 450 @override |
| 451 bool moveNext() { |
| 452 if (elements == null && hierarchyNodes == null) { |
| 453 // Initial state. Iterate through subclasses. |
| 454 elements = iterable.subtypeSet.node.subclasses( |
| 455 strict: !includeRoot, |
| 456 includeDirectlyInstantiated: includeDirectlyInstantiated, |
| 457 includeIndirectlyInstantiated: includeIndirectlyInstantiated, |
| 458 includeUninstantiated: includeUninstantiated).iterator; |
| 459 } |
| 460 if (elements != null && elements.moveNext()) { |
| 461 return true; |
| 462 } |
| 463 if (hierarchyNodes == null) { |
| 464 // Start iterating through subtypes. |
| 465 hierarchyNodes = iterable.subtypeSet._directSubtypes.iterator; |
| 466 } |
| 467 while (hierarchyNodes.moveNext()) { |
| 468 elements = hierarchyNodes.current.subclasses( |
| 469 includeDirectlyInstantiated: includeDirectlyInstantiated, |
| 470 includeIndirectlyInstantiated: includeIndirectlyInstantiated, |
| 471 includeUninstantiated: includeUninstantiated).iterator; |
| 472 if (elements.moveNext()) { |
| 473 return true; |
| 474 } |
| 475 } |
| 476 return false; |
| 477 } |
| 478 } |
OLD | NEW |