| Index: pkg/compiler/lib/src/types/flat_type_mask.dart
|
| diff --git a/pkg/compiler/lib/src/types/flat_type_mask.dart b/pkg/compiler/lib/src/types/flat_type_mask.dart
|
| deleted file mode 100644
|
| index 91023a36232feda70fc77e9a31adbfa3e8536c5d..0000000000000000000000000000000000000000
|
| --- a/pkg/compiler/lib/src/types/flat_type_mask.dart
|
| +++ /dev/null
|
| @@ -1,702 +0,0 @@
|
| -// 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);
|
| - }
|
| - }
|
| -}
|
|
|