| 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 | 7 import 'dart:collection' show |
| 8 IterableBase; | 8 IterableBase; |
| 9 import '../common.dart'; | |
| 10 import '../elements/elements.dart' show | 9 import '../elements/elements.dart' show |
| 11 ClassElement; | 10 ClassElement; |
| 12 import '../util/enumset.dart' show | 11 import '../util/enumset.dart' show |
| 13 EnumSet; | 12 EnumSet; |
| 14 import '../util/util.dart' show | 13 import '../util/util.dart' show |
| 15 Link; | 14 Link; |
| 16 | 15 |
| 17 /// Enum for the different kinds of instantiation of a class. | 16 /// Enum for the different kinds of instantiation of a class. |
| 18 enum Instantiation { | 17 enum Instantiation { |
| 19 UNINSTANTIATED, | 18 UNINSTANTIATED, |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 86 } | 85 } |
| 87 if (includeIndirectlyInstantiated) { | 86 if (includeIndirectlyInstantiated) { |
| 88 mask.add(Instantiation.INDIRECTLY_INSTANTIATED); | 87 mask.add(Instantiation.INDIRECTLY_INSTANTIATED); |
| 89 } | 88 } |
| 90 if (includeUninstantiated) { | 89 if (includeUninstantiated) { |
| 91 mask.add(Instantiation.UNINSTANTIATED); | 90 mask.add(Instantiation.UNINSTANTIATED); |
| 92 } | 91 } |
| 93 return mask; | 92 return mask; |
| 94 } | 93 } |
| 95 | 94 |
| 96 final ClassHierarchyNode parentNode; | |
| 97 final ClassElement cls; | 95 final ClassElement cls; |
| 98 final EnumSet<Instantiation> _mask = | 96 final EnumSet<Instantiation> _mask = |
| 99 new EnumSet<Instantiation>.fromValues( | 97 new EnumSet<Instantiation>.fromValues( |
| 100 const <Instantiation>[Instantiation.UNINSTANTIATED]); | 98 const <Instantiation>[Instantiation.UNINSTANTIATED]); |
| 101 | 99 |
| 102 ClassElement _leastUpperInstantiatedSubclass; | 100 ClassElement _leastUpperInstantiatedSubclass; |
| 103 int _instantiatedSubclassCount = 0; | |
| 104 | 101 |
| 105 /// `true` if [cls] has been directly instantiated. | 102 /// `true` if [cls] has been directly instantiated. |
| 106 /// | 103 /// |
| 107 /// For instance `C` but _not_ `B` in: | 104 /// For instance `C` but _not_ `B` in: |
| 108 /// class B {} | 105 /// class B {} |
| 109 /// class C extends B {} | 106 /// class C extends B {} |
| 110 /// main() => new C(); | 107 /// main() => new C(); |
| 111 /// | 108 /// |
| 112 bool get isDirectlyInstantiated => | 109 bool get isDirectlyInstantiated => |
| 113 _mask.contains(Instantiation.DIRECTLY_INSTANTIATED); | 110 _mask.contains(Instantiation.DIRECTLY_INSTANTIATED); |
| 114 | 111 |
| 115 void set isDirectlyInstantiated(bool value) { | 112 void set isDirectlyInstantiated(bool value) { |
| 116 if (value != isDirectlyInstantiated) { | 113 if (value != isDirectlyInstantiated) { |
| 117 ClassHierarchyNode parent = parentNode; | |
| 118 if (value) { | 114 if (value) { |
| 119 _mask.remove(Instantiation.UNINSTANTIATED); | 115 _mask.remove(Instantiation.UNINSTANTIATED); |
| 120 _mask.add(Instantiation.DIRECTLY_INSTANTIATED); | 116 _mask.add(Instantiation.DIRECTLY_INSTANTIATED); |
| 121 while (parent != null) { | |
| 122 parent._updateInstantiatedSubclassCount(1); | |
| 123 parent = parent.parentNode; | |
| 124 } | |
| 125 } else { | 117 } else { |
| 126 _mask.remove(Instantiation.DIRECTLY_INSTANTIATED); | 118 _mask.remove(Instantiation.DIRECTLY_INSTANTIATED); |
| 127 if (_mask.isEmpty) { | 119 if (_mask.isEmpty) { |
| 128 _mask.add(Instantiation.UNINSTANTIATED); | 120 _mask.add(Instantiation.UNINSTANTIATED); |
| 129 } | 121 } |
| 130 while (parent != null) { | |
| 131 parent._updateInstantiatedSubclassCount(-1); | |
| 132 parent = parent.parentNode; | |
| 133 } | |
| 134 } | 122 } |
| 135 } | 123 } |
| 136 } | 124 } |
| 137 | 125 |
| 138 /// `true` if [cls] has been instantiated through subclasses. | 126 /// `true` if [cls] has been instantiated through subclasses. |
| 139 /// | 127 /// |
| 140 /// For instance `A` and `B` but _not_ `C` in: | 128 /// For instance `A` and `B` but _not_ `C` in: |
| 141 /// class A {} | 129 /// class A {} |
| 142 /// class B extends A {} | 130 /// class B extends A {} |
| 143 /// class C extends B {} | 131 /// class C extends B {} |
| 144 /// main() => [new B(), new C()]; | 132 /// main() => [new B(), new C()]; |
| 145 /// | 133 /// |
| 146 bool get isIndirectlyInstantiated => _instantiatedSubclassCount > 0; | 134 bool get isIndirectlyInstantiated => |
| 135 _mask.contains(Instantiation.INDIRECTLY_INSTANTIATED); |
| 147 | 136 |
| 148 /// The number of strict subclasses that are directly or indirectly | 137 void set isIndirectlyInstantiated(bool value) { |
| 149 /// instantiated. | 138 if (value != isIndirectlyInstantiated) { |
| 150 int get instantiatedSubclassCount => _instantiatedSubclassCount; | 139 if (value) { |
| 151 | |
| 152 void _updateInstantiatedSubclassCount(int change) { | |
| 153 bool before = isIndirectlyInstantiated; | |
| 154 _instantiatedSubclassCount += change; | |
| 155 bool after = isIndirectlyInstantiated; | |
| 156 if (before != after) { | |
| 157 if (after) { | |
| 158 _mask.remove(Instantiation.UNINSTANTIATED); | 140 _mask.remove(Instantiation.UNINSTANTIATED); |
| 159 _mask.add(Instantiation.INDIRECTLY_INSTANTIATED); | 141 _mask.add(Instantiation.INDIRECTLY_INSTANTIATED); |
| 160 } else { | 142 } else { |
| 161 _mask.remove(Instantiation.INDIRECTLY_INSTANTIATED); | 143 _mask.remove(Instantiation.INDIRECTLY_INSTANTIATED); |
| 162 if (_mask.isEmpty) { | 144 if (_mask.isEmpty) { |
| 163 _mask.add(Instantiation.UNINSTANTIATED); | 145 _mask.add(Instantiation.UNINSTANTIATED); |
| 164 } | 146 } |
| 165 } | 147 } |
| 166 } | 148 } |
| 167 } | 149 } |
| 168 | 150 |
| 169 /// The nodes for the direct subclasses of [cls]. | 151 /// The nodes for the direct subclasses of [cls]. |
| 170 Link<ClassHierarchyNode> _directSubclasses = const Link<ClassHierarchyNode>(); | 152 Link<ClassHierarchyNode> _directSubclasses = const Link<ClassHierarchyNode>(); |
| 171 | 153 |
| 172 ClassHierarchyNode(this.parentNode, this.cls) { | 154 ClassHierarchyNode(this.cls); |
| 173 if (parentNode != null) { | |
| 174 parentNode.addDirectSubclass(this); | |
| 175 } | |
| 176 } | |
| 177 | 155 |
| 178 /// Adds [subclass] as a direct subclass of [cls]. | 156 /// Adds [subclass] as a direct subclass of [cls]. |
| 179 void addDirectSubclass(ClassHierarchyNode subclass) { | 157 void addDirectSubclass(ClassHierarchyNode subclass) { |
| 180 assert(subclass.cls.superclass == cls); | 158 assert(subclass.cls.superclass == cls); |
| 181 assert(!_directSubclasses.contains(subclass)); | 159 assert(!_directSubclasses.contains(subclass)); |
| 182 _directSubclasses = _directSubclasses.prepend(subclass); | 160 _directSubclasses = _directSubclasses.prepend(subclass); |
| 183 } | 161 } |
| 184 | 162 |
| 185 /// Returns `true` if [other] is contained in the subtree of this node. | 163 /// Returns `true` if [other] is contained in the subtree of this node. |
| 186 /// | 164 /// |
| 187 /// This means that [other] is a subclass of [cls]. | 165 /// This means that [other] is a subclass of [cls]. |
| 188 bool contains(ClassElement other) { | 166 bool contains(ClassElement other) { |
| 189 while (other != null) { | 167 while (other != null) { |
| 190 if (cls == other) return true; | 168 if (cls == other) return true; |
| 191 if (cls.hierarchyDepth >= other.hierarchyDepth) return false; | 169 if (cls.hierarchyDepth >= other.hierarchyDepth) return false; |
| 192 other = other.superclass; | 170 other = other.superclass; |
| 193 } | 171 } |
| 194 return false; | 172 return false; |
| 195 } | 173 } |
| 196 | 174 |
| 197 /// `true` if [cls] has been directly or indirectly instantiated. | 175 /// `true` if [cls] has been directly or indirectly instantiated. |
| 198 bool get isInstantiated => isDirectlyInstantiated || isIndirectlyInstantiated; | 176 bool get isInstantiated => isDirectlyInstantiated || isIndirectlyInstantiated; |
| 199 | 177 |
| 200 /// Returns an [Iterable] of the subclasses of [cls] possibly including [cls]. | 178 /// Returns an [Iterable] of the subclasses of [cls] possibly including [cls]. |
| 201 /// | 179 /// |
| 180 /// The directly instantiated, indirectly instantiated and uninstantiated |
| 181 /// subclasses of [cls] are returned if [includeDirectlyInstantiated], |
| 182 /// [includeIndirectlyInstantiated], and [includeUninstantiated] are `true`, |
| 183 /// respectively. If [strict] is `true`, [cls] itself is _not_ returned. |
| 184 Iterable<ClassElement> subclasses( |
| 185 {bool includeDirectlyInstantiated: true, |
| 186 bool includeIndirectlyInstantiated: true, |
| 187 bool includeUninstantiated: true, |
| 188 bool strict: false}) { |
| 189 EnumSet<Instantiation> mask = createMask( |
| 190 includeDirectlyInstantiated: includeDirectlyInstantiated, |
| 191 includeIndirectlyInstantiated:includeIndirectlyInstantiated, |
| 192 includeUninstantiated: includeUninstantiated); |
| 193 return subclassesByMask(mask, strict: strict); |
| 194 } |
| 195 |
| 196 /// Returns an [Iterable] of the subclasses of [cls] possibly including [cls]. |
| 197 /// |
| 202 /// Subclasses are included if their instantiation properties intersect with | 198 /// Subclasses are included if their instantiation properties intersect with |
| 203 /// their corresponding [Instantiation] values in [mask]. If [strict] is | 199 /// their corresponding [Instantiation] values in [mask]. If [strict] is |
| 204 /// `true`, [cls] itself is _not_ returned. | 200 /// `true`, [cls] itself is _not_ returned. |
| 205 Iterable<ClassElement> subclassesByMask( | 201 Iterable<ClassElement> subclassesByMask( |
| 206 EnumSet<Instantiation> mask, | 202 EnumSet<Instantiation> mask, |
| 207 {bool strict: false}) { | 203 {bool strict: false}) { |
| 208 return new ClassHierarchyNodeIterable( | 204 return new ClassHierarchyNodeIterable( |
| 209 this, mask, includeRoot: !strict); | 205 this, mask, includeRoot: !strict); |
| 210 } | 206 } |
| 211 | 207 |
| 212 /// Applies [predicate] to each subclass of [cls] matching the criteria | |
| 213 /// specified by [mask] and [strict]. If [predicate] returns `true` on a | |
| 214 /// class, visitation is stopped immediately and the function returns `true`. | |
| 215 /// | |
| 216 /// [predicate] is applied to subclasses if their instantiation properties | |
| 217 /// intersect with their corresponding [Instantiation] values in [mask]. If | |
| 218 /// [strict] is `true`, [predicate] is _not_ called on [cls] itself. | |
| 219 bool anySubclass( | |
| 220 bool predicate(ClassElement cls), | |
| 221 EnumSet<Instantiation> mask, | |
| 222 {bool strict: false}) { | |
| 223 | |
| 224 ForEach wrapper(ClassElement cls) { | |
| 225 return predicate(cls) ? ForEach.STOP : ForEach.CONTINUE; | |
| 226 } | |
| 227 return forEachSubclass(wrapper, mask, strict: strict) == ForEach.STOP; | |
| 228 } | |
| 229 | |
| 230 /// Applies [f] to each subclass of [cls] matching the criteria specified by | |
| 231 /// [mask] and [strict]. | |
| 232 /// | |
| 233 /// [f] is a applied to subclasses if their instantiation properties intersect | |
| 234 /// with their corresponding [Instantiation] values in [mask]. If [strict] is | |
| 235 /// `true`, [f] is _not_ called on [cls] itself. | |
| 236 /// | |
| 237 /// The visitation of subclasses can be cut short by the return value of [f]. | |
| 238 /// If [ForEach.STOP] is returned, no further classes are visited and the | |
| 239 /// function stops immediately. If [ForEach.SKIP_CHILDREN] is returned, the | |
| 240 /// subclasses of the last visited class are skipped, but visitation | |
| 241 /// continues. The return value of the function is either [ForEach.STOP], if | |
| 242 /// visitation was stopped, or [ForEach.CONTINUE] if visitation continued to | |
| 243 /// the end. | |
| 244 ForEach forEachSubclass( | |
| 245 ForEachFunction f, | |
| 246 EnumSet<Instantiation> mask, | |
| 247 {bool strict: false}) { | |
| 248 ForEach forEach; | |
| 249 if (!strict && mask.intersects(_mask)) { | |
| 250 forEach = f(cls); | |
| 251 } | |
| 252 // Interpret `forEach == null` as `forEach == ForEach.CONTINUE`. | |
| 253 forEach ??= ForEach.CONTINUE; | |
| 254 | |
| 255 if (!mask.contains(Instantiation.UNINSTANTIATED) && !isInstantiated) { | |
| 256 // Bypass children since all are uninstantiated. | |
| 257 return forEach; | |
| 258 } | |
| 259 if (forEach == ForEach.CONTINUE) { | |
| 260 for (ClassHierarchyNode subclass in _directSubclasses) { | |
| 261 ForEach subForEach = subclass.forEachSubclass(f, mask); | |
| 262 if (subForEach == ForEach.STOP) { | |
| 263 return subForEach; | |
| 264 } | |
| 265 } | |
| 266 } | |
| 267 return forEach; | |
| 268 } | |
| 269 | |
| 270 /// Returns the most specific subclass of [cls] (including [cls]) that is | 208 /// Returns the most specific subclass of [cls] (including [cls]) that is |
| 271 /// directly instantiated or a superclass of all directly instantiated | 209 /// directly instantiated or a superclass of all directly instantiated |
| 272 /// subclasses. If [cls] is not instantiated, `null` is returned. | 210 /// subclasses. If [cls] is not instantiated, `null` is returned. |
| 273 ClassElement getLubOfInstantiatedSubclasses() { | 211 ClassElement getLubOfInstantiatedSubclasses() { |
| 274 if (!isInstantiated) return null; | 212 if (!isInstantiated) return null; |
| 275 if (_leastUpperInstantiatedSubclass == null) { | 213 if (_leastUpperInstantiatedSubclass == null) { |
| 276 _leastUpperInstantiatedSubclass = | 214 _leastUpperInstantiatedSubclass = |
| 277 _computeLeastUpperInstantiatedSubclass(); | 215 _computeLeastUpperInstantiatedSubclass(); |
| 278 } | 216 } |
| 279 return _leastUpperInstantiatedSubclass; | 217 return _leastUpperInstantiatedSubclass; |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 330 if (sorted) { | 268 if (sorted) { |
| 331 subclasses = _directSubclasses.toList()..sort((a, b) { | 269 subclasses = _directSubclasses.toList()..sort((a, b) { |
| 332 return a.cls.name.compareTo(b.cls.name); | 270 return a.cls.name.compareTo(b.cls.name); |
| 333 }); | 271 }); |
| 334 } | 272 } |
| 335 bool needsComma = false; | 273 bool needsComma = false; |
| 336 for (ClassHierarchyNode child in subclasses) { | 274 for (ClassHierarchyNode child in subclasses) { |
| 337 if (instantiatedOnly && !child.isInstantiated) { | 275 if (instantiatedOnly && !child.isInstantiated) { |
| 338 continue; | 276 continue; |
| 339 } | 277 } |
| 340 if (withRespectTo != null && | 278 if (withRespectTo != null && !child.subclasses().any(isRelatedTo)) { |
| 341 !child.anySubclass(isRelatedTo, ClassHierarchyNode.ALL)) { | |
| 342 continue; | 279 continue; |
| 343 } | 280 } |
| 344 if (needsComma) { | 281 if (needsComma) { |
| 345 sb.write(',\n'); | 282 sb.write(',\n'); |
| 346 } else { | 283 } else { |
| 347 sb.write('\n'); | 284 sb.write('\n'); |
| 348 } | 285 } |
| 349 child.printOn( | 286 child.printOn( |
| 350 sb, | 287 sb, |
| 351 '$indentation ', | 288 '$indentation ', |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 423 class ClassSet { | 360 class ClassSet { |
| 424 final ClassHierarchyNode node; | 361 final ClassHierarchyNode node; |
| 425 ClassElement _leastUpperInstantiatedSubtype; | 362 ClassElement _leastUpperInstantiatedSubtype; |
| 426 | 363 |
| 427 List<ClassHierarchyNode> _directSubtypes; | 364 List<ClassHierarchyNode> _directSubtypes; |
| 428 | 365 |
| 429 ClassSet(this.node); | 366 ClassSet(this.node); |
| 430 | 367 |
| 431 ClassElement get cls => node.cls; | 368 ClassElement get cls => node.cls; |
| 432 | 369 |
| 433 /// Returns the number of directly instantiated subtypes of [cls]. | 370 /// Returns an [Iterable] of the subclasses of [cls] possibly including [cls]. |
| 434 int get instantiatedSubtypeCount { | 371 /// |
| 435 int count = node.instantiatedSubclassCount; | 372 /// The directly instantiated, indirectly instantiated and uninstantiated |
| 436 if (_directSubtypes != null) { | 373 /// subclasses of [cls] are returned if [includeDirectlyInstantiated], |
| 437 for (ClassHierarchyNode subtypeNode in _directSubtypes) { | 374 /// [includeIndirectlyInstantiated], and [includeUninstantiated] are `true`, |
| 438 if (subtypeNode.isDirectlyInstantiated) { | 375 /// respectively. If [strict] is `true`, [cls] itself is _not_ returned. |
| 439 count++; | 376 Iterable<ClassElement> subclasses( |
| 440 } | 377 {bool includeDirectlyInstantiated: true, |
| 441 count += subtypeNode.instantiatedSubclassCount; | 378 bool includeIndirectlyInstantiated: true, |
| 442 } | 379 bool includeUninstantiated: true, |
| 443 } | 380 bool strict: false}) { |
| 444 return count; | 381 EnumSet<Instantiation> mask = ClassHierarchyNode.createMask( |
| 445 } | 382 includeDirectlyInstantiated: includeDirectlyInstantiated, |
| 446 | 383 includeIndirectlyInstantiated:includeIndirectlyInstantiated, |
| 447 /// Returns `true` if all instantiated subtypes of [cls] are subclasses of | 384 includeUninstantiated: includeUninstantiated); |
| 448 /// [cls]. | 385 return subclassesByMask(mask, strict: strict); |
| 449 bool get hasOnlyInstantiatedSubclasses { | |
| 450 if (_directSubtypes != null) { | |
| 451 for (ClassHierarchyNode subtypeNode in _directSubtypes) { | |
| 452 if (subtypeNode.isInstantiated) { | |
| 453 return false; | |
| 454 } | |
| 455 } | |
| 456 } | |
| 457 return true; | |
| 458 } | 386 } |
| 459 | 387 |
| 460 /// Returns an [Iterable] of the subclasses of [cls] possibly including [cls]. | 388 /// Returns an [Iterable] of the subclasses of [cls] possibly including [cls]. |
| 461 /// | 389 /// |
| 462 /// Subclasses are included if their instantiation properties intersect with | 390 /// Subclasses are included if their instantiation properties intersect with |
| 463 /// their corresponding [Instantiation] values in [mask]. If [strict] is | 391 /// their corresponding [Instantiation] values in [mask]. If [strict] is |
| 464 /// `true`, [cls] itself is _not_ returned. | 392 /// `true`, [cls] itself is _not_ returned. |
| 465 Iterable<ClassElement> subclassesByMask( | 393 Iterable<ClassElement> subclassesByMask( |
| 466 EnumSet<Instantiation> mask, | 394 EnumSet<Instantiation> mask, |
| 467 {bool strict: false}) { | 395 {bool strict: false}) { |
| (...skipping 11 matching lines...) Expand all Loading... |
| 479 bool includeIndirectlyInstantiated: true, | 407 bool includeIndirectlyInstantiated: true, |
| 480 bool includeUninstantiated: true, | 408 bool includeUninstantiated: true, |
| 481 bool strict: false}) { | 409 bool strict: false}) { |
| 482 EnumSet<Instantiation> mask = ClassHierarchyNode.createMask( | 410 EnumSet<Instantiation> mask = ClassHierarchyNode.createMask( |
| 483 includeDirectlyInstantiated: includeDirectlyInstantiated, | 411 includeDirectlyInstantiated: includeDirectlyInstantiated, |
| 484 includeIndirectlyInstantiated:includeIndirectlyInstantiated, | 412 includeIndirectlyInstantiated:includeIndirectlyInstantiated, |
| 485 includeUninstantiated: includeUninstantiated); | 413 includeUninstantiated: includeUninstantiated); |
| 486 return subtypesByMask(mask, strict: strict); | 414 return subtypesByMask(mask, strict: strict); |
| 487 } | 415 } |
| 488 | 416 |
| 417 |
| 489 /// Returns an [Iterable] of the subtypes of [cls] possibly including [cls]. | 418 /// Returns an [Iterable] of the subtypes of [cls] possibly including [cls]. |
| 490 /// | 419 /// |
| 491 /// Subtypes are included if their instantiation properties intersect with | 420 /// Subtypes are included if their instantiation properties intersect with |
| 492 /// their corresponding [Instantiation] values in [mask]. If [strict] is | 421 /// their corresponding [Instantiation] values in [mask]. If [strict] is |
| 493 /// `true`, [cls] itself is _not_ returned. | 422 /// `true`, [cls] itself is _not_ returned. |
| 494 Iterable<ClassElement> subtypesByMask( | 423 Iterable<ClassElement> subtypesByMask( |
| 495 EnumSet<Instantiation> mask, | 424 EnumSet<Instantiation> mask, |
| 496 {bool strict: false}) { | 425 {bool strict: false}) { |
| 497 if (_directSubtypes == null) { | 426 if (_directSubtypes == null) { |
| 498 return node.subclassesByMask( | 427 return node.subclassesByMask( |
| 499 mask, | 428 mask, |
| 500 strict: strict); | 429 strict: strict); |
| 501 } | 430 } |
| 502 | 431 |
| 503 return new SubtypesIterable.SubtypesIterator(this, | 432 return new SubtypesIterable.SubtypesIterator(this, |
| 504 mask, | 433 mask, |
| 505 includeRoot: !strict); | 434 includeRoot: !strict); |
| 506 } | 435 } |
| 507 | 436 |
| 508 /// Applies [predicate] to each subclass of [cls] matching the criteria | |
| 509 /// specified by [mask] and [strict]. If [predicate] returns `true` on a | |
| 510 /// class, visitation is stopped immediately and the function returns `true`. | |
| 511 /// | |
| 512 /// [predicate] is applied to subclasses if their instantiation properties | |
| 513 /// intersect with their corresponding [Instantiation] values in [mask]. If | |
| 514 /// [strict] is `true`, [predicate] is _not_ called on [cls] itself. | |
| 515 bool anySubclass( | |
| 516 bool predicate(ClassElement cls), | |
| 517 EnumSet<Instantiation> mask, | |
| 518 {bool strict: false}) { | |
| 519 return node.anySubclass(predicate, mask, strict: strict); | |
| 520 } | |
| 521 | |
| 522 /// Applies [f] to each subclass of [cls] matching the criteria specified by | |
| 523 /// [mask] and [strict]. | |
| 524 /// | |
| 525 /// [f] is a applied to subclasses if their instantiation properties intersect | |
| 526 /// with their corresponding [Instantiation] values in [mask]. If [strict] is | |
| 527 /// `true`, [f] is _not_ called on [cls] itself. | |
| 528 /// | |
| 529 /// The visitation of subclasses can be cut short by the return value of [f]. | |
| 530 /// If [ForEach.STOP] is returned, no further classes are visited and the | |
| 531 /// function stops immediately. If [ForEach.SKIP_CHILDREN] is returned, the | |
| 532 /// subclasses of the last visited class are skipped, but visitation | |
| 533 /// continues. The return value of the function is either [ForEach.STOP], if | |
| 534 /// visitation was stopped, or [ForEach.CONTINUE] if visitation continued to | |
| 535 /// the end. | |
| 536 ForEach forEachSubclass( | |
| 537 ForEachFunction f, | |
| 538 EnumSet<Instantiation> mask, | |
| 539 {bool strict: false}) { | |
| 540 return node.forEachSubclass(f, mask, strict: strict); | |
| 541 } | |
| 542 | |
| 543 /// Applies [predicate] to each subtype of [cls] matching the criteria | |
| 544 /// specified by [mask] and [strict]. If [predicate] returns `true` on a | |
| 545 /// class, visitation is stopped immediately and the function returns `true`. | |
| 546 /// | |
| 547 /// [predicate] is applied to subtypes if their instantiation properties | |
| 548 /// intersect with their corresponding [Instantiation] values in [mask]. If | |
| 549 /// [strict] is `true`, [predicate] is _not_ called on [cls] itself. | |
| 550 bool anySubtype( | |
| 551 bool predicate(ClassElement cls), | |
| 552 EnumSet<Instantiation> mask, | |
| 553 {bool strict: false}) { | |
| 554 | |
| 555 ForEach wrapper(ClassElement cls) { | |
| 556 return predicate(cls) ? ForEach.STOP : ForEach.CONTINUE; | |
| 557 } | |
| 558 return forEachSubtype(wrapper, mask, strict: strict) == ForEach.STOP; | |
| 559 } | |
| 560 | |
| 561 /// Applies [f] to each subtype of [cls] matching the criteria specified by | |
| 562 /// [mask] and [strict]. | |
| 563 /// | |
| 564 /// [f] is a applied to subtypes if their instantiation properties intersect | |
| 565 /// with their corresponding [Instantiation] values in [mask]. If [strict] is | |
| 566 /// `true`, [f] is _not_ called on [cls] itself. | |
| 567 /// | |
| 568 /// The visitation of subtypes can be cut short by the return value of [f]. | |
| 569 /// If [ForEach.STOP] is returned, no further classes are visited and the | |
| 570 /// function stops immediately. If [ForEach.SKIP_CHILDREN] is returned, the | |
| 571 /// subclasses of the last visited class are skipped, but visitation | |
| 572 /// continues. The return value of the function is either [ForEach.STOP], if | |
| 573 /// visitation was stopped, or [ForEach.CONTINUE] if visitation continued to | |
| 574 /// the end. | |
| 575 ForEach forEachSubtype( | |
| 576 ForEachFunction f, | |
| 577 EnumSet<Instantiation> mask, | |
| 578 {bool strict: false}) { | |
| 579 if (!mask.contains(Instantiation.UNINSTANTIATED) && !node.isInstantiated) { | |
| 580 // Bypass children since all are uninstantiated. | |
| 581 return ForEach.CONTINUE; | |
| 582 } | |
| 583 ForEach forEach = node.forEachSubclass(f, mask, strict: strict); | |
| 584 forEach ??= ForEach.CONTINUE; | |
| 585 if (forEach == ForEach.CONTINUE && _directSubtypes != null) { | |
| 586 for (ClassHierarchyNode subclass in _directSubtypes) { | |
| 587 ForEach subForEach = subclass.forEachSubclass(f, mask); | |
| 588 if (subForEach == ForEach.STOP) { | |
| 589 return subForEach; | |
| 590 } | |
| 591 } | |
| 592 } | |
| 593 return forEach; | |
| 594 } | |
| 595 | |
| 596 /// Adds [subtype] as a subtype of [cls]. | 437 /// Adds [subtype] as a subtype of [cls]. |
| 597 void addSubtype(ClassHierarchyNode subtype) { | 438 void addSubtype(ClassHierarchyNode subtype) { |
| 598 if (node.contains(subtype.cls)) { | 439 if (node.contains(subtype.cls)) { |
| 599 return; | 440 return; |
| 600 } | 441 } |
| 601 if (_directSubtypes == null) { | 442 if (_directSubtypes == null) { |
| 602 _directSubtypes = <ClassHierarchyNode>[subtype]; | 443 _directSubtypes = <ClassHierarchyNode>[subtype]; |
| 603 } else { | 444 } else { |
| 604 int hierarchyDepth = subtype.cls.hierarchyDepth; | 445 int hierarchyDepth = subtype.cls.hierarchyDepth; |
| 605 List<ClassHierarchyNode> newSubtypes = <ClassHierarchyNode>[]; | 446 List<ClassHierarchyNode> newSubtypes = <ClassHierarchyNode>[]; |
| (...skipping 241 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 847 } | 688 } |
| 848 while (hierarchyNodes.moveNext()) { | 689 while (hierarchyNodes.moveNext()) { |
| 849 elements = hierarchyNodes.current.subclassesByMask(mask).iterator; | 690 elements = hierarchyNodes.current.subclassesByMask(mask).iterator; |
| 850 if (elements.moveNext()) { | 691 if (elements.moveNext()) { |
| 851 return true; | 692 return true; |
| 852 } | 693 } |
| 853 } | 694 } |
| 854 return false; | 695 return false; |
| 855 } | 696 } |
| 856 } | 697 } |
| 857 | |
| 858 /// Enum values returned from the [ForEachFunction] provided to the `forEachX` | |
| 859 /// functions of [ClassHierarchyNode] and [ClassSet]. The value is used to | |
| 860 /// control the continued iteration. | |
| 861 enum ForEach { | |
| 862 /// Iteration continues. | |
| 863 CONTINUE, | |
| 864 /// Iteration stops immediately. | |
| 865 STOP, | |
| 866 /// Iteration skips the child classes of the current class. | |
| 867 SKIP_CHILDREN, | |
| 868 } | |
| 869 | |
| 870 /// Visiting function used for the `forEachX` functions of [ClassHierarchyNode] | |
| 871 /// and [ClassSet]. The return value controls the continued iteration. If `null` | |
| 872 /// is returned, iteration continues to the end. | |
| 873 typedef ForEach ForEachFunction(ClassElement cls); | |
| OLD | NEW |