blob: 5d4720d0f064123a5cd4290f036e5cfba82061a1 [file] [log] [blame]
// 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]";
}
}