blob: 0aa5b30b94f88475bd695ecc64cc9b5a7fa4193f [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 masks;
enum _FlatTypeMaskKind { empty, exact, subclass, subtype }
/// A flat type mask is a type mask that has been flattened to contain a
/// base type.
class FlatTypeMask extends TypeMask {
/// Tag used for identifying serialized [FlatTypeMask] objects in a
/// debugging data stream.
static const String tag = 'flat-type-mask';
static const int _NULL_INDEX = 0;
static const int _LATE_SENTINEL_INDEX = 1;
static const int _USED_INDICES = 2;
static const int _NONE_MASK = 0;
static const int _NULL_MASK = 1 << _NULL_INDEX;
static const int _LATE_SENTINEL_MASK = 1 << _LATE_SENTINEL_INDEX;
static const int _ALL_MASK = (1 << _USED_INDICES) - 1;
final ClassEntity base;
final int flags;
static int _computeFlags(_FlatTypeMaskKind kind,
{bool isNullable: false, bool hasLateSentinel: false}) {
int mask = _NONE_MASK;
if (isNullable) mask |= _NULL_MASK;
if (hasLateSentinel) mask |= _LATE_SENTINEL_MASK;
return _computeFlagsRaw(kind.index, mask);
}
static int _computeFlagsRaw(int kind, int mask) =>
kind << _USED_INDICES | mask;
static _FlatTypeMaskKind _lookupKind(int flags) =>
_FlatTypeMaskKind.values[flags >> _USED_INDICES];
static bool _hasNullableFlag(int flags) => flags & _NULL_MASK != _NONE_MASK;
static bool _hasLateSentinelFlag(int flags) =>
flags & _LATE_SENTINEL_MASK != _NONE_MASK;
factory FlatTypeMask.exact(ClassEntity base, JClosedWorld world,
{bool hasLateSentinel: false}) =>
FlatTypeMask._canonicalize(base, _FlatTypeMaskKind.exact, world,
isNullable: true, hasLateSentinel: hasLateSentinel);
factory FlatTypeMask.subclass(ClassEntity base, JClosedWorld world,
{bool hasLateSentinel: false}) =>
FlatTypeMask._canonicalize(base, _FlatTypeMaskKind.subclass, world,
isNullable: true, hasLateSentinel: hasLateSentinel);
factory FlatTypeMask.subtype(ClassEntity base, JClosedWorld world,
{bool hasLateSentinel: false}) =>
FlatTypeMask._canonicalize(base, _FlatTypeMaskKind.subtype, world,
isNullable: true, hasLateSentinel: hasLateSentinel);
factory FlatTypeMask.nonNullEmpty({bool hasLateSentinel: false}) =>
hasLateSentinel
? const FlatTypeMask._(null, _LATE_SENTINEL_MASK)
: const FlatTypeMask._(null, _NONE_MASK);
factory FlatTypeMask.empty({bool hasLateSentinel: false}) => hasLateSentinel
? const FlatTypeMask._(null, _NULL_MASK | _LATE_SENTINEL_MASK)
: const FlatTypeMask._(null, _NULL_MASK);
factory FlatTypeMask.nonNullExact(ClassEntity base, JClosedWorld world,
{bool hasLateSentinel: false}) =>
FlatTypeMask._canonicalize(base, _FlatTypeMaskKind.exact, world,
hasLateSentinel: hasLateSentinel);
factory FlatTypeMask.nonNullSubclass(ClassEntity base, JClosedWorld world,
{bool hasLateSentinel: false}) =>
FlatTypeMask._canonicalize(base, _FlatTypeMaskKind.subclass, world,
hasLateSentinel: hasLateSentinel);
factory FlatTypeMask.nonNullSubtype(ClassEntity base, JClosedWorld world,
{bool hasLateSentinel: false}) =>
FlatTypeMask._canonicalize(base, _FlatTypeMaskKind.subtype, world,
hasLateSentinel: hasLateSentinel);
factory FlatTypeMask._canonicalize(
ClassEntity base, _FlatTypeMaskKind kind, JClosedWorld world,
{bool isNullable: false, bool hasLateSentinel: false}) {
if (base == world.commonElements.nullClass) {
return FlatTypeMask.empty(hasLateSentinel: hasLateSentinel);
}
return FlatTypeMask._(
base,
_computeFlags(kind,
isNullable: isNullable, hasLateSentinel: hasLateSentinel));
}
const FlatTypeMask._(this.base, this.flags);
/// Ensures that the generated mask is normalized, i.e., a call to
/// [TypeMask.assertIsNormalized] with the factory's result returns `true`.
factory FlatTypeMask.normalized(
ClassEntity base, int flags, CommonMasks domain) {
bool isNullable = _hasNullableFlag(flags);
bool hasLateSentinel = _hasLateSentinelFlag(flags);
if (base == domain.commonElements.nullClass) {
return FlatTypeMask.empty(hasLateSentinel: hasLateSentinel);
}
_FlatTypeMaskKind kind = _lookupKind(flags);
if (kind == _FlatTypeMaskKind.empty || kind == _FlatTypeMaskKind.exact) {
return FlatTypeMask._(base, flags);
}
if (kind == _FlatTypeMaskKind.subtype) {
if (!domain._closedWorld.classHierarchy.hasAnyStrictSubtype(base) ||
domain._closedWorld.classHierarchy.hasOnlySubclasses(base)) {
flags = _computeFlags(_FlatTypeMaskKind.subclass,
isNullable: isNullable, hasLateSentinel: hasLateSentinel);
}
}
if (kind == _FlatTypeMaskKind.subclass &&
!domain._closedWorld.classHierarchy.hasAnyStrictSubclass(base)) {
flags = _computeFlags(_FlatTypeMaskKind.exact,
isNullable: isNullable, hasLateSentinel: hasLateSentinel);
}
return domain.getCachedMask(base, flags, () => FlatTypeMask._(base, flags));
}
/// Deserializes a [FlatTypeMask] object from [source].
factory FlatTypeMask.readFromDataSource(
DataSource source, CommonMasks domain) {
source.begin(tag);
ClassEntity base = source.readClassOrNull();
int flags = source.readInt();
source.end(tag);
return domain.getCachedMask(base, flags, () => FlatTypeMask._(base, flags));
}
/// Serializes this [FlatTypeMask] to [sink].
@override
void writeToDataSink(DataSink sink) {
sink.writeEnum(TypeMaskKind.flat);
sink.begin(tag);
sink.writeClassOrNull(base);
sink.writeInt(flags);
sink.end(tag);
}
_FlatTypeMaskKind get _kind => _lookupKind(flags);
int get _mask => flags & _ALL_MASK;
ClassQuery get _classQuery => isExact
? ClassQuery.EXACT
: (isSubclass ? ClassQuery.SUBCLASS : ClassQuery.SUBTYPE);
@override
bool get isEmpty => isEmptyOrFlagged && _mask == _NONE_MASK;
@override
bool get isNull => isEmptyOrFlagged && _mask == _NULL_MASK;
@override
bool get isEmptyOrFlagged => _kind == _FlatTypeMaskKind.empty;
@override
bool get isExact => _kind == _FlatTypeMaskKind.exact;
@override
bool get isNullable => _hasNullableFlag(flags);
@override
bool get hasLateSentinel => _hasLateSentinelFlag(flags);
@override
AbstractBool get isLateSentinel {
if (!hasLateSentinel) return AbstractBool.False;
if (isEmptyOrFlagged && _mask == _LATE_SENTINEL_MASK) {
return AbstractBool.True;
}
return AbstractBool.Maybe;
}
@override
bool get isUnion => false;
@override
bool get isContainer => false;
@override
bool get isSet => false;
@override
bool get isMap => false;
@override
bool get isDictionary => false;
@override
bool get isForwarding => false;
@override
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 => _kind == _FlatTypeMaskKind.subclass;
bool get isSubtype => _kind == _FlatTypeMaskKind.subtype;
@override
FlatTypeMask withFlags({bool isNullable, bool hasLateSentinel}) {
int newFlags = _computeFlags(_kind,
isNullable: isNullable ?? this.isNullable,
hasLateSentinel: hasLateSentinel ?? this.hasLateSentinel);
if (newFlags == flags) return this;
return FlatTypeMask._(base, newFlags);
}
@override
bool contains(ClassEntity other, JClosedWorld closedWorld) {
if (isEmptyOrFlagged) {
return false;
} else if (identical(base, other)) {
return true;
} else if (isExact) {
return false;
} else if (isSubclass) {
return closedWorld.classHierarchy.isSubclassOf(other, base);
} else {
assert(isSubtype);
return closedWorld.classHierarchy.isSubtypeOf(other, base);
}
}
bool _isSingleImplementationOf(ClassEntity cls, JClosedWorld closedWorld) {
// 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.
CommonElements commonElements = closedWorld.commonElements;
if (containsOnlyString(closedWorld)) {
return cls == closedWorld.commonElements.stringClass ||
cls == commonElements.jsStringClass;
}
if (containsOnlyBool(closedWorld)) {
return cls == closedWorld.commonElements.boolClass ||
cls == commonElements.jsBoolClass;
}
if (containsOnlyInt(closedWorld)) {
return cls == closedWorld.commonElements.intClass ||
cls == commonElements.jsIntClass ||
cls == commonElements.jsPositiveIntClass ||
cls == commonElements.jsUInt32Class ||
cls == commonElements.jsUInt31Class;
}
return false;
}
@override
bool isInMask(TypeMask other, JClosedWorld closedWorld) {
// Quick check whether to handle null.
if (isNullable && !other.isNullable) return false;
if (hasLateSentinel && !other.hasLateSentinel) {
return false;
}
// The empty type contains no classes.
if (isEmptyOrFlagged) return true;
if (other.isEmptyOrFlagged) return false;
other = TypeMask.nonForwardingMask(other);
// If other is union, delegate to UnionTypeMask.containsMask.
if (other is! FlatTypeMask) return other.containsMask(this, closedWorld);
// The other must be flat, so compare base and flags.
FlatTypeMask flatOther = other;
ClassEntity 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, closedWorld);
}
// 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 == closedWorld.commonElements.objectClass);
return closedWorld.classHierarchy.isSubclassOf(base, otherBase);
}
assert(flatOther.isSubtype);
// Check whether this TypeMask satisfies otherBase's interface.
return satisfies(otherBase, closedWorld);
}
@override
bool containsMask(TypeMask other, JClosedWorld closedWorld) {
return other.isInMask(this, closedWorld);
}
@override
bool containsOnlyInt(JClosedWorld closedWorld) {
CommonElements commonElements = closedWorld.commonElements;
return base == commonElements.intClass ||
base == commonElements.jsIntClass ||
base == commonElements.jsPositiveIntClass ||
base == commonElements.jsUInt31Class ||
base == commonElements.jsUInt32Class;
}
@override
bool containsOnlyNum(JClosedWorld closedWorld) {
return containsOnlyInt(closedWorld) ||
base == closedWorld.commonElements.doubleClass ||
base == closedWorld.commonElements.jsNumNotIntClass ||
base == closedWorld.commonElements.numClass ||
base == closedWorld.commonElements.jsNumberClass;
}
@override
bool containsOnlyBool(JClosedWorld closedWorld) {
return base == closedWorld.commonElements.boolClass ||
base == closedWorld.commonElements.jsBoolClass;
}
@override
bool containsOnlyString(JClosedWorld closedWorld) {
return base == closedWorld.commonElements.stringClass ||
base == closedWorld.commonElements.jsStringClass;
}
@override
bool containsOnly(ClassEntity cls) {
return base == cls;
}
@override
bool satisfies(ClassEntity cls, JClosedWorld closedWorld) {
if (isEmptyOrFlagged) return false;
if (closedWorld.classHierarchy.isSubtypeOf(base, cls)) return true;
return false;
}
@override
ClassEntity singleClass(JClosedWorld closedWorld) {
if (isEmptyOrFlagged) return null;
if (isNullable) return null; // It is Null and some other class.
if (hasLateSentinel) return null;
if (isExact) {
return base;
} else if (isSubclass) {
return closedWorld.classHierarchy.hasAnyStrictSubclass(base)
? null
: base;
} else {
assert(isSubtype);
return null;
}
}
@override
bool containsAll(JClosedWorld closedWorld) {
if (isEmptyOrFlagged || isExact) return false;
return identical(base, closedWorld.commonElements.objectClass);
}
@override
TypeMask union(TypeMask other, CommonMasks domain) {
JClosedWorld closedWorld = domain._closedWorld;
assert(other != null);
assert(TypeMask.assertIsNormalized(this, closedWorld));
assert(TypeMask.assertIsNormalized(other, closedWorld));
if (other is! FlatTypeMask) return other.union(this, domain);
FlatTypeMask flatOther = other;
bool isNullable = this.isNullable || flatOther.isNullable;
bool hasLateSentinel = this.hasLateSentinel || flatOther.hasLateSentinel;
if (isEmptyOrFlagged) {
return flatOther.withFlags(
isNullable: isNullable, hasLateSentinel: hasLateSentinel);
} else if (flatOther.isEmptyOrFlagged) {
return withFlags(
isNullable: isNullable, hasLateSentinel: hasLateSentinel);
} else if (base == flatOther.base) {
return unionSame(flatOther, domain);
} else if (closedWorld.classHierarchy.isSubclassOf(flatOther.base, base)) {
return unionStrictSubclass(flatOther, domain);
} else if (closedWorld.classHierarchy.isSubclassOf(base, flatOther.base)) {
return flatOther.unionStrictSubclass(this, domain);
} else if (closedWorld.classHierarchy.isSubtypeOf(flatOther.base, base)) {
return unionStrictSubtype(flatOther, domain);
} else if (closedWorld.classHierarchy.isSubtypeOf(base, flatOther.base)) {
return flatOther.unionStrictSubtype(this, domain);
} else {
return UnionTypeMask._internal(
<FlatTypeMask>[withoutFlags(), flatOther.withoutFlags()],
isNullable: isNullable, hasLateSentinel: hasLateSentinel);
}
}
TypeMask unionSame(FlatTypeMask other, CommonMasks domain) {
assert(base == other.base);
assert(TypeMask.assertIsNormalized(this, domain._closedWorld));
assert(TypeMask.assertIsNormalized(other, domain._closedWorld));
// 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._mask : other.flags | _mask;
if (flags == combined) {
return this;
} else if (other.flags == combined) {
return other;
} else {
return FlatTypeMask.normalized(base, combined, domain);
}
}
TypeMask unionStrictSubclass(FlatTypeMask other, CommonMasks domain) {
assert(base != other.base);
assert(domain._closedWorld.classHierarchy.isSubclassOf(other.base, base));
assert(TypeMask.assertIsNormalized(this, domain._closedWorld));
assert(TypeMask.assertIsNormalized(other, domain._closedWorld));
int combined;
if ((isExact && other.isExact) ||
base == domain.commonElements.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 = _computeFlagsRaw(
_FlatTypeMaskKind.subclass.index, _mask | other._mask);
} 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._mask : other.flags | _mask;
}
// If we weaken the constraint on this type, we have to make sure that
// the result is normalized.
return flags != combined
? FlatTypeMask.normalized(base, combined, domain)
: this;
}
TypeMask unionStrictSubtype(FlatTypeMask other, CommonMasks domain) {
assert(base != other.base);
assert(!domain._closedWorld.classHierarchy.isSubclassOf(other.base, base));
assert(domain._closedWorld.classHierarchy.isSubtypeOf(other.base, base));
assert(TypeMask.assertIsNormalized(this, domain._closedWorld));
assert(TypeMask.assertIsNormalized(other, domain._closedWorld));
// 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 =
_computeFlagsRaw(_FlatTypeMaskKind.subtype.index, _mask | other._mask);
// We know there is at least one subtype, [other.base], so no need
// to normalize.
return flags != combined
? FlatTypeMask.normalized(base, combined, domain)
: this;
}
@override
TypeMask intersection(TypeMask other, CommonMasks domain) {
assert(other != null);
if (other is! FlatTypeMask) return other.intersection(this, domain);
assert(TypeMask.assertIsNormalized(this, domain._closedWorld));
assert(TypeMask.assertIsNormalized(other, domain._closedWorld));
FlatTypeMask flatOther = other;
ClassEntity otherBase = flatOther.base;
bool includeNull = isNullable && flatOther.isNullable;
bool includeLateSentinel = hasLateSentinel && flatOther.hasLateSentinel;
if (isEmptyOrFlagged) {
return withFlags(
isNullable: includeNull, hasLateSentinel: includeLateSentinel);
} else if (flatOther.isEmptyOrFlagged) {
return other.withFlags(
isNullable: includeNull, hasLateSentinel: includeLateSentinel);
}
SubclassResult result = domain._closedWorld.classHierarchy
.commonSubclasses(base, _classQuery, otherBase, flatOther._classQuery);
switch (result.kind) {
case SubclassResultKind.EMPTY:
return includeNull
? TypeMask.empty(hasLateSentinel: includeLateSentinel)
: TypeMask.nonNullEmpty(hasLateSentinel: includeLateSentinel);
case SubclassResultKind.EXACT1:
assert(isExact);
return withFlags(
isNullable: includeNull, hasLateSentinel: includeLateSentinel);
case SubclassResultKind.EXACT2:
assert(other.isExact);
return other.withFlags(
isNullable: includeNull, hasLateSentinel: includeLateSentinel);
case SubclassResultKind.SUBCLASS1:
assert(isSubclass);
return withFlags(
isNullable: includeNull, hasLateSentinel: includeLateSentinel);
case SubclassResultKind.SUBCLASS2:
assert(flatOther.isSubclass);
return other.withFlags(
isNullable: includeNull, hasLateSentinel: includeLateSentinel);
case SubclassResultKind.SUBTYPE1:
assert(isSubtype);
return withFlags(
isNullable: includeNull, hasLateSentinel: includeLateSentinel);
case SubclassResultKind.SUBTYPE2:
assert(flatOther.isSubtype);
return other.withFlags(
isNullable: includeNull, hasLateSentinel: includeLateSentinel);
case SubclassResultKind.SET:
default:
if (result.classes.isEmpty) {
return includeNull
? TypeMask.empty(hasLateSentinel: includeLateSentinel)
: TypeMask.nonNullEmpty(hasLateSentinel: includeLateSentinel);
} else if (result.classes.length == 1) {
ClassEntity cls = result.classes.first;
return includeNull
? TypeMask.subclass(cls, domain._closedWorld,
hasLateSentinel: includeLateSentinel)
: TypeMask.nonNullSubclass(cls, domain._closedWorld,
hasLateSentinel: includeLateSentinel);
}
List<FlatTypeMask> masks = List.from(result.classes.map(
(ClassEntity cls) =>
TypeMask.nonNullSubclass(cls, domain._closedWorld)));
if (masks.length > UnionTypeMask.MAX_UNION_LENGTH) {
return UnionTypeMask.flatten(masks, domain,
includeNull: includeNull,
includeLateSentinel: includeLateSentinel);
}
return UnionTypeMask._internal(masks,
isNullable: includeNull, hasLateSentinel: includeLateSentinel);
}
}
@override
bool isDisjoint(TypeMask other, JClosedWorld closedWorld) {
if (other is! FlatTypeMask) return other.isDisjoint(this, closedWorld);
FlatTypeMask flatOther = other;
if (isNullable && flatOther.isNullable) return false;
if (hasLateSentinel && flatOther.hasLateSentinel) return false;
if (isEmptyOrFlagged || flatOther.isEmptyOrFlagged) return true;
if (base == flatOther.base) return false;
if (isExact && flatOther.isExact) return true;
if (isExact) return !flatOther.contains(base, closedWorld);
if (flatOther.isExact) return !contains(flatOther.base, closedWorld);
// Normalization guarantees that isExact === !isSubclass && !isSubtype.
// Both are subclass or subtype masks, so if there is a subclass
// relationship, they are not disjoint.
if (closedWorld.classHierarchy.isSubclassOf(flatOther.base, base)) {
return false;
}
if (closedWorld.classHierarchy.isSubclassOf(base, flatOther.base)) {
return false;
}
// Two different base classes have no common subclass unless one is a
// subclass of the other (checked above).
if (isSubclass && flatOther.isSubclass) return true;
return _isDisjointHelper(this, flatOther, closedWorld);
}
static bool _isDisjointHelper(
FlatTypeMask a, FlatTypeMask b, JClosedWorld closedWorld) {
if (!a.isSubclass && b.isSubclass) {
return _isDisjointHelper(b, a, closedWorld);
}
assert(a.isSubclass || a.isSubtype);
assert(b.isSubtype);
var elements = a.isSubclass
? closedWorld.classHierarchy.strictSubclassesOf(a.base)
: closedWorld.classHierarchy.strictSubtypesOf(a.base);
for (var element in elements) {
if (closedWorld.classHierarchy.isSubtypeOf(element, b.base)) return false;
}
return true;
}
TypeMask intersectionSame(FlatTypeMask other, CommonMasks domain) {
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._mask | ~_ALL_MASK)
: other.flags & (_mask | ~_ALL_MASK);
if (flags == combined) {
return this;
} else if (other.flags == combined) {
return other;
} else {
return FlatTypeMask.normalized(base, combined, domain);
}
}
TypeMask intersectionStrictSubclass(FlatTypeMask other, CommonMasks domain) {
assert(base != other.base);
assert(domain._closedWorld.classHierarchy.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 & (_mask | ~_ALL_MASK);
if (other.flags == combined) {
return other;
} else {
return FlatTypeMask.normalized(other.base, combined, domain);
}
}
TypeMask intersectionEmpty(FlatTypeMask other) {
bool isNullable = this.isNullable && other.isNullable;
bool hasLateSentinel = this.hasLateSentinel && other.hasLateSentinel;
return isNullable
? TypeMask.empty(hasLateSentinel: hasLateSentinel)
: TypeMask.nonNullEmpty(hasLateSentinel: hasLateSentinel);
}
@override
bool canHit(MemberEntity element, Name name, JClosedWorld closedWorld) {
CommonElements commonElements = closedWorld.commonElements;
assert(element.name == name.text);
if (isEmptyOrFlagged) {
return isNullable &&
closedWorld.hasElementIn(commonElements.jsNullClass, name, element);
}
ClassEntity other = element.enclosingClass;
if (other == commonElements.jsNullClass) {
return isNullable;
} else if (isExact) {
return closedWorld.hasElementIn(base, name, element);
} else if (isSubclass) {
return closedWorld.hasElementIn(base, name, element) ||
closedWorld.classHierarchy.isSubclassOf(other, base) ||
closedWorld.hasAnySubclassThatMixes(base, other);
} else {
assert(isSubtype);
bool result = closedWorld.hasElementIn(base, name, element) ||
closedWorld.classHierarchy.isSubtypeOf(other, base) ||
closedWorld.hasAnySubclassThatImplements(other, base) ||
closedWorld.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<ClassEntity> mixinUses = closedWorld.mixinUsesOf(base);
return mixinUses.any((mixinApplication) =>
closedWorld.hasElementIn(mixinApplication, name, element) ||
closedWorld.classHierarchy.isSubclassOf(other, mixinApplication) ||
closedWorld.hasAnySubclassThatMixes(mixinApplication, other));
}
}
@override
bool needsNoSuchMethodHandling(
Selector selector, covariant JClosedWorld closedWorld) {
// A call on an empty type mask is either dead code, or a call on
// `null`.
if (isEmptyOrFlagged) return false;
// A call on an exact mask for an abstract class is dead code.
// TODO(johnniwinther): A type mask cannot be abstract. Remove the need
// for this noise (currently used for super-calls in inference and mirror
// usage).
if (isExact && base.isAbstract) return false;
return closedWorld.needsNoSuchMethod(base, selector, _classQuery);
}
@override
MemberEntity locateSingleMember(Selector selector, CommonMasks domain) {
if (isEmptyOrFlagged) return null;
JClosedWorld closedWorld = domain._closedWorld;
if (closedWorld.includesClosureCallInDomain(selector, this, domain))
return null;
Iterable<MemberEntity> targets =
closedWorld.locateMembersInDomain(selector, this, domain);
if (targets.length != 1) return null;
MemberEntity result = targets.first;
ClassEntity enclosing = result.enclosingClass;
// We only return the found element if it is guaranteed to be implemented on
// all classes in the receiver type [this]. It could be found only in a
// subclass or in an inheritance-wise unrelated class in case of subtype
// selectors.
if (isSubtype) {
// if (closedWorld.isUsedAsMixin(enclosing)) {
if (closedWorld.everySubtypeIsSubclassOfOrMixinUseOf(base, enclosing)) {
return result;
}
//}
return null;
} else {
if (closedWorld.classHierarchy.isSubclassOf(base, enclosing)) {
return result;
}
if (closedWorld.isSubclassOfMixinUseOf(base, enclosing)) return result;
}
return null;
}
@override
bool operator ==(var other) {
if (identical(this, other)) return true;
if (other is! FlatTypeMask) return false;
FlatTypeMask otherMask = other;
return (flags == otherMask.flags) && (base == otherMask.base);
}
@override
int get hashCode {
return (base == null ? 0 : base.hashCode) + 31 * flags.hashCode;
}
@override
String toString() {
StringBuffer buffer = StringBuffer('[');
buffer.writeAll([
if (isEmpty) 'empty',
if (isNullable) 'null',
if (hasLateSentinel) 'sentinel',
if (isExact) 'exact=${base.name}',
if (isSubclass) 'subclass=${base.name}',
if (isSubtype) 'subtype=${base.name}',
], '|');
buffer.write(']');
return buffer.toString();
}
}