OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 part of masks; | 5 part of masks; |
6 | 6 |
7 /** | 7 /** |
8 * A flat type mask is a type mask that has been flattened to contain a | 8 * A flat type mask is a type mask that has been flattened to contain a |
9 * base type. | 9 * base type. |
10 */ | 10 */ |
11 class FlatTypeMask implements TypeMask { | 11 class FlatTypeMask implements TypeMask { |
12 static const int EMPTY = 0; | 12 static const int EMPTY = 0; |
13 static const int EXACT = 1; | 13 static const int EXACT = 1; |
14 static const int SUBCLASS = 2; | 14 static const int SUBCLASS = 2; |
15 static const int SUBTYPE = 3; | 15 static const int SUBTYPE = 3; |
16 | 16 |
17 final ClassElement base; | 17 final Entity base; |
18 final int flags; | 18 final int flags; |
19 | 19 |
20 FlatTypeMask(ClassElement base, int kind, bool isNullable) | 20 FlatTypeMask(Entity base, int kind, bool isNullable) |
21 : this.internal(base, (kind << 1) | (isNullable ? 1 : 0)); | 21 : this.internal(base, (kind << 1) | (isNullable ? 1 : 0)); |
22 | 22 |
23 FlatTypeMask.exact(ClassElement base) : this.internal(base, (EXACT << 1) | 1); | 23 FlatTypeMask.exact(Entity base) : this.internal(base, (EXACT << 1) | 1); |
24 FlatTypeMask.subclass(ClassElement base) | 24 FlatTypeMask.subclass(Entity base) : this.internal(base, (SUBCLASS << 1) | 1); |
25 : this.internal(base, (SUBCLASS << 1) | 1); | 25 FlatTypeMask.subtype(Entity base) : this.internal(base, (SUBTYPE << 1) | 1); |
26 FlatTypeMask.subtype(ClassElement base) | |
27 : this.internal(base, (SUBTYPE << 1) | 1); | |
28 | 26 |
29 const FlatTypeMask.nonNullEmpty() | 27 const FlatTypeMask.nonNullEmpty() |
30 : base = null, | 28 : base = null, |
31 flags = 0; | 29 flags = 0; |
32 const FlatTypeMask.empty() | 30 const FlatTypeMask.empty() |
33 : base = null, | 31 : base = null, |
34 flags = 1; | 32 flags = 1; |
35 | 33 |
36 FlatTypeMask.nonNullExact(ClassElement base) | 34 FlatTypeMask.nonNullExact(Entity base) : this.internal(base, EXACT << 1); |
37 : this.internal(base, EXACT << 1); | 35 FlatTypeMask.nonNullSubclass(Entity base) |
38 FlatTypeMask.nonNullSubclass(ClassElement base) | |
39 : this.internal(base, SUBCLASS << 1); | 36 : this.internal(base, SUBCLASS << 1); |
40 FlatTypeMask.nonNullSubtype(ClassElement base) | 37 FlatTypeMask.nonNullSubtype(Entity base) : this.internal(base, SUBTYPE << 1); |
41 : this.internal(base, SUBTYPE << 1); | 38 |
| 39 bool _validateBase(ClassElement element) => element.isDeclaration; |
42 | 40 |
43 FlatTypeMask.internal(this.base, this.flags) { | 41 FlatTypeMask.internal(this.base, this.flags) { |
44 assert(base == null || base.isDeclaration); | 42 assert(base == null || _validateBase(base)); |
45 } | 43 } |
46 | 44 |
47 /** | 45 /** |
48 * Ensures that the generated mask is normalized, i.e., a call to | 46 * Ensures that the generated mask is normalized, i.e., a call to |
49 * [TypeMask.assertIsNormalized] with the factory's result returns `true`. | 47 * [TypeMask.assertIsNormalized] with the factory's result returns `true`. |
50 */ | 48 */ |
51 factory FlatTypeMask.normalized( | 49 factory FlatTypeMask.normalized(Entity base, int flags, ClosedWorld world) { |
52 ClassElement base, int flags, ClosedWorld world) { | |
53 if ((flags >> 1) == EMPTY || ((flags >> 1) == EXACT)) { | 50 if ((flags >> 1) == EMPTY || ((flags >> 1) == EXACT)) { |
54 return new FlatTypeMask.internal(base, flags); | 51 return new FlatTypeMask.internal(base, flags); |
55 } | 52 } |
56 if ((flags >> 1) == SUBTYPE) { | 53 if ((flags >> 1) == SUBTYPE) { |
57 if (!world.hasAnyStrictSubtype(base) || world.hasOnlySubclasses(base)) { | 54 if (!world.hasAnyStrictSubtype(base) || world.hasOnlySubclasses(base)) { |
58 flags = (flags & 0x1) | (SUBCLASS << 1); | 55 flags = (flags & 0x1) | (SUBCLASS << 1); |
59 } | 56 } |
60 } | 57 } |
61 if (((flags >> 1) == SUBCLASS) && !world.hasAnyStrictSubclass(base)) { | 58 if (((flags >> 1) == SUBCLASS) && !world.hasAnyStrictSubclass(base)) { |
62 flags = (flags & 0x1) | (EXACT << 1); | 59 flags = (flags & 0x1) | (EXACT << 1); |
63 } | 60 } |
64 Map<ClassElement, TypeMask> cachedMasks = | 61 return world.getCachedMask( |
65 world.canonicalizedTypeMasks[flags]; | 62 base, flags, () => new FlatTypeMask.internal(base, flags)); |
66 if (cachedMasks == null) { | |
67 world.canonicalizedTypeMasks[flags] = | |
68 cachedMasks = <ClassElement, TypeMask>{}; | |
69 } | |
70 return cachedMasks.putIfAbsent( | |
71 base, () => new FlatTypeMask.internal(base, flags)); | |
72 } | 63 } |
73 | 64 |
74 bool get isEmpty => isEmptyOrNull && !isNullable; | 65 bool get isEmpty => isEmptyOrNull && !isNullable; |
75 bool get isNull => isEmptyOrNull && isNullable; | 66 bool get isNull => isEmptyOrNull && isNullable; |
76 bool get isEmptyOrNull => (flags >> 1) == EMPTY; | 67 bool get isEmptyOrNull => (flags >> 1) == EMPTY; |
77 bool get isExact => (flags >> 1) == EXACT; | 68 bool get isExact => (flags >> 1) == EXACT; |
78 bool get isNullable => (flags & 1) != 0; | 69 bool get isNullable => (flags & 1) != 0; |
79 | 70 |
80 bool get isUnion => false; | 71 bool get isUnion => false; |
81 bool get isContainer => false; | 72 bool get isContainer => false; |
82 bool get isMap => false; | 73 bool get isMap => false; |
83 bool get isDictionary => false; | 74 bool get isDictionary => false; |
84 bool get isForwarding => false; | 75 bool get isForwarding => false; |
85 bool get isValue => false; | 76 bool get isValue => false; |
86 | 77 |
87 // TODO(kasperl): Get rid of these. They should not be a visible | 78 // TODO(kasperl): Get rid of these. They should not be a visible |
88 // part of the implementation because they make it hard to add | 79 // part of the implementation because they make it hard to add |
89 // proper union types if we ever want to. | 80 // proper union types if we ever want to. |
90 bool get isSubclass => (flags >> 1) == SUBCLASS; | 81 bool get isSubclass => (flags >> 1) == SUBCLASS; |
91 bool get isSubtype => (flags >> 1) == SUBTYPE; | 82 bool get isSubtype => (flags >> 1) == SUBTYPE; |
92 | 83 |
93 TypeMask nullable() { | 84 TypeMask nullable() { |
94 return isNullable ? this : new FlatTypeMask.internal(base, flags | 1); | 85 return isNullable ? this : new FlatTypeMask.internal(base, flags | 1); |
95 } | 86 } |
96 | 87 |
97 TypeMask nonNullable() { | 88 TypeMask nonNullable() { |
98 return isNullable ? new FlatTypeMask.internal(base, flags & ~1) : this; | 89 return isNullable ? new FlatTypeMask.internal(base, flags & ~1) : this; |
99 } | 90 } |
100 | 91 |
101 bool contains(ClassElement type, ClosedWorld closedWorld) { | 92 bool contains(Entity other, ClosedWorld closedWorld) { |
102 assert(type.isDeclaration); | 93 assert(_validateBase(other)); |
103 if (isEmptyOrNull) { | 94 if (isEmptyOrNull) { |
104 return false; | 95 return false; |
105 } else if (identical(base, type)) { | 96 } else if (identical(base, other)) { |
106 return true; | 97 return true; |
107 } else if (isExact) { | 98 } else if (isExact) { |
108 return false; | 99 return false; |
109 } else if (isSubclass) { | 100 } else if (isSubclass) { |
110 return closedWorld.isSubclassOf(type, base); | 101 return closedWorld.isSubclassOf(other, base); |
111 } else { | 102 } else { |
112 assert(isSubtype); | 103 assert(isSubtype); |
113 return closedWorld.isSubtypeOf(type, base); | 104 return closedWorld.isSubtypeOf(other, base); |
114 } | 105 } |
115 } | 106 } |
116 | 107 |
117 bool isSingleImplementationOf(ClassElement cls, ClosedWorld closedWorld) { | 108 bool isSingleImplementationOf(Entity cls, ClosedWorld closedWorld) { |
118 // Special case basic types so that, for example, JSString is the | 109 // Special case basic types so that, for example, JSString is the |
119 // single implementation of String. | 110 // single implementation of String. |
120 // The general optimization is to realize there is only one class that | 111 // The general optimization is to realize there is only one class that |
121 // implements [base] and [base] is not instantiated. We however do | 112 // implements [base] and [base] is not instantiated. We however do |
122 // not track correctly the list of truly instantiated classes. | 113 // not track correctly the list of truly instantiated classes. |
123 BackendClasses backendClasses = closedWorld.backendClasses; | 114 BackendClasses backendClasses = closedWorld.backendClasses; |
124 if (containsOnlyString(closedWorld)) { | 115 if (containsOnlyString(closedWorld)) { |
125 return cls == closedWorld.coreClasses.stringClass || | 116 return cls == closedWorld.coreClasses.stringClass || |
126 cls == backendClasses.stringImplementation; | 117 cls == backendClasses.stringImplementation; |
127 } | 118 } |
(...skipping 19 matching lines...) Expand all Loading... |
147 if (isEmptyOrNull) return isNullable ? other.isNullable : true; | 138 if (isEmptyOrNull) return isNullable ? other.isNullable : true; |
148 // The empty type contains no classes. | 139 // The empty type contains no classes. |
149 if (other.isEmptyOrNull) return false; | 140 if (other.isEmptyOrNull) return false; |
150 // Quick check whether to handle null. | 141 // Quick check whether to handle null. |
151 if (isNullable && !other.isNullable) return false; | 142 if (isNullable && !other.isNullable) return false; |
152 other = TypeMask.nonForwardingMask(other); | 143 other = TypeMask.nonForwardingMask(other); |
153 // If other is union, delegate to UnionTypeMask.containsMask. | 144 // If other is union, delegate to UnionTypeMask.containsMask. |
154 if (other is! FlatTypeMask) return other.containsMask(this, closedWorld); | 145 if (other is! FlatTypeMask) return other.containsMask(this, closedWorld); |
155 // The other must be flat, so compare base and flags. | 146 // The other must be flat, so compare base and flags. |
156 FlatTypeMask flatOther = other; | 147 FlatTypeMask flatOther = other; |
157 ClassElement otherBase = flatOther.base; | 148 Entity otherBase = flatOther.base; |
158 // If other is exact, it only contains its base. | 149 // If other is exact, it only contains its base. |
159 // TODO(herhut): Get rid of isSingleImplementationOf. | 150 // TODO(herhut): Get rid of isSingleImplementationOf. |
160 if (flatOther.isExact) { | 151 if (flatOther.isExact) { |
161 return (isExact && base == otherBase) || | 152 return (isExact && base == otherBase) || |
162 isSingleImplementationOf(otherBase, closedWorld); | 153 isSingleImplementationOf(otherBase, closedWorld); |
163 } | 154 } |
164 // If other is subclass, this has to be subclass, as well. Unless | 155 // If other is subclass, this has to be subclass, as well. Unless |
165 // flatOther.base covers all subtypes of this. Currently, we only | 156 // flatOther.base covers all subtypes of this. Currently, we only |
166 // consider object to behave that way. | 157 // consider object to behave that way. |
167 // TODO(herhut): Add check whether flatOther.base is superclass of | 158 // TODO(herhut): Add check whether flatOther.base is superclass of |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
207 return base == closedWorld.coreClasses.boolClass || | 198 return base == closedWorld.coreClasses.boolClass || |
208 base == backendClasses.boolImplementation; | 199 base == backendClasses.boolImplementation; |
209 } | 200 } |
210 | 201 |
211 bool containsOnlyString(ClosedWorld closedWorld) { | 202 bool containsOnlyString(ClosedWorld closedWorld) { |
212 BackendClasses backendClasses = closedWorld.backendClasses; | 203 BackendClasses backendClasses = closedWorld.backendClasses; |
213 return base == closedWorld.coreClasses.stringClass || | 204 return base == closedWorld.coreClasses.stringClass || |
214 base == backendClasses.stringImplementation; | 205 base == backendClasses.stringImplementation; |
215 } | 206 } |
216 | 207 |
217 bool containsOnly(ClassElement cls) { | 208 bool containsOnly(Entity cls) { |
218 assert(cls.isDeclaration); | 209 assert(_validateBase(cls)); |
219 return base == cls; | 210 return base == cls; |
220 } | 211 } |
221 | 212 |
222 bool satisfies(ClassElement cls, ClosedWorld closedWorld) { | 213 bool satisfies(Entity cls, ClosedWorld closedWorld) { |
223 assert(cls.isDeclaration); | 214 assert(_validateBase(cls)); |
224 if (isEmptyOrNull) return false; | 215 if (isEmptyOrNull) return false; |
225 if (closedWorld.isSubtypeOf(base, cls)) return true; | 216 if (closedWorld.isSubtypeOf(base, cls)) return true; |
226 return false; | 217 return false; |
227 } | 218 } |
228 | 219 |
229 /** | 220 /// Returns the [Entity] if this type represents a single class, otherwise |
230 * Returns the [ClassElement] if this type represents a single class, | 221 /// returns `null`. This method is conservative. |
231 * otherwise returns `null`. This method is conservative. | 222 Entity singleClass(ClosedWorld closedWorld) { |
232 */ | |
233 ClassElement singleClass(ClosedWorld closedWorld) { | |
234 if (isEmptyOrNull) return null; | 223 if (isEmptyOrNull) return null; |
235 if (isNullable) return null; // It is Null and some other class. | 224 if (isNullable) return null; // It is Null and some other class. |
236 if (isExact) { | 225 if (isExact) { |
237 return base; | 226 return base; |
238 } else if (isSubclass) { | 227 } else if (isSubclass) { |
239 return closedWorld.hasAnyStrictSubclass(base) ? null : base; | 228 return closedWorld.hasAnyStrictSubclass(base) ? null : base; |
240 } else { | 229 } else { |
241 assert(isSubtype); | 230 assert(isSubtype); |
242 return null; | 231 return null; |
243 } | 232 } |
(...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
472 TypeMask intersectionHelper(FlatTypeMask other, ClosedWorld closedWorld) { | 461 TypeMask intersectionHelper(FlatTypeMask other, ClosedWorld closedWorld) { |
473 assert(base != other.base); | 462 assert(base != other.base); |
474 assert(!closedWorld.isSubclassOf(base, other.base)); | 463 assert(!closedWorld.isSubclassOf(base, other.base)); |
475 assert(!closedWorld.isSubclassOf(other.base, base)); | 464 assert(!closedWorld.isSubclassOf(other.base, base)); |
476 // If one of the masks are exact or if both of them are subclass | 465 // If one of the masks are exact or if both of them are subclass |
477 // masks, then the intersection is empty. | 466 // masks, then the intersection is empty. |
478 if (isExact || other.isExact) return intersectionEmpty(other); | 467 if (isExact || other.isExact) return intersectionEmpty(other); |
479 if (isSubclass && other.isSubclass) return intersectionEmpty(other); | 468 if (isSubclass && other.isSubclass) return intersectionEmpty(other); |
480 assert(isSubtype || other.isSubtype); | 469 assert(isSubtype || other.isSubtype); |
481 int kind = (isSubclass || other.isSubclass) ? SUBCLASS : SUBTYPE; | 470 int kind = (isSubclass || other.isSubclass) ? SUBCLASS : SUBTYPE; |
| 471 // TODO(johnniwinther): Move this computation to [ClosedWorld]. |
482 // Compute the set of classes that are contained in both type masks. | 472 // Compute the set of classes that are contained in both type masks. |
483 Set<ClassElement> common = commonContainedClasses(this, other, closedWorld); | 473 Set<ClassElement> common = commonContainedClasses(this, other, closedWorld); |
484 if (common == null || common.isEmpty) return intersectionEmpty(other); | 474 if (common == null || common.isEmpty) return intersectionEmpty(other); |
485 // Narrow down the candidates by only looking at common classes | 475 // Narrow down the candidates by only looking at common classes |
486 // that do not have a superclass or supertype that will be a | 476 // that do not have a superclass or supertype that will be a |
487 // better candidate. | 477 // better candidate. |
488 Iterable<ClassElement> candidates = common.where((ClassElement each) { | 478 Iterable<ClassElement> candidates = common.where((ClassElement each) { |
489 bool containsSuperclass = common.contains(each.supertype.element); | 479 bool containsSuperclass = common.contains(each.supertype.element); |
490 // If the superclass is also a candidate, then we don't want to | 480 // If the superclass is also a candidate, then we don't want to |
491 // deal with this class. If we're only looking for a subclass we | 481 // deal with this class. If we're only looking for a subclass we |
492 // know we don't have to look at the list of interfaces because | 482 // know we don't have to look at the list of interfaces because |
493 // they can never be in the common set. | 483 // they can never be in the common set. |
494 if (containsSuperclass || kind == SUBCLASS) return !containsSuperclass; | 484 if (containsSuperclass || kind == SUBCLASS) return !containsSuperclass; |
495 // Run through the direct supertypes of the class. If the common | 485 // Run through the direct supertypes of the class. If the common |
496 // set contains the direct supertype of the class, we ignore the | 486 // set contains the direct supertype of the class, we ignore the |
497 // the class because the supertype is a better candidate. | 487 // the class because the supertype is a better candidate. |
498 for (Link link = each.interfaces; !link.isEmpty; link = link.tail) { | 488 for (Link link = each.interfaces; !link.isEmpty; link = link.tail) { |
499 if (common.contains(link.head.element)) return false; | 489 if (common.contains(link.head.element)) return false; |
500 } | 490 } |
501 return true; | 491 return true; |
502 }); | 492 }); |
503 // Run through the list of candidates and compute the union. The | 493 // Run through the list of candidates and compute the union. The |
504 // result will only be nullable if both masks are nullable. We have | 494 // result will only be nullable if both masks are nullable. We have |
505 // to normalize here, as we generate types based on new base classes. | 495 // to normalize here, as we generate types based on new base classes. |
506 int combined = (kind << 1) | (flags & other.flags & 1); | 496 int combined = (kind << 1) | (flags & other.flags & 1); |
507 Iterable<TypeMask> masks = candidates.map((ClassElement cls) { | 497 Iterable<TypeMask> masks = candidates.map((Entity cls) { |
508 return new FlatTypeMask.normalized(cls, combined, closedWorld); | 498 return new FlatTypeMask.normalized(cls, combined, closedWorld); |
509 }); | 499 }); |
510 return UnionTypeMask.unionOf(masks, closedWorld); | 500 return UnionTypeMask.unionOf(masks, closedWorld); |
511 } | 501 } |
512 | 502 |
513 TypeMask intersectionEmpty(FlatTypeMask other) { | 503 TypeMask intersectionEmpty(FlatTypeMask other) { |
514 return isNullable && other.isNullable | 504 return isNullable && other.isNullable |
515 ? new TypeMask.empty() | 505 ? new TypeMask.empty() |
516 : new TypeMask.nonNullEmpty(); | 506 : new TypeMask.nonNullEmpty(); |
517 } | 507 } |
518 | 508 |
519 /** | 509 /** |
520 * Returns whether [element] will be the one used at runtime when being | |
521 * invoked on an instance of [cls]. [selector] is used to ensure library | |
522 * privacy is taken into account. | |
523 */ | |
524 static bool hasElementIn( | |
525 ClassElement cls, Selector selector, Element element) { | |
526 // Use [:implementation:] of [element] | |
527 // because our function set only stores declarations. | |
528 Element result = findMatchIn(cls, selector); | |
529 return result == null | |
530 ? false | |
531 : result.implementation == element.implementation; | |
532 } | |
533 | |
534 static Element findMatchIn(ClassElement cls, Selector selector) { | |
535 // Use the [:implementation] of [cls] in case the found [element] | |
536 // is in the patch class. | |
537 return cls.implementation.lookupByName(selector.memberName); | |
538 } | |
539 | |
540 /** | |
541 * Returns whether [element] is a potential target when being | 510 * Returns whether [element] is a potential target when being |
542 * invoked on this type mask. [selector] is used to ensure library | 511 * invoked on this type mask. [selector] is used to ensure library |
543 * privacy is taken into account. | 512 * privacy is taken into account. |
544 */ | 513 */ |
545 bool canHit(Element element, Selector selector, ClosedWorld closedWorld) { | 514 bool canHit(Element element, Selector selector, ClosedWorld closedWorld) { |
546 BackendClasses backendClasses = closedWorld.backendClasses; | 515 BackendClasses backendClasses = closedWorld.backendClasses; |
547 assert(element.name == selector.name); | 516 assert(element.name == selector.name); |
548 if (isEmpty) return false; | 517 if (isEmpty) return false; |
549 if (isNull) { | 518 if (isNull) { |
550 return hasElementIn(backendClasses.nullImplementation, selector, element); | 519 return closedWorld.hasElementIn( |
| 520 backendClasses.nullImplementation, selector, element); |
551 } | 521 } |
552 | 522 |
553 // TODO(kasperl): Can't we just avoid creating typed selectors | 523 // TODO(kasperl): Can't we just avoid creating typed selectors |
554 // based of function types? | 524 // based of function types? |
555 Element self = base; | 525 Element self = base; |
556 if (self.isTypedef) { | 526 if (self.isTypedef) { |
557 // A typedef is a function type that doesn't have any | 527 // A typedef is a function type that doesn't have any |
558 // user-defined members. | 528 // user-defined members. |
559 return false; | 529 return false; |
560 } | 530 } |
561 | 531 |
562 ClassElement other = element.enclosingClass; | 532 ClassElement other = element.enclosingClass; |
563 if (other == backendClasses.nullImplementation) { | 533 if (other == backendClasses.nullImplementation) { |
564 return isNullable; | 534 return isNullable; |
565 } else if (isExact) { | 535 } else if (isExact) { |
566 return hasElementIn(self, selector, element); | 536 return closedWorld.hasElementIn(self, selector, element); |
567 } else if (isSubclass) { | 537 } else if (isSubclass) { |
568 return hasElementIn(self, selector, element) || | 538 return closedWorld.hasElementIn(self, selector, element) || |
569 other.isSubclassOf(self) || | 539 other.isSubclassOf(self) || |
570 closedWorld.hasAnySubclassThatMixes(self, other); | 540 closedWorld.hasAnySubclassThatMixes(self, other); |
571 } else { | 541 } else { |
572 assert(isSubtype); | 542 assert(isSubtype); |
573 bool result = hasElementIn(self, selector, element) || | 543 bool result = closedWorld.hasElementIn(self, selector, element) || |
574 other.implementsInterface(self) || | 544 other.implementsInterface(self) || |
575 closedWorld.hasAnySubclassThatImplements(other, base) || | 545 closedWorld.hasAnySubclassThatImplements(other, base) || |
576 closedWorld.hasAnySubclassOfMixinUseThatImplements(other, base); | 546 closedWorld.hasAnySubclassOfMixinUseThatImplements(other, base); |
577 if (result) return true; | 547 if (result) return true; |
578 // If the class is used as a mixin, we have to check if the element | 548 // If the class is used as a mixin, we have to check if the element |
579 // can be hit from any of the mixin applications. | 549 // can be hit from any of the mixin applications. |
580 Iterable<ClassElement> mixinUses = closedWorld.mixinUsesOf(self); | 550 Iterable<ClassElement> mixinUses = closedWorld.mixinUsesOf(self); |
581 return mixinUses.any((mixinApplication) => | 551 return mixinUses.any((mixinApplication) => |
582 hasElementIn(mixinApplication, selector, element) || | 552 closedWorld.hasElementIn(mixinApplication, selector, element) || |
583 other.isSubclassOf(mixinApplication) || | 553 other.isSubclassOf(mixinApplication) || |
584 closedWorld.hasAnySubclassThatMixes(mixinApplication, other)); | 554 closedWorld.hasAnySubclassThatMixes(mixinApplication, other)); |
585 } | 555 } |
586 } | 556 } |
587 | 557 |
588 /** | |
589 * Returns whether a [selector] call on an instance of [cls] | |
590 * will hit a method at runtime, and not go through [noSuchMethod]. | |
591 */ | |
592 static bool hasConcreteMatch( | |
593 ClassElement cls, Selector selector, ClosedWorld world) { | |
594 assert(invariant(cls, world.isInstantiated(cls), | |
595 message: '$cls has not been instantiated.')); | |
596 Element element = findMatchIn(cls, selector); | |
597 if (element == null) return false; | |
598 | |
599 if (element.isAbstract) { | |
600 ClassElement enclosingClass = element.enclosingClass; | |
601 return hasConcreteMatch(enclosingClass.superclass, selector, world); | |
602 } | |
603 return selector.appliesUntyped(element); | |
604 } | |
605 | |
606 bool needsNoSuchMethodHandling(Selector selector, ClosedWorld closedWorld) { | 558 bool needsNoSuchMethodHandling(Selector selector, ClosedWorld closedWorld) { |
607 // A call on an empty type mask is either dead code, or a call on | 559 // A call on an empty type mask is either dead code, or a call on |
608 // `null`. | 560 // `null`. |
609 if (isEmptyOrNull) return false; | 561 if (isEmptyOrNull) return false; |
610 // A call on an exact mask for an abstract class is dead code. | 562 // A call on an exact mask for an abstract class is dead code. |
611 if (isExact && base.isAbstract) return false; | 563 // TODO(johnniwinther): A type mask cannot be abstract. Remove the need |
612 // If the receiver is guaranteed to have a member that | 564 // for this noise (currently used for super-calls in inference and mirror |
613 // matches what we're looking for, there's no need to | 565 // usage). |
614 // introduce a noSuchMethod handler. It will never be called. | 566 if (isExact && closedWorld.isAbstract(base)) return false; |
615 // | |
616 // As an example, consider this class hierarchy: | |
617 // | |
618 // A <-- noSuchMethod | |
619 // / \ | |
620 // C B <-- foo | |
621 // | |
622 // If we know we're calling foo on an object of type B we | |
623 // don't have to worry about the noSuchMethod method in A | |
624 // because objects of type B implement foo. On the other hand, | |
625 // if we end up calling foo on something of type C we have to | |
626 // add a handler for it. | |
627 | 567 |
628 // If the holders of all user-defined noSuchMethod | 568 return closedWorld.needsNoSuchMethod( |
629 // implementations that might be applicable to the receiver | 569 base, |
630 // type have a matching member for the current name and | 570 selector, |
631 // selector, we avoid introducing a noSuchMethod handler. | 571 isExact |
632 // | 572 ? ClassQuery.EXACT |
633 // As an example, consider this class hierarchy: | 573 : (isSubclass ? ClassQuery.SUBCLASS : ClassQuery.SUBTYPE)); |
634 // | |
635 // A <-- foo | |
636 // / \ | |
637 // noSuchMethod --> B C <-- bar | |
638 // | | | |
639 // C D <-- noSuchMethod | |
640 // | |
641 // When calling foo on an object of type A, we know that the | |
642 // implementations of noSuchMethod are in the classes B and D | |
643 // that also (indirectly) implement foo, so we do not need a | |
644 // handler for it. | |
645 // | |
646 // If we're calling bar on an object of type D, we don't need | |
647 // the handler either because all objects of type D implement | |
648 // bar through inheritance. | |
649 // | |
650 // If we're calling bar on an object of type A we do need the | |
651 // handler because we may have to call B.noSuchMethod since B | |
652 // does not implement bar. | |
653 | |
654 /// Returns `true` if [cls] is an instantiated class that does not have | |
655 /// a concrete method matching [selector]. | |
656 bool needsNoSuchMethod(ClassElement cls) { | |
657 // We can skip uninstantiated subclasses. | |
658 // TODO(johnniwinther): Put filtering into the (Class)World. | |
659 if (!closedWorld.isInstantiated(cls)) { | |
660 return false; | |
661 } | |
662 // We can just skip abstract classes because we know no | |
663 // instance of them will be created at runtime, and | |
664 // therefore there is no instance that will require | |
665 // [noSuchMethod] handling. | |
666 return !cls.isAbstract && !hasConcreteMatch(cls, selector, closedWorld); | |
667 } | |
668 | |
669 bool baseNeedsNoSuchMethod = needsNoSuchMethod(base); | |
670 if (isExact || baseNeedsNoSuchMethod) { | |
671 return baseNeedsNoSuchMethod; | |
672 } | |
673 | |
674 Iterable<ClassElement> subclassesToCheck; | |
675 if (isSubtype) { | |
676 subclassesToCheck = closedWorld.strictSubtypesOf(base); | |
677 } else { | |
678 assert(isSubclass); | |
679 subclassesToCheck = closedWorld.strictSubclassesOf(base); | |
680 } | |
681 | |
682 return subclassesToCheck != null && | |
683 subclassesToCheck.any(needsNoSuchMethod); | |
684 } | 574 } |
685 | 575 |
686 Element locateSingleElement(Selector selector, Compiler compiler) { | 576 Element locateSingleElement(Selector selector, Compiler compiler) { |
687 if (isEmptyOrNull) return null; | 577 if (isEmptyOrNull) return null; |
688 Iterable<Element> targets = | 578 Iterable<Element> targets = |
689 compiler.closedWorld.allFunctions.filter(selector, this); | 579 compiler.closedWorld.allFunctions.filter(selector, this); |
690 if (targets.length != 1) return null; | 580 if (targets.length != 1) return null; |
691 Element result = targets.first; | 581 Element result = targets.first; |
692 ClassElement enclosing = result.enclosingClass; | 582 ClassElement enclosing = result.enclosingClass.declaration; |
693 // We only return the found element if it is guaranteed to be implemented on | 583 // We only return the found element if it is guaranteed to be implemented on |
694 // all classes in the receiver type [this]. It could be found only in a | 584 // all classes in the receiver type [this]. It could be found only in a |
695 // subclass or in an inheritance-wise unrelated class in case of subtype | 585 // subclass or in an inheritance-wise unrelated class in case of subtype |
696 // selectors. | 586 // selectors. |
697 ClosedWorld closedWorld = compiler.closedWorld; | 587 ClosedWorld closedWorld = compiler.closedWorld; |
698 if (isSubtype) { | 588 if (isSubtype) { |
699 // if (closedWorld.isUsedAsMixin(enclosing)) { | 589 // if (closedWorld.isUsedAsMixin(enclosing)) { |
700 if (closedWorld.everySubtypeIsSubclassOfOrMixinUseOf(base, enclosing)) { | 590 if (closedWorld.everySubtypeIsSubclassOfOrMixinUseOf(base, enclosing)) { |
701 return result; | 591 return result; |
702 } | 592 } |
703 //} | 593 //} |
704 return null; | 594 return null; |
705 } else { | 595 } else { |
706 if (base.isSubclassOf(enclosing)) return result; | 596 if (closedWorld.isSubclassOf(base, enclosing)) return result; |
707 if (closedWorld.isSubclassOfMixinUseOf(base, enclosing)) return result; | 597 if (closedWorld.isSubclassOfMixinUseOf(base, enclosing)) return result; |
708 } | 598 } |
709 return null; | 599 return null; |
710 } | 600 } |
711 | 601 |
712 bool operator ==(var other) { | 602 bool operator ==(var other) { |
713 if (identical(this, other)) return true; | 603 if (identical(this, other)) return true; |
714 if (other is! FlatTypeMask) return false; | 604 if (other is! FlatTypeMask) return false; |
715 FlatTypeMask otherMask = other; | 605 FlatTypeMask otherMask = other; |
716 return (flags == otherMask.flags) && (base == otherMask.base); | 606 return (flags == otherMask.flags) && (base == otherMask.base); |
(...skipping 29 matching lines...) Expand all Loading... |
746 if (x.isExact) { | 636 if (x.isExact) { |
747 return null; | 637 return null; |
748 } else if (x.isSubclass) { | 638 } else if (x.isSubclass) { |
749 return closedWorld.strictSubclassesOf(element); | 639 return closedWorld.strictSubclassesOf(element); |
750 } else { | 640 } else { |
751 assert(x.isSubtype); | 641 assert(x.isSubtype); |
752 return closedWorld.strictSubtypesOf(element); | 642 return closedWorld.strictSubtypesOf(element); |
753 } | 643 } |
754 } | 644 } |
755 } | 645 } |
OLD | NEW |