| 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 |