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 |