| // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| part of types; |
| |
| /** |
| * A flat type mask is a type mask that has been flatten to contain a |
| * base type. |
| */ |
| class FlatTypeMask implements TypeMask { |
| static const int EMPTY = 0; |
| static const int EXACT = 1; |
| static const int SUBCLASS = 2; |
| static const int SUBTYPE = 3; |
| |
| final ClassElement base; |
| final int flags; |
| |
| FlatTypeMask(ClassElement base, int kind, bool isNullable) |
| : this.internal(base, (kind << 1) | (isNullable ? 1 : 0)); |
| |
| FlatTypeMask.exact(ClassElement base) |
| : this.internal(base, (EXACT << 1) | 1); |
| FlatTypeMask.subclass(ClassElement base) |
| : this.internal(base, (SUBCLASS << 1) | 1); |
| FlatTypeMask.subtype(ClassElement base) |
| : this.internal(base, (SUBTYPE << 1) | 1); |
| |
| const FlatTypeMask.nonNullEmpty(): base = null, flags = 0; |
| const FlatTypeMask.empty() : base = null, flags = 1; |
| |
| FlatTypeMask.nonNullExact(ClassElement base) |
| : this.internal(base, EXACT << 1); |
| FlatTypeMask.nonNullSubclass(ClassElement base) |
| : this.internal(base, SUBCLASS << 1); |
| FlatTypeMask.nonNullSubtype(ClassElement base) |
| : this.internal(base, SUBTYPE << 1); |
| |
| FlatTypeMask.internal(this.base, this.flags) { |
| assert(base == null || base.isDeclaration); |
| } |
| |
| /** |
| * Ensures that the generated mask is normalized, i.e., a call to |
| * [TypeMask.assertIsNormalized] with the factory's result returns `true`. |
| */ |
| factory FlatTypeMask.normalized(ClassElement base, int flags, World world) { |
| if ((flags >> 1) == EMPTY || ((flags >> 1) == EXACT)) { |
| return new FlatTypeMask.internal(base, flags); |
| } |
| if ((flags >> 1) == SUBTYPE) { |
| if (!world.hasAnySubtype(base) || world.hasOnlySubclasses(base)) { |
| flags = (flags & 0x1) | (SUBCLASS << 1); |
| } |
| } |
| if (((flags >> 1) == SUBCLASS) && !world.hasAnySubclass(base)) { |
| flags = (flags & 0x1) | (EXACT << 1); |
| } |
| return new FlatTypeMask.internal(base, flags); |
| } |
| |
| bool get isEmpty => (flags >> 1) == EMPTY; |
| bool get isExact => (flags >> 1) == EXACT; |
| bool get isNullable => (flags & 1) != 0; |
| |
| bool get isUnion => false; |
| bool get isContainer => false; |
| bool get isMap => false; |
| bool get isDictionary => false; |
| bool get isForwarding => false; |
| bool get isValue => false; |
| |
| // TODO(kasperl): Get rid of these. They should not be a visible |
| // part of the implementation because they make it hard to add |
| // proper union types if we ever want to. |
| bool get isSubclass => (flags >> 1) == SUBCLASS; |
| bool get isSubtype => (flags >> 1) == SUBTYPE; |
| |
| TypeMask nullable() { |
| return isNullable ? this : new FlatTypeMask.internal(base, flags | 1); |
| } |
| |
| TypeMask nonNullable() { |
| return isNullable ? new FlatTypeMask.internal(base, flags & ~1) : this; |
| } |
| |
| bool contains(ClassElement type, ClassWorld classWorld) { |
| assert(type.isDeclaration); |
| if (isEmpty) { |
| return false; |
| } else if (identical(base, type)) { |
| return true; |
| } else if (isExact) { |
| return false; |
| } else if (isSubclass) { |
| return classWorld.isSubclassOf(type, base); |
| } else { |
| assert(isSubtype); |
| return classWorld.isSubtypeOf(type, base); |
| } |
| } |
| |
| bool isSingleImplementationOf(ClassElement cls, ClassWorld classWorld) { |
| // Special case basic types so that, for example, JSString is the |
| // single implementation of String. |
| // The general optimization is to realize there is only one class that |
| // implements [base] and [base] is not instantiated. We however do |
| // not track correctly the list of truly instantiated classes. |
| Backend backend = classWorld.backend; |
| if (containsOnlyString(classWorld)) { |
| return cls == classWorld.stringClass || |
| cls == backend.stringImplementation; |
| } |
| if (containsOnlyBool(classWorld)) { |
| return cls == classWorld.boolClass || cls == backend.boolImplementation; |
| } |
| if (containsOnlyInt(classWorld)) { |
| return cls == classWorld.intClass |
| || cls == backend.intImplementation |
| || cls == backend.positiveIntImplementation |
| || cls == backend.uint32Implementation |
| || cls == backend.uint31Implementation; |
| } |
| if (containsOnlyDouble(classWorld)) { |
| return cls == classWorld.doubleClass |
| || cls == backend.doubleImplementation; |
| } |
| return false; |
| } |
| |
| bool isInMask(TypeMask other, ClassWorld classWorld) { |
| // null is treated separately, so the empty mask might still contain it. |
| if (isEmpty) return isNullable ? other.isNullable : true; |
| // The empty type contains no classes. |
| if (other.isEmpty) return false; |
| // Quick check whether to handle null. |
| if (isNullable && !other.isNullable) return false; |
| other = TypeMask.nonForwardingMask(other); |
| // If other is union, delegate to UnionTypeMask.containsMask. |
| if (other is! FlatTypeMask) return other.containsMask(this, classWorld); |
| // The other must be flat, so compare base and flags. |
| FlatTypeMask flatOther = other; |
| ClassElement otherBase = flatOther.base; |
| // If other is exact, it only contains its base. |
| // TODO(herhut): Get rid of isSingleImplementationOf. |
| if (flatOther.isExact) { |
| return (isExact && base == otherBase) |
| || isSingleImplementationOf(otherBase, classWorld); |
| } |
| // If other is subclass, this has to be subclass, as well. Unless |
| // flatOther.base covers all subtypes of this. Currently, we only |
| // consider object to behave that way. |
| // TODO(herhut): Add check whether flatOther.base is superclass of |
| // all subclasses of this.base. |
| if (flatOther.isSubclass) { |
| if (isSubtype) return (otherBase == classWorld.objectClass); |
| return classWorld.isSubclassOf(base, otherBase); |
| } |
| assert(flatOther.isSubtype); |
| // Check whether this TypeMask satisfies otherBase's interface. |
| return satisfies(otherBase, classWorld); |
| } |
| |
| bool containsMask(TypeMask other, ClassWorld classWorld) { |
| return other.isInMask(this, classWorld); |
| } |
| |
| bool containsOnlyInt(ClassWorld classWorld) { |
| Backend backend = classWorld.backend; |
| return base == classWorld.intClass |
| || base == backend.intImplementation |
| || base == backend.positiveIntImplementation |
| || base == backend.uint31Implementation |
| || base == backend.uint32Implementation; |
| } |
| |
| bool containsOnlyDouble(ClassWorld classWorld) { |
| Backend backend = classWorld.backend; |
| return base == classWorld.doubleClass |
| || base == backend.doubleImplementation; |
| } |
| |
| bool containsOnlyNum(ClassWorld classWorld) { |
| Backend backend = classWorld.backend; |
| return containsOnlyInt(classWorld) |
| || containsOnlyDouble(classWorld) |
| || base == classWorld.numClass |
| || base == backend.numImplementation; |
| } |
| |
| bool containsOnlyBool(ClassWorld classWorld) { |
| Backend backend = classWorld.backend; |
| return base == classWorld.boolClass |
| || base == backend.boolImplementation; |
| } |
| |
| bool containsOnlyString(ClassWorld classWorld) { |
| Backend backend = classWorld.backend; |
| return base == classWorld.stringClass |
| || base == backend.stringImplementation; |
| } |
| |
| bool containsOnly(ClassElement cls) { |
| assert(cls.isDeclaration); |
| return base == cls; |
| } |
| |
| bool satisfies(ClassElement cls, ClassWorld classWorld) { |
| assert(cls.isDeclaration); |
| if (isEmpty) return false; |
| if (classWorld.isSubtypeOf(base, cls)) return true; |
| return false; |
| } |
| |
| /** |
| * Returns the [ClassElement] if this type represents a single class, |
| * otherwise returns `null`. This method is conservative. |
| */ |
| ClassElement singleClass(ClassWorld classWorld) { |
| if (isEmpty) return null; |
| if (isNullable) return null; // It is Null and some other class. |
| if (isExact) { |
| return base; |
| } else if (isSubclass) { |
| return classWorld.hasAnyStrictSubclass(base) ? null : base; |
| } else { |
| assert(isSubtype); |
| return null; |
| } |
| } |
| |
| /** |
| * Returns whether or not this type mask contains all types. |
| */ |
| bool containsAll(ClassWorld classWorld) { |
| if (isEmpty || isExact) return false; |
| return identical(base, classWorld.objectClass); |
| } |
| |
| TypeMask union(TypeMask other, ClassWorld classWorld) { |
| assert(other != null); |
| assert(TypeMask.assertIsNormalized(this, classWorld)); |
| assert(TypeMask.assertIsNormalized(other, classWorld)); |
| if (other is! FlatTypeMask) return other.union(this, classWorld); |
| FlatTypeMask flatOther = other; |
| if (isEmpty) { |
| return isNullable ? flatOther.nullable() : flatOther; |
| } else if (flatOther.isEmpty) { |
| return flatOther.isNullable ? nullable() : this; |
| } else if (base == flatOther.base) { |
| return unionSame(flatOther, classWorld); |
| } else if (classWorld.isSubclassOf(flatOther.base, base)) { |
| return unionStrictSubclass(flatOther, classWorld); |
| } else if (classWorld.isSubclassOf(base, flatOther.base)) { |
| return flatOther.unionStrictSubclass(this, classWorld); |
| } else if (classWorld.isSubtypeOf(flatOther.base, base)) { |
| return unionStrictSubtype(flatOther, classWorld); |
| } else if (classWorld.isSubtypeOf(base, flatOther.base)) { |
| return flatOther.unionStrictSubtype(this, classWorld); |
| } else { |
| return new UnionTypeMask._internal(<FlatTypeMask>[this, flatOther]); |
| } |
| } |
| |
| TypeMask unionSame(FlatTypeMask other, ClassWorld classWorld) { |
| assert(base == other.base); |
| assert(TypeMask.assertIsNormalized(this, classWorld)); |
| assert(TypeMask.assertIsNormalized(other, classWorld)); |
| // The two masks share the base type, so we must chose the least |
| // constraining kind (the highest) of the two. If either one of |
| // the masks are nullable the result should be nullable too. |
| // As both masks are normalized, the result will be, too. |
| int combined = (flags > other.flags) |
| ? flags | (other.flags & 1) |
| : other.flags | (flags & 1); |
| if (flags == combined) { |
| return this; |
| } else if (other.flags == combined) { |
| return other; |
| } else { |
| return new FlatTypeMask.normalized(base, combined, classWorld); |
| } |
| } |
| |
| TypeMask unionStrictSubclass(FlatTypeMask other, ClassWorld classWorld) { |
| assert(base != other.base); |
| assert(classWorld.isSubclassOf(other.base, base)); |
| assert(TypeMask.assertIsNormalized(this, classWorld)); |
| assert(TypeMask.assertIsNormalized(other, classWorld)); |
| int combined; |
| if ((isExact && other.isExact) || base == classWorld.objectClass) { |
| // Since the other mask is a subclass of this mask, we need the |
| // resulting union to be a subclass too. If either one of the |
| // masks are nullable the result should be nullable too. |
| combined = (SUBCLASS << 1) | ((flags | other.flags) & 1); |
| } else { |
| // Both masks are at least subclass masks, so we pick the least |
| // constraining kind (the highest) of the two. If either one of |
| // the masks are nullable the result should be nullable too. |
| combined = (flags > other.flags) |
| ? flags | (other.flags & 1) |
| : other.flags | (flags & 1); |
| } |
| // If we weaken the constraint on this type, we have to make sure that |
| // the result is normalized. |
| return (flags != combined) |
| ? new FlatTypeMask.normalized(base, combined, classWorld) |
| : this; |
| } |
| |
| TypeMask unionStrictSubtype(FlatTypeMask other, ClassWorld classWorld) { |
| assert(base != other.base); |
| assert(!classWorld.isSubclassOf(other.base, base)); |
| assert(classWorld.isSubtypeOf(other.base, base)); |
| assert(TypeMask.assertIsNormalized(this, classWorld)); |
| assert(TypeMask.assertIsNormalized(other, classWorld)); |
| // Since the other mask is a subtype of this mask, we need the |
| // resulting union to be a subtype too. If either one of the masks |
| // are nullable the result should be nullable too. |
| int combined = (SUBTYPE << 1) | ((flags | other.flags) & 1); |
| // We know there is at least one subtype, [other.base], so no need |
| // to normalize. |
| return (flags != combined) |
| ? new FlatTypeMask.normalized(base, combined, classWorld) |
| : this; |
| } |
| |
| TypeMask intersection(TypeMask other, ClassWorld classWorld) { |
| assert(other != null); |
| if (other is! FlatTypeMask) return other.intersection(this, classWorld); |
| assert(TypeMask.assertIsNormalized(this, classWorld)); |
| assert(TypeMask.assertIsNormalized(other, classWorld)); |
| FlatTypeMask flatOther = other; |
| if (isEmpty) { |
| return flatOther.isNullable ? this : nonNullable(); |
| } else if (flatOther.isEmpty) { |
| return isNullable ? flatOther : other.nonNullable(); |
| } else if (base == flatOther.base) { |
| return intersectionSame(flatOther, classWorld); |
| } else if (classWorld.isSubclassOf(flatOther.base, base)) { |
| return intersectionStrictSubclass(flatOther, classWorld); |
| } else if (classWorld.isSubclassOf(base, flatOther.base)) { |
| return flatOther.intersectionStrictSubclass(this, classWorld); |
| } else if (classWorld.isSubtypeOf(flatOther.base, base)) { |
| return intersectionStrictSubtype(flatOther, classWorld); |
| } else if (classWorld.isSubtypeOf(base, flatOther.base)) { |
| return flatOther.intersectionStrictSubtype(this, classWorld); |
| } else { |
| return intersectionDisjoint(flatOther, classWorld); |
| } |
| } |
| |
| TypeMask intersectionSame(FlatTypeMask other, ClassWorld classWorld) { |
| assert(base == other.base); |
| // The two masks share the base type, so we must chose the most |
| // constraining kind (the lowest) of the two. Only if both masks |
| // are nullable, will the result be nullable too. |
| // The result will be normalized, as the two inputs are normalized, too. |
| int combined = (flags < other.flags) |
| ? flags & ((other.flags & 1) | ~1) |
| : other.flags & ((flags & 1) | ~1); |
| if (flags == combined) { |
| return this; |
| } else if (other.flags == combined) { |
| return other; |
| } else { |
| return new FlatTypeMask.normalized(base, combined, classWorld); |
| } |
| } |
| |
| TypeMask intersectionStrictSubclass(FlatTypeMask other, |
| ClassWorld classWorld) { |
| assert(base != other.base); |
| assert(classWorld.isSubclassOf(other.base, base)); |
| // If this mask isn't at least a subclass mask, then the |
| // intersection with the other mask is empty. |
| if (isExact) return intersectionEmpty(other); |
| // Only the other mask puts constraints on the intersection mask, |
| // so base the combined flags on the other mask. Only if both |
| // masks are nullable, will the result be nullable too. |
| // The result is guaranteed to be normalized, as the other type |
| // was normalized. |
| int combined = other.flags & ((flags & 1) | ~1); |
| if (other.flags == combined) { |
| return other; |
| } else { |
| return new FlatTypeMask.normalized(other.base, combined, classWorld); |
| } |
| } |
| |
| TypeMask intersectionStrictSubtype(FlatTypeMask other, |
| ClassWorld classWorld) { |
| assert(base != other.base); |
| assert(classWorld.isSubtypeOf(other.base, base)); |
| if (!isSubtype) return intersectionHelper(other, classWorld); |
| // Only the other mask puts constraints on the intersection mask, |
| // so base the combined flags on the other mask. Only if both |
| // masks are nullable, will the result be nullable too. |
| // The result is guaranteed to be normalized, as the other type |
| // was normalized. |
| int combined = other.flags & ((flags & 1) | ~1); |
| if (other.flags == combined) { |
| return other; |
| } else { |
| return new FlatTypeMask.normalized(other.base, combined, classWorld); |
| } |
| } |
| |
| TypeMask intersectionDisjoint(FlatTypeMask other, ClassWorld classWorld) { |
| assert(base != other.base); |
| assert(!classWorld.isSubtypeOf(base, other.base)); |
| assert(!classWorld.isSubtypeOf(other.base, base)); |
| return intersectionHelper(other, classWorld); |
| } |
| |
| TypeMask intersectionHelper(FlatTypeMask other, ClassWorld classWorld) { |
| assert(base != other.base); |
| assert(!classWorld.isSubclassOf(base, other.base)); |
| assert(!classWorld.isSubclassOf(other.base, base)); |
| // If one of the masks are exact or if both of them are subclass |
| // masks, then the intersection is empty. |
| if (isExact || other.isExact) return intersectionEmpty(other); |
| if (isSubclass && other.isSubclass) return intersectionEmpty(other); |
| assert(isSubtype || other.isSubtype); |
| int kind = (isSubclass || other.isSubclass) ? SUBCLASS : SUBTYPE; |
| // Compute the set of classes that are contained in both type masks. |
| Set<ClassElement> common = commonContainedClasses(this, other, classWorld); |
| if (common == null || common.isEmpty) return intersectionEmpty(other); |
| // Narrow down the candidates by only looking at common classes |
| // that do not have a superclass or supertype that will be a |
| // better candidate. |
| Iterable<ClassElement> candidates = common.where((ClassElement each) { |
| bool containsSuperclass = common.contains(each.supertype.element); |
| // If the superclass is also a candidate, then we don't want to |
| // deal with this class. If we're only looking for a subclass we |
| // know we don't have to look at the list of interfaces because |
| // they can never be in the common set. |
| if (containsSuperclass || kind == SUBCLASS) return !containsSuperclass; |
| // Run through the direct supertypes of the class. If the common |
| // set contains the direct supertype of the class, we ignore the |
| // the class because the supertype is a better candidate. |
| for (Link link = each.interfaces; !link.isEmpty; link = link.tail) { |
| if (common.contains(link.head.element)) return false; |
| } |
| return true; |
| }); |
| // Run through the list of candidates and compute the union. The |
| // result will only be nullable if both masks are nullable. We have |
| // to normalize here, as we generate types based on new base classes. |
| int combined = (kind << 1) | (flags & other.flags & 1); |
| Iterable<TypeMask> masks = candidates.map((ClassElement cls) { |
| return new FlatTypeMask.normalized(cls, combined, classWorld); |
| }); |
| return UnionTypeMask.unionOf(masks, classWorld); |
| } |
| |
| TypeMask intersectionEmpty(FlatTypeMask other) { |
| return isNullable && other.isNullable ? new TypeMask.empty() |
| : new TypeMask.nonNullEmpty(); |
| } |
| |
| /** |
| * Returns whether [element] will be the one used at runtime when being |
| * invoked on an instance of [cls]. [selector] is used to ensure library |
| * privacy is taken into account. |
| */ |
| static bool hasElementIn(ClassElement cls, |
| Selector selector, |
| Element element) { |
| // Use [:implementation:] of [element] |
| // because our function set only stores declarations. |
| Element result = findMatchIn(cls, selector); |
| return result == null |
| ? false |
| : result.implementation == element.implementation; |
| } |
| |
| static Element findMatchIn(ClassElement cls, |
| Selector selector) { |
| // Use the [:implementation] of [cls] in case the found [element] |
| // is in the patch class. |
| return cls.implementation.lookupSelector(selector); |
| } |
| |
| /** |
| * Returns whether [element] is a potential target when being |
| * invoked on this type mask. [selector] is used to ensure library |
| * privacy is taken into account. |
| */ |
| bool canHit(Element element, Selector selector, ClassWorld classWorld) { |
| Backend backend = classWorld.backend; |
| assert(element.name == selector.name); |
| if (isEmpty) { |
| if (!isNullable) return false; |
| return hasElementIn(backend.nullImplementation, selector, element); |
| } |
| |
| // TODO(kasperl): Can't we just avoid creating typed selectors |
| // based of function types? |
| Element self = base; |
| if (self.isTypedef) { |
| // A typedef is a function type that doesn't have any |
| // user-defined members. |
| return false; |
| } |
| |
| ClassElement other = element.enclosingClass; |
| if (other == backend.nullImplementation) { |
| return isNullable; |
| } else if (isExact) { |
| return hasElementIn(self, selector, element); |
| } else if (isSubclass) { |
| assert(classWorld.isClosed); |
| return hasElementIn(self, selector, element) |
| || other.isSubclassOf(self) |
| || classWorld.hasAnySubclassThatMixes(self, other); |
| } else { |
| assert(isSubtype); |
| assert(classWorld.isClosed); |
| bool result = hasElementIn(self, selector, element) |
| || other.implementsInterface(self) |
| || classWorld.hasAnySubclassThatImplements(other, base) |
| || classWorld.hasAnySubclassOfMixinUseThatImplements(other, base); |
| if (result) return true; |
| // If the class is used as a mixin, we have to check if the element |
| // can be hit from any of the mixin applications. |
| Iterable<ClassElement> mixinUses = classWorld.mixinUsesOf(self); |
| return mixinUses.any((mixinApplication) => |
| hasElementIn(mixinApplication, selector, element) |
| || other.isSubclassOf(mixinApplication) |
| || classWorld.hasAnySubclassThatMixes(mixinApplication, other)); |
| } |
| } |
| |
| /** |
| * Returns whether a [selector] call on an instance of [cls] |
| * will hit a method at runtime, and not go through [noSuchMethod]. |
| */ |
| static bool hasConcreteMatch(ClassElement cls, |
| Selector selector, |
| World world) { |
| assert(invariant(cls, |
| world.compiler.resolverWorld.isInstantiated(cls), |
| message: '$cls has not been instantiated.')); |
| Element element = findMatchIn(cls, selector); |
| if (element == null) return false; |
| |
| if (element.isAbstract) { |
| ClassElement enclosingClass = element.enclosingClass; |
| return hasConcreteMatch(enclosingClass.superclass, selector, world); |
| } |
| return selector.appliesUntyped(element, world); |
| } |
| |
| bool needsNoSuchMethodHandling(Selector selector, ClassWorld classWorld) { |
| // A call on an empty type mask is either dead code, or a call on |
| // `null`. |
| if (isEmpty) return false; |
| // A call on an exact mask for an abstract class is dead code. |
| if (isExact && base.isAbstract) return false; |
| // If the receiver is guaranteed to have a member that |
| // matches what we're looking for, there's no need to |
| // introduce a noSuchMethod handler. It will never be called. |
| // |
| // As an example, consider this class hierarchy: |
| // |
| // A <-- noSuchMethod |
| // / \ |
| // C B <-- foo |
| // |
| // If we know we're calling foo on an object of type B we |
| // don't have to worry about the noSuchMethod method in A |
| // because objects of type B implement foo. On the other hand, |
| // if we end up calling foo on something of type C we have to |
| // add a handler for it. |
| |
| // If the holders of all user-defined noSuchMethod |
| // implementations that might be applicable to the receiver |
| // type have a matching member for the current name and |
| // selector, we avoid introducing a noSuchMethod handler. |
| // |
| // As an example, consider this class hierarchy: |
| // |
| // A <-- foo |
| // / \ |
| // noSuchMethod --> B C <-- bar |
| // | | |
| // C D <-- noSuchMethod |
| // |
| // When calling foo on an object of type A, we know that the |
| // implementations of noSuchMethod are in the classes B and D |
| // that also (indirectly) implement foo, so we do not need a |
| // handler for it. |
| // |
| // If we're calling bar on an object of type D, we don't need |
| // the handler either because all objects of type D implement |
| // bar through inheritance. |
| // |
| // If we're calling bar on an object of type A we do need the |
| // handler because we may have to call B.noSuchMethod since B |
| // does not implement bar. |
| |
| /// Returns `true` if [cls] is an instantiated class that does not have |
| /// a concrete method matching [selector]. |
| bool needsNoSuchMethod(ClassElement cls) { |
| // We can skip uninstantiated subclasses. |
| // TODO(johnniwinther): Put filtering into the (Class)World. |
| if (!classWorld.isInstantiated(cls)) { |
| return false; |
| } |
| // We can just skip abstract classes because we know no |
| // instance of them will be created at runtime, and |
| // therefore there is no instance that will require |
| // [noSuchMethod] handling. |
| return !cls.isAbstract |
| && !hasConcreteMatch(cls, selector, classWorld); |
| } |
| |
| bool baseNeedsNoSuchMethod = needsNoSuchMethod(base); |
| if (isExact || baseNeedsNoSuchMethod) { |
| return baseNeedsNoSuchMethod; |
| } |
| |
| Iterable<ClassElement> subclassesToCheck; |
| if (isSubtype) { |
| subclassesToCheck = classWorld.subtypesOf(base); |
| } else { |
| assert(isSubclass); |
| subclassesToCheck = classWorld.subclassesOf(base); |
| } |
| |
| return subclassesToCheck != null && |
| subclassesToCheck.any(needsNoSuchMethod); |
| } |
| |
| Element locateSingleElement(Selector selector, Compiler compiler) { |
| if (isEmpty) return null; |
| Iterable<Element> targets = compiler.world.allFunctions.filter(selector); |
| if (targets.length != 1) return null; |
| Element result = targets.first; |
| ClassElement enclosing = result.enclosingClass; |
| // We only return the found element if it is guaranteed to be |
| // implemented on the exact receiver type. It could be found in a |
| // subclass or in an inheritance-wise unrelated class in case of |
| // subtype selectors. |
| return (base.isSubclassOf(enclosing)) ? result : null; |
| } |
| |
| bool operator ==(var other) { |
| if (other is !FlatTypeMask) return false; |
| FlatTypeMask otherMask = other; |
| return (flags == otherMask.flags) && (base == otherMask.base); |
| } |
| |
| int get hashCode { |
| return (base == null ? 0 : base.hashCode) + 31 * flags.hashCode; |
| } |
| |
| String toString() { |
| if (isEmpty) return isNullable ? '[null]' : '[empty]'; |
| StringBuffer buffer = new StringBuffer(); |
| if (isNullable) buffer.write('null|'); |
| if (isExact) buffer.write('exact='); |
| if (isSubclass) buffer.write('subclass='); |
| if (isSubtype) buffer.write('subtype='); |
| buffer.write(base.name); |
| return "[$buffer]"; |
| } |
| |
| static Set<ClassElement> commonContainedClasses(FlatTypeMask x, |
| FlatTypeMask y, |
| ClassWorld classWorld) { |
| Iterable<ClassElement> xSubset = containedSubset(x, classWorld); |
| if (xSubset == null) return null; |
| Iterable<ClassElement> ySubset = containedSubset(y, classWorld); |
| if (ySubset == null) return null; |
| Iterable<ClassElement> smallSet, largeSet; |
| if (xSubset.length <= ySubset.length) { |
| smallSet = xSubset; |
| largeSet = ySubset; |
| } else { |
| smallSet = ySubset; |
| largeSet = xSubset; |
| } |
| var result = smallSet.where((ClassElement each) => largeSet.contains(each)); |
| return result.toSet(); |
| } |
| |
| static Iterable<ClassElement> containedSubset(FlatTypeMask x, |
| ClassWorld classWorld) { |
| ClassElement element = x.base; |
| if (x.isExact) { |
| return null; |
| } else if (x.isSubclass) { |
| return classWorld.subclassesOf(element); |
| } else { |
| assert(x.isSubtype); |
| return classWorld.subtypesOf(element); |
| } |
| } |
| } |