| // 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 type mask represents a set of concrete types, but the operations |
| * on it are not guaranteed to be precise. When computing the union of |
| * two masks you may get a mask that is too wide (like a common |
| * superclass instead of a proper union type) and when computing the |
| * intersection of two masks you may get a mask that is too narrow. |
| */ |
| class TypeMask { |
| |
| static const int EXACT = 0; |
| static const int SUBCLASS = 1; |
| static const int SUBTYPE = 2; |
| |
| final DartType base; |
| final int flags; |
| |
| TypeMask(DartType base, int kind, bool isNullable) |
| : this.internal(base, (kind << 1) | (isNullable ? 1 : 0)); |
| |
| const TypeMask.exact(DartType base) |
| : this.internal(base, (EXACT << 1) | 1); |
| const TypeMask.subclass(DartType base) |
| : this.internal(base, (SUBCLASS << 1) | 1); |
| const TypeMask.subtype(DartType base) |
| : this.internal(base, (SUBTYPE << 1) | 1); |
| |
| const TypeMask.nonNullExact(DartType base) |
| : this.internal(base, EXACT << 1); |
| const TypeMask.nonNullSubclass(DartType base) |
| : this.internal(base, SUBCLASS << 1); |
| const TypeMask.nonNullSubtype(DartType base) |
| : this.internal(base, SUBTYPE << 1); |
| |
| const TypeMask.internal(this.base, this.flags); |
| |
| bool get isNullable => (flags & 1) != 0; |
| bool get isExact => (flags >> 1) == EXACT; |
| |
| // 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; |
| |
| /** |
| * Returns a nullable variant of [this] type mask. |
| */ |
| TypeMask nullable() { |
| return isNullable ? this : new TypeMask.internal(base, flags | 1); |
| } |
| |
| /** |
| * Returns whether or not this type mask contains the given type. |
| */ |
| bool contains(DartType type, Compiler compiler) { |
| // TODO(kasperl): Do this error handling earlier. |
| if (base.kind != TypeKind.INTERFACE) return false; |
| assert(type.kind == TypeKind.INTERFACE); |
| // Compare the interface types. |
| ClassElement baseElement = base.element; |
| ClassElement typeElement = type.element; |
| if (isExact) { |
| return identical(baseElement, typeElement); |
| } else if (isSubclass) { |
| return typeElement.isSubclassOf(baseElement); |
| } else { |
| assert(isSubtype); |
| Set<ClassElement> subtypes = compiler.world.subtypes[baseElement]; |
| return subtypes != null ? subtypes.contains(typeElement) : false; |
| } |
| } |
| |
| // TODO(kasperl): Try to get rid of this method. It shouldn't really |
| // be necessary. |
| bool containsAll(Compiler compiler) { |
| // TODO(kasperl): Do this error handling earlier. |
| if (base.kind != TypeKind.INTERFACE) return false; |
| // TODO(kasperl): Should we take nullability into account here? |
| if (isExact) return false; |
| ClassElement baseElement = base.element; |
| return identical(baseElement, compiler.objectClass) |
| || identical(baseElement, compiler.dynamicClass); |
| } |
| |
| // TODO(kasperl): This implementation is a bit sketchy, but it |
| // behaves the same as the old implementation on HType. The plan is |
| // to extend this and add proper testing of it. |
| TypeMask union(TypeMask other, Types types) { |
| // TODO(kasperl): Add subclass handling. |
| if (base == other.base) { |
| return unionSame(other, types); |
| } else if (types.isSubtype(other.base, base)) { |
| return unionSubtype(other, types); |
| } else if (types.isSubtype(base, other.base)) { |
| return other.unionSubtype(this, types); |
| } |
| return null; |
| } |
| |
| TypeMask unionSame(TypeMask other, Types types) { |
| assert(base == other.base); |
| // 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. |
| 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 TypeMask.internal(base, combined); |
| } |
| } |
| |
| TypeMask unionSubtype(TypeMask other, Types types) { |
| assert(types.isSubtype(other.base, base)); |
| // 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); |
| return (flags != combined) |
| ? new TypeMask.internal(base, combined) |
| : this; |
| } |
| |
| // TODO(kasperl): This implementation is a bit sketchy, but it |
| // behaves the same as the old implementation on HType. The plan is |
| // to extend this and add proper testing of it. |
| TypeMask intersection(TypeMask other, Types types) { |
| int combined = (flags < other.flags) |
| ? flags & ((other.flags & 1) | ~1) |
| : other.flags & ((flags & 1) | ~1); |
| if (base == other.base) { |
| if (flags == combined) { |
| return this; |
| } else if (other.flags == combined) { |
| return other; |
| } else { |
| return new TypeMask.internal(base, combined); |
| } |
| } else if (types.isSubtype(other.base, base)) { |
| if (other.flags == combined) { |
| return other; |
| } else { |
| return new TypeMask.internal(other.base, combined); |
| } |
| } else if (types.isSubtype(base, other.base)) { |
| if (flags == combined) { |
| return this; |
| } else { |
| return new TypeMask.internal(base, combined); |
| } |
| } |
| return null; |
| } |
| |
| bool operator ==(var other) { |
| if (other is !TypeMask) return false; |
| TypeMask otherMask = other; |
| return (base == otherMask.base) && (flags == otherMask.flags); |
| } |
| |
| String toString() { |
| 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.element.name.slowToString()); |
| return "[$buffer]"; |
| } |
| } |