Chromium Code Reviews| 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 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.
| |
| 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 model because they are implied | |
|
karlklose
2015/09/08 12:09:21
'model' -> 'modeled'?
Johnni Winther
2015/09/09 17:19:20
Done.
| |
| 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> 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.
| |
| 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 && iterables == 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 (iterables == null) { | |
| 464 // Start iterating through subtypes. | |
| 465 iterables = iterable.subtypeSet._directSubtypes.iterator; | |
| 466 } | |
| 467 while (iterables.moveNext()) { | |
| 468 elements = iterables.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 |