[dart2js] Refactor typemasks.

Change-Id: I13886baba98f70614cd05e6ee5cb38421264b537
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/210645
Reviewed-by: Stephen Adams <sra@google.com>
Reviewed-by: Joshua Litt <joshualitt@google.com>
This commit is contained in:
Mayank Patke 2021-09-13 21:59:20 +00:00 committed by commit-bot@chromium.org
parent 4744f66f6f
commit 39fd70e816
22 changed files with 560 additions and 732 deletions

View file

@ -299,10 +299,6 @@ abstract class AbstractValueDomain {
/// exact class at runtime.
AbstractBool isExact(covariant AbstractValue value);
/// Returns an [AbstractBool] that describes whether [value] is an exact class
/// or `null` at runtime.
AbstractBool isExactOrNull(covariant AbstractValue value);
/// Returns the [ClassEntity] if this [value] is a non-null instance of an
/// exact class at runtime, and `null` otherwise.
ClassEntity getExactClass(covariant AbstractValue value);
@ -323,10 +319,6 @@ abstract class AbstractValueDomain {
/// bool at runtime.
AbstractBool isPrimitiveBoolean(covariant AbstractValue value);
/// Returns an [AbstractBool] that describes whether [value] is a JavaScript
/// array at runtime.
AbstractBool isPrimitiveArray(covariant AbstractValue value);
/// Returns an [AbstractBool] that describes whether [value] is a JavaScript
/// string, array, native HTML list or `null` at runtime.
AbstractBool isIndexablePrimitive(covariant AbstractValue value);

View file

@ -281,8 +281,6 @@ class PowersetBitsDomain {
AbstractBool isIndexablePrimitive(int value) => isOther(value);
AbstractBool isPrimitiveArray(int value) => isOther(value);
AbstractBool isPrimitiveBoolean(int value) {
if (isDefinitelyTrue(value) || isDefinitelyFalse(value)) {
return AbstractBool.True;
@ -300,8 +298,6 @@ class PowersetBitsDomain {
? AbstractBool.True
: (isPotentiallyNull(value) ? AbstractBool.Maybe : AbstractBool.False);
AbstractBool isExactOrNull(int value) => AbstractBool.Maybe;
AbstractBool isExact(int value) => AbstractBool.Maybe;
AbstractBool isEmpty(int value) {

View file

@ -527,12 +527,6 @@ class PowersetDomain implements AbstractValueDomain {
_powersetBitsDomain.isIndexablePrimitive(value._powersetBits),
_abstractValueDomain.isIndexablePrimitive(value._abstractValue));
@override
AbstractBool isPrimitiveArray(covariant PowersetValue value) =>
AbstractBool.strengthen(
_powersetBitsDomain.isPrimitiveArray(value._powersetBits),
_abstractValueDomain.isPrimitiveArray(value._abstractValue));
@override
AbstractBool isPrimitiveBoolean(covariant PowersetValue value) =>
AbstractBool.strengthen(
@ -560,12 +554,6 @@ class PowersetDomain implements AbstractValueDomain {
ClassEntity getExactClass(covariant PowersetValue value) =>
_abstractValueDomain.getExactClass(value._abstractValue);
@override
AbstractBool isExactOrNull(covariant PowersetValue value) =>
AbstractBool.strengthen(
_powersetBitsDomain.isExactOrNull(value._powersetBits),
_abstractValueDomain.isExactOrNull(value._abstractValue));
@override
AbstractBool isExact(covariant PowersetValue value) =>
AbstractBool.strengthen(_powersetBitsDomain.isExact(value._powersetBits),

View file

@ -382,10 +382,6 @@ class WrappedAbstractValueDomain implements AbstractValueDomain {
AbstractBool isIndexablePrimitive(covariant WrappedAbstractValue value) =>
_abstractValueDomain.isIndexablePrimitive(value._abstractValue);
@override
AbstractBool isPrimitiveArray(covariant WrappedAbstractValue value) =>
_abstractValueDomain.isPrimitiveArray(value._abstractValue);
@override
AbstractBool isPrimitiveBoolean(covariant WrappedAbstractValue value) =>
_abstractValueDomain.isPrimitiveBoolean(value._abstractValue);
@ -406,10 +402,6 @@ class WrappedAbstractValueDomain implements AbstractValueDomain {
ClassEntity getExactClass(covariant WrappedAbstractValue value) =>
_abstractValueDomain.getExactClass(value._abstractValue);
@override
AbstractBool isExactOrNull(covariant WrappedAbstractValue value) =>
_abstractValueDomain.isExactOrNull(value._abstractValue);
@override
AbstractBool isExact(covariant WrappedAbstractValue value) =>
_abstractValueDomain.isExact(value._abstractValue);

View file

@ -274,9 +274,6 @@ class TrivialAbstractValueDomain implements AbstractValueDomain {
@override
AbstractBool isIndexablePrimitive(AbstractValue value) => AbstractBool.Maybe;
@override
AbstractBool isPrimitiveArray(AbstractValue value) => AbstractBool.Maybe;
@override
AbstractBool isPrimitiveBoolean(AbstractValue value) => AbstractBool.Maybe;
@ -292,9 +289,6 @@ class TrivialAbstractValueDomain implements AbstractValueDomain {
@override
ClassEntity getExactClass(AbstractValue value) => null;
@override
AbstractBool isExactOrNull(AbstractValue value) => AbstractBool.Maybe;
@override
AbstractBool isExact(AbstractValue value) => AbstractBool.Maybe;

View file

@ -648,7 +648,7 @@ class TypeSystem {
// mapped iterable, we save the intermediate results to avoid computing them
// again.
var list = [];
bool isDynamicIngoringNull = false;
bool isDynamicIgnoringNull = false;
bool mayBeNull = false;
for (AbstractValue mask in masks) {
// Don't do any work on computing unions if we know that after all that
@ -656,12 +656,12 @@ class TypeSystem {
// TODO(sigmund): change to `mask == dynamicType` so we can continue to
// track the non-nullable bit.
if (_abstractValueDomain.containsAll(mask).isPotentiallyTrue) {
isDynamicIngoringNull = true;
isDynamicIgnoringNull = true;
}
if (_abstractValueDomain.isNull(mask).isPotentiallyTrue) {
mayBeNull = true;
}
if (isDynamicIngoringNull && mayBeNull) return dynamicType;
if (isDynamicIgnoringNull && mayBeNull) return dynamicType;
list.add(mask);
}
@ -671,12 +671,12 @@ class TypeSystem {
newType == null ? mask : _abstractValueDomain.union(newType, mask);
// Likewise - stop early if we already reach dynamic.
if (_abstractValueDomain.containsAll(newType).isPotentiallyTrue) {
isDynamicIngoringNull = true;
isDynamicIgnoringNull = true;
}
if (_abstractValueDomain.isNull(newType).isPotentiallyTrue) {
mayBeNull = true;
}
if (isDynamicIngoringNull && mayBeNull) return dynamicType;
if (isDynamicIgnoringNull && mayBeNull) return dynamicType;
}
return newType ?? _abstractValueDomain.emptyType;

View file

@ -27,20 +27,20 @@ class ContainerTypeMask extends AllocationTypeMask {
// The length of the container.
final int length;
ContainerTypeMask(this.forwardTo, this.allocationNode, this.allocationElement,
this.elementType, this.length);
const ContainerTypeMask(this.forwardTo, this.allocationNode,
this.allocationElement, this.elementType, this.length);
/// Deserializes a [ContainerTypeMask] object from [source].
factory ContainerTypeMask.readFromDataSource(
DataSource source, CommonMasks domain) {
source.begin(tag);
TypeMask forwardTo = new TypeMask.readFromDataSource(source, domain);
TypeMask forwardTo = TypeMask.readFromDataSource(source, domain);
ir.TreeNode allocationNode = source.readTreeNodeOrNull();
MemberEntity allocationElement = source.readMemberOrNull();
TypeMask elementType = new TypeMask.readFromDataSource(source, domain);
TypeMask elementType = TypeMask.readFromDataSource(source, domain);
int length = source.readIntOrNull();
source.end(tag);
return new ContainerTypeMask(
return ContainerTypeMask(
forwardTo, allocationNode, allocationElement, elementType, length);
}
@ -58,19 +58,13 @@ class ContainerTypeMask extends AllocationTypeMask {
}
@override
TypeMask nullable() {
return isNullable
? this
: new ContainerTypeMask(forwardTo.nullable(), allocationNode,
allocationElement, elementType, length);
}
@override
TypeMask nonNullable() {
return isNullable
? new ContainerTypeMask(forwardTo.nonNullable(), allocationNode,
allocationElement, elementType, length)
: this;
ContainerTypeMask withFlags({bool isNullable}) {
isNullable ??= this.isNullable;
if (isNullable == this.isNullable) {
return this;
}
return ContainerTypeMask(forwardTo.withFlags(isNullable: isNullable),
allocationNode, allocationElement, elementType, length);
}
@override
@ -79,36 +73,16 @@ class ContainerTypeMask extends AllocationTypeMask {
bool get isExact => true;
@override
bool equalsDisregardNull(other) {
if (other is! ContainerTypeMask) return false;
return super.equalsDisregardNull(other) &&
allocationNode == other.allocationNode &&
elementType == other.elementType &&
length == other.length;
}
@override
TypeMask intersection(TypeMask other, CommonMasks domain) {
TypeMask forwardIntersection = forwardTo.intersection(other, domain);
if (forwardIntersection.isEmptyOrNull) return forwardIntersection;
return forwardIntersection.isNullable ? nullable() : nonNullable();
}
@override
TypeMask union(dynamic other, CommonMasks domain) {
if (this == other) {
return this;
} else if (equalsDisregardNull(other)) {
return other.isNullable ? other : this;
} else if (other.isEmptyOrNull) {
return other.isNullable ? this.nullable() : this;
} else if (other.isContainer &&
TypeMask _unionSpecialCases(TypeMask other, CommonMasks domain,
{bool isNullable}) {
assert(isNullable != null);
if (other is ContainerTypeMask &&
elementType != null &&
other.elementType != null) {
TypeMask newElementType = elementType.union(other.elementType, domain);
int newLength = (length == other.length) ? length : null;
TypeMask newForwardTo = forwardTo.union(other.forwardTo, domain);
return new ContainerTypeMask(
return ContainerTypeMask(
newForwardTo,
allocationNode == other.allocationNode ? allocationNode : null,
allocationElement == other.allocationElement
@ -116,19 +90,22 @@ class ContainerTypeMask extends AllocationTypeMask {
: null,
newElementType,
newLength);
} else {
return forwardTo.union(other, domain);
}
return null;
}
@override
bool operator ==(other) => super == other;
bool operator ==(other) {
if (identical(this, other)) return true;
if (other is! ContainerTypeMask) return false;
return super == other &&
elementType == other.elementType &&
length == other.length;
}
@override
int get hashCode {
return computeHashCode(
allocationNode, isNullable, elementType, length, forwardTo);
}
int get hashCode => Hashing.objectHash(
length, Hashing.objectHash(elementType, super.hashCode));
@override
String toString() {

View file

@ -16,9 +16,9 @@ class DictionaryTypeMask extends MapTypeMask {
static const String tag = 'dictionary-type-mask';
// The underlying key/value map of this dictionary.
final Map<String, AbstractValue> _typeMap;
final Map<String, TypeMask> _typeMap;
DictionaryTypeMask(
const DictionaryTypeMask(
TypeMask forwardTo,
ir.Node allocationNode,
MemberEntity allocationElement,
@ -31,15 +31,15 @@ class DictionaryTypeMask extends MapTypeMask {
factory DictionaryTypeMask.readFromDataSource(
DataSource source, CommonMasks domain) {
source.begin(tag);
TypeMask forwardTo = new TypeMask.readFromDataSource(source, domain);
TypeMask forwardTo = TypeMask.readFromDataSource(source, domain);
ir.TreeNode allocationNode = source.readTreeNodeOrNull();
MemberEntity allocationElement = source.readMemberOrNull();
TypeMask keyType = new TypeMask.readFromDataSource(source, domain);
TypeMask valueType = new TypeMask.readFromDataSource(source, domain);
Map<String, AbstractValue> typeMap = source
.readStringMap(() => new TypeMask.readFromDataSource(source, domain));
TypeMask keyType = TypeMask.readFromDataSource(source, domain);
TypeMask valueType = TypeMask.readFromDataSource(source, domain);
Map<String, TypeMask> typeMap =
source.readStringMap(() => TypeMask.readFromDataSource(source, domain));
source.end(tag);
return new DictionaryTypeMask(forwardTo, allocationNode, allocationElement,
return DictionaryTypeMask(forwardTo, allocationNode, allocationElement,
keyType, valueType, typeMap);
}
@ -53,27 +53,20 @@ class DictionaryTypeMask extends MapTypeMask {
sink.writeMemberOrNull(allocationElement);
keyType.writeToDataSink(sink);
valueType.writeToDataSink(sink);
sink.writeStringMap(_typeMap, (AbstractValue value) {
TypeMask typeMask = value;
sink.writeStringMap(_typeMap, (TypeMask typeMask) {
typeMask.writeToDataSink(sink);
});
sink.end(tag);
}
@override
TypeMask nullable() {
return isNullable
? this
: new DictionaryTypeMask(forwardTo.nullable(), allocationNode,
allocationElement, keyType, valueType, _typeMap);
}
@override
TypeMask nonNullable() {
return isNullable
? new DictionaryTypeMask(forwardTo.nonNullable(), allocationNode,
allocationElement, keyType, valueType, _typeMap)
: this;
DictionaryTypeMask withFlags({bool isNullable}) {
isNullable ??= this.isNullable;
if (isNullable == this.isNullable) {
return this;
}
return DictionaryTypeMask(forwardTo.withFlags(isNullable: isNullable),
allocationNode, allocationElement, keyType, valueType, _typeMap);
}
@override
@ -86,37 +79,15 @@ class DictionaryTypeMask extends MapTypeMask {
TypeMask getValueForKey(String key) => _typeMap[key];
@override
bool equalsDisregardNull(other) {
if (other is! DictionaryTypeMask) return false;
return allocationNode == other.allocationNode &&
keyType == other.keyType &&
valueType == other.valueType &&
_typeMap.keys.every((k) => other._typeMap.containsKey(k)) &&
other._typeMap.keys.every(
(k) => _typeMap.containsKey(k) && _typeMap[k] == other._typeMap[k]);
}
@override
TypeMask intersection(TypeMask other, CommonMasks domain) {
TypeMask forwardIntersection = forwardTo.intersection(other, domain);
if (forwardIntersection.isEmptyOrNull) return forwardIntersection;
return forwardIntersection.isNullable ? nullable() : nonNullable();
}
@override
TypeMask union(dynamic other, CommonMasks domain) {
if (this == other) {
return this;
} else if (equalsDisregardNull(other)) {
return other.isNullable ? other : this;
} else if (other.isEmptyOrNull) {
return other.isNullable ? this.nullable() : this;
} else if (other.isDictionary) {
TypeMask _unionSpecialCases(TypeMask other, CommonMasks domain,
{bool isNullable}) {
assert(isNullable != null);
if (other is DictionaryTypeMask) {
TypeMask newForwardTo = forwardTo.union(other.forwardTo, domain);
TypeMask newKeyType = keyType.union(other.keyType, domain);
TypeMask newValueType = valueType.union(other.valueType, domain);
Map<String, TypeMask> mappings = <String, TypeMask>{};
_typeMap.forEach((k, dynamic v) {
Map<String, TypeMask> mappings = {};
_typeMap.forEach((k, v) {
if (!other._typeMap.containsKey(k)) {
mappings[k] = v.nullable();
}
@ -128,28 +99,32 @@ class DictionaryTypeMask extends MapTypeMask {
mappings[k] = v.nullable();
}
});
return new DictionaryTypeMask(
return DictionaryTypeMask(
newForwardTo, null, null, newKeyType, newValueType, mappings);
} else if (other.isMap &&
}
if (other is MapTypeMask &&
(other.keyType != null) &&
(other.valueType != null)) {
TypeMask newForwardTo = forwardTo.union(other.forwardTo, domain);
TypeMask newKeyType = keyType.union(other.keyType, domain);
TypeMask newValueType = valueType.union(other.valueType, domain);
return new MapTypeMask(
newForwardTo, null, null, newKeyType, newValueType);
} else {
return forwardTo.union(other, domain);
return MapTypeMask(newForwardTo, null, null, newKeyType, newValueType);
}
return null;
}
@override
bool operator ==(other) => super == other;
bool operator ==(other) {
if (identical(this, other)) return true;
if (other is! DictionaryTypeMask) return false;
return super == other &&
_typeMap.keys.every((k) => other._typeMap.containsKey(k)) &&
other._typeMap.keys.every(
(k) => _typeMap.containsKey(k) && _typeMap[k] == other._typeMap[k]);
}
@override
int get hashCode {
return computeHashCode(allocationNode, isNullable, _typeMap, forwardTo);
}
int get hashCode => Hashing.objectHash(_typeMap, super.hashCode);
@override
String toString() {

View file

@ -4,74 +4,94 @@
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 implements TypeMask {
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 EMPTY = 0;
static const int EXACT = 1;
static const int SUBCLASS = 2;
static const int SUBTYPE = 3;
static const int _NULL_INDEX = 0;
static const int _USED_INDICES = 1;
static const int _NONE_MASK = 0;
static const int _NULL_MASK = 1 << _NULL_INDEX;
static const int _ALL_MASK = (1 << _USED_INDICES) - 1;
final ClassEntity base;
final int flags;
factory FlatTypeMask.exact(ClassEntity base, JClosedWorld world) =>
FlatTypeMask._canonicalize(base, EXACT, true, world);
factory FlatTypeMask.subclass(ClassEntity base, JClosedWorld world) =>
FlatTypeMask._canonicalize(base, SUBCLASS, true, world);
factory FlatTypeMask.subtype(ClassEntity base, JClosedWorld world) =>
FlatTypeMask._canonicalize(base, SUBTYPE, true, world);
static int _computeFlags(_FlatTypeMaskKind kind, {bool hasNull: false}) {
int mask = _NONE_MASK;
if (hasNull) mask |= _NULL_MASK;
return _computeFlagsRaw(kind.index, mask);
}
const FlatTypeMask.nonNullEmpty()
: base = null,
flags = 0;
const FlatTypeMask.empty()
: base = null,
flags = 1;
static int _computeFlagsRaw(int kind, int mask) =>
kind << _USED_INDICES | mask;
static _FlatTypeMaskKind _lookupKind(int flags) =>
_FlatTypeMaskKind.values[flags >> _USED_INDICES];
static bool _hasNullFlag(int flags) => flags & _NULL_MASK != _NONE_MASK;
factory FlatTypeMask.exact(ClassEntity base, JClosedWorld world) =>
FlatTypeMask._canonicalize(base, _FlatTypeMaskKind.exact, world,
isNullable: true);
factory FlatTypeMask.subclass(ClassEntity base, JClosedWorld world) =>
FlatTypeMask._canonicalize(base, _FlatTypeMaskKind.subclass, world,
isNullable: true);
factory FlatTypeMask.subtype(ClassEntity base, JClosedWorld world) =>
FlatTypeMask._canonicalize(base, _FlatTypeMaskKind.subtype, world,
isNullable: true);
factory FlatTypeMask.nonNullEmpty() => const FlatTypeMask._(null, _NONE_MASK);
factory FlatTypeMask.empty() => const FlatTypeMask._(null, _NULL_MASK);
factory FlatTypeMask.nonNullExact(ClassEntity base, JClosedWorld world) =>
FlatTypeMask._canonicalize(base, EXACT, false, world);
FlatTypeMask._canonicalize(base, _FlatTypeMaskKind.exact, world);
factory FlatTypeMask.nonNullSubclass(ClassEntity base, JClosedWorld world) =>
FlatTypeMask._canonicalize(base, SUBCLASS, false, world);
FlatTypeMask._canonicalize(base, _FlatTypeMaskKind.subclass, world);
factory FlatTypeMask.nonNullSubtype(ClassEntity base, JClosedWorld world) =>
FlatTypeMask._canonicalize(base, SUBTYPE, false, world);
FlatTypeMask._canonicalize(base, _FlatTypeMaskKind.subtype, world);
factory FlatTypeMask._canonicalize(
ClassEntity base, int kind, bool isNullable, JClosedWorld world) {
ClassEntity base, _FlatTypeMaskKind kind, JClosedWorld world,
{bool isNullable: false}) {
if (base == world.commonElements.nullClass) {
return FlatTypeMask.empty();
}
return FlatTypeMask._(base, (kind << 1) | (isNullable ? 1 : 0));
return FlatTypeMask._(base, _computeFlags(kind, hasNull: isNullable));
}
FlatTypeMask._(this.base, this.flags);
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 = _hasNullFlag(flags);
if (base == domain.commonElements.nullClass) {
return FlatTypeMask.empty();
}
if ((flags >> 1) == EMPTY || ((flags >> 1) == EXACT)) {
return new FlatTypeMask._(base, flags);
_FlatTypeMaskKind kind = _lookupKind(flags);
if (kind == _FlatTypeMaskKind.empty || kind == _FlatTypeMaskKind.exact) {
return FlatTypeMask._(base, flags);
}
if ((flags >> 1) == SUBTYPE) {
if (kind == _FlatTypeMaskKind.subtype) {
if (!domain._closedWorld.classHierarchy.hasAnyStrictSubtype(base) ||
domain._closedWorld.classHierarchy.hasOnlySubclasses(base)) {
flags = (flags & 0x1) | (SUBCLASS << 1);
flags = _computeFlags(_FlatTypeMaskKind.subclass, hasNull: isNullable);
}
}
if (((flags >> 1) == SUBCLASS) &&
if (kind == _FlatTypeMaskKind.subclass &&
!domain._closedWorld.classHierarchy.hasAnyStrictSubclass(base)) {
flags = (flags & 0x1) | (EXACT << 1);
flags = _computeFlags(_FlatTypeMaskKind.exact, hasNull: isNullable);
}
return domain.getCachedMask(
base, flags, () => new FlatTypeMask._(base, flags));
return domain.getCachedMask(base, flags, () => FlatTypeMask._(base, flags));
}
/// Deserializes a [FlatTypeMask] object from [source].
@ -81,8 +101,7 @@ class FlatTypeMask implements TypeMask {
ClassEntity base = source.readClassOrNull();
int flags = source.readInt();
source.end(tag);
return domain.getCachedMask(
base, flags, () => new FlatTypeMask._(base, flags));
return domain.getCachedMask(base, flags, () => FlatTypeMask._(base, flags));
}
/// Serializes this [FlatTypeMask] to [sink].
@ -95,20 +114,24 @@ class FlatTypeMask implements TypeMask {
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 => isEmptyOrNull && !isNullable;
bool get isEmpty => isEmptyOrFlagged && _mask == _NONE_MASK;
@override
bool get isNull => isEmptyOrNull && isNullable;
bool get isNull => isEmptyOrFlagged && _mask == _NULL_MASK;
@override
bool get isEmptyOrNull => (flags >> 1) == EMPTY;
bool get isEmptyOrFlagged => _kind == _FlatTypeMaskKind.empty;
@override
bool get isExact => (flags >> 1) == EXACT;
bool get isExact => _kind == _FlatTypeMaskKind.exact;
@override
bool get isNullable => (flags & 1) != 0;
bool get isNullable => _hasNullFlag(flags);
@override
bool get isUnion => false;
@ -128,22 +151,19 @@ class FlatTypeMask implements TypeMask {
// 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;
bool get isSubclass => _kind == _FlatTypeMaskKind.subclass;
bool get isSubtype => _kind == _FlatTypeMaskKind.subtype;
@override
TypeMask nullable() {
return isNullable ? this : new FlatTypeMask._(base, flags | 1);
}
@override
TypeMask nonNullable() {
return isNullable ? new FlatTypeMask._(base, flags & ~1) : this;
FlatTypeMask withFlags({bool isNullable}) {
int newFlags = _computeFlags(_kind, hasNull: isNullable ?? this.isNullable);
if (newFlags == flags) return this;
return FlatTypeMask._(base, newFlags);
}
@override
bool contains(ClassEntity other, JClosedWorld closedWorld) {
if (isEmptyOrNull) {
if (isEmptyOrFlagged) {
return false;
} else if (identical(base, other)) {
return true;
@ -184,11 +204,11 @@ class FlatTypeMask implements TypeMask {
@override
bool isInMask(TypeMask other, JClosedWorld closedWorld) {
if (isEmptyOrNull) return isNullable ? other.isNullable : true;
// The empty type contains no classes.
if (other.isEmptyOrNull) return false;
// Quick check whether to handle null.
if (isNullable && !other.isNullable) 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);
@ -259,14 +279,14 @@ class FlatTypeMask implements TypeMask {
@override
bool satisfies(ClassEntity cls, JClosedWorld closedWorld) {
if (isEmptyOrNull) return false;
if (isEmptyOrFlagged) return false;
if (closedWorld.classHierarchy.isSubtypeOf(base, cls)) return true;
return false;
}
@override
ClassEntity singleClass(JClosedWorld closedWorld) {
if (isEmptyOrNull) return null;
if (isEmptyOrFlagged) return null;
if (isNullable) return null; // It is Null and some other class.
if (isExact) {
return base;
@ -282,7 +302,7 @@ class FlatTypeMask implements TypeMask {
@override
bool containsAll(JClosedWorld closedWorld) {
if (isEmptyOrNull || isExact) return false;
if (isEmptyOrFlagged || isExact) return false;
return identical(base, closedWorld.commonElements.objectClass);
}
@ -294,10 +314,11 @@ class FlatTypeMask implements TypeMask {
assert(TypeMask.assertIsNormalized(other, closedWorld));
if (other is! FlatTypeMask) return other.union(this, domain);
FlatTypeMask flatOther = other;
if (isEmptyOrNull) {
return isNullable ? flatOther.nullable() : flatOther;
} else if (flatOther.isEmptyOrNull) {
return flatOther.isNullable ? nullable() : this;
bool isNullable = this.isNullable || flatOther.isNullable;
if (isEmptyOrFlagged) {
return flatOther.withFlags(isNullable: isNullable);
} else if (flatOther.isEmptyOrFlagged) {
return withFlags(isNullable: isNullable);
} else if (base == flatOther.base) {
return unionSame(flatOther, domain);
} else if (closedWorld.classHierarchy.isSubclassOf(flatOther.base, base)) {
@ -309,9 +330,9 @@ class FlatTypeMask implements TypeMask {
} else if (closedWorld.classHierarchy.isSubtypeOf(base, flatOther.base)) {
return flatOther.unionStrictSubtype(this, domain);
} else {
return new UnionTypeMask._internal(
<FlatTypeMask>[this.nonNullable(), flatOther.nonNullable()],
isNullable || other.isNullable);
return UnionTypeMask._internal(
<FlatTypeMask>[withoutFlags(), flatOther.withoutFlags()],
isNullable: isNullable);
}
}
@ -323,15 +344,14 @@ class FlatTypeMask implements TypeMask {
// 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);
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 new FlatTypeMask.normalized(base, combined, domain);
return FlatTypeMask.normalized(base, combined, domain);
}
}
@ -346,19 +366,19 @@ class FlatTypeMask implements TypeMask {
// 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);
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.flags & 1)
: other.flags | (flags & 1);
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)
? new FlatTypeMask.normalized(base, combined, domain)
return flags != combined
? FlatTypeMask.normalized(base, combined, domain)
: this;
}
@ -371,11 +391,12 @@ class FlatTypeMask implements TypeMask {
// 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);
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)
? new FlatTypeMask.normalized(base, combined, domain)
return flags != combined
? FlatTypeMask.normalized(base, combined, domain)
: this;
}
@ -391,10 +412,10 @@ class FlatTypeMask implements TypeMask {
bool includeNull = isNullable && flatOther.isNullable;
if (isEmptyOrNull) {
return includeNull ? this : nonNullable();
} else if (flatOther.isEmptyOrNull) {
return includeNull ? other : other.nonNullable();
if (isEmptyOrFlagged) {
return withFlags(isNullable: includeNull);
} else if (flatOther.isEmptyOrFlagged) {
return other.withFlags(isNullable: includeNull);
}
SubclassResult result = domain._closedWorld.classHierarchy
@ -428,17 +449,17 @@ class FlatTypeMask implements TypeMask {
} else if (result.classes.length == 1) {
ClassEntity cls = result.classes.first;
return includeNull
? new TypeMask.subclass(cls, domain._closedWorld)
: new TypeMask.nonNullSubclass(cls, domain._closedWorld);
? TypeMask.subclass(cls, domain._closedWorld)
: TypeMask.nonNullSubclass(cls, domain._closedWorld);
}
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, includeNull, domain);
return UnionTypeMask.flatten(masks, domain, includeNull: includeNull);
}
return new UnionTypeMask._internal(masks, includeNull);
return UnionTypeMask._internal(masks, isNullable: includeNull);
}
}
@ -448,7 +469,7 @@ class FlatTypeMask implements TypeMask {
FlatTypeMask flatOther = other;
if (isNullable && flatOther.isNullable) return false;
if (isEmptyOrNull || flatOther.isEmptyOrNull) return true;
if (isEmptyOrFlagged || flatOther.isEmptyOrFlagged) return true;
if (base == flatOther.base) return false;
if (isExact && flatOther.isExact) return true;
@ -495,14 +516,14 @@ class FlatTypeMask implements TypeMask {
// 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);
? flags & (other._mask | ~_ALL_MASK)
: other.flags & (_mask | ~_ALL_MASK);
if (flags == combined) {
return this;
} else if (other.flags == combined) {
return other;
} else {
return new FlatTypeMask.normalized(base, combined, domain);
return FlatTypeMask.normalized(base, combined, domain);
}
}
@ -517,28 +538,26 @@ class FlatTypeMask implements TypeMask {
// 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);
int combined = other.flags & (_mask | ~_ALL_MASK);
if (other.flags == combined) {
return other;
} else {
return new FlatTypeMask.normalized(other.base, combined, domain);
return FlatTypeMask.normalized(other.base, combined, domain);
}
}
TypeMask intersectionEmpty(FlatTypeMask other) {
return isNullable && other.isNullable
? new TypeMask.empty()
: new TypeMask.nonNullEmpty();
bool isNullable = this.isNullable && other.isNullable;
return isNullable ? TypeMask.empty() : TypeMask.nonNullEmpty();
}
@override
bool canHit(MemberEntity element, Name name, JClosedWorld closedWorld) {
CommonElements commonElements = closedWorld.commonElements;
assert(element.name == name.text);
if (isEmpty) return false;
if (isNull) {
return closedWorld.hasElementIn(
commonElements.jsNullClass, name, element);
if (isEmptyOrFlagged) {
return isNullable &&
closedWorld.hasElementIn(commonElements.jsNullClass, name, element);
}
ClassEntity other = element.enclosingClass;
@ -572,7 +591,7 @@ class FlatTypeMask implements TypeMask {
Selector selector, covariant JClosedWorld closedWorld) {
// A call on an empty type mask is either dead code, or a call on
// `null`.
if (isEmptyOrNull) return false;
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
@ -584,7 +603,7 @@ class FlatTypeMask implements TypeMask {
@override
MemberEntity locateSingleMember(Selector selector, CommonMasks domain) {
if (isEmptyOrNull) return null;
if (isEmptyOrFlagged) return null;
JClosedWorld closedWorld = domain._closedWorld;
if (closedWorld.includesClosureCallInDomain(selector, this, domain))
return null;
@ -628,13 +647,15 @@ class FlatTypeMask implements TypeMask {
@override
String toString() {
if (isEmptyOrNull) 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]";
StringBuffer buffer = StringBuffer('[');
buffer.writeAll([
if (isEmpty) 'empty',
if (isNullable) 'null',
if (isExact) 'exact=${base.name}',
if (isSubclass) 'subclass=${base.name}',
if (isSubtype) 'subtype=${base.name}',
], '|');
buffer.write(']');
return buffer.toString();
}
}

View file

@ -4,15 +4,15 @@
part of masks;
/// A type mask that wraps an other one, and delegate all its
/// A type mask that wraps another one, and delegates all its
/// implementation methods to it.
abstract class ForwardingTypeMask implements TypeMask {
abstract class ForwardingTypeMask extends TypeMask {
TypeMask get forwardTo;
ForwardingTypeMask();
const ForwardingTypeMask();
@override
bool get isEmptyOrNull => forwardTo.isEmptyOrNull;
bool get isEmptyOrFlagged => forwardTo.isEmptyOrFlagged;
@override
bool get isEmpty => forwardTo.isEmpty;
@override
@ -93,17 +93,25 @@ abstract class ForwardingTypeMask implements TypeMask {
}
@override
TypeMask union(other, CommonMasks domain) {
TypeMask union(TypeMask other, CommonMasks domain) {
if (this == other) {
return this;
} else if (equalsDisregardNull(other)) {
return other.isNullable ? other : this;
} else if (other.isEmptyOrNull) {
return other.isNullable ? this.nullable() : this;
}
return forwardTo.union(other, domain);
bool isNullable = this.isNullable || other.isNullable;
if (isEmptyOrFlagged) {
return other.withFlags(isNullable: isNullable);
}
if (other.isEmptyOrFlagged) {
return withFlags(isNullable: isNullable);
}
return _unionSpecialCases(other, domain, isNullable: isNullable) ??
forwardTo.union(other, domain);
}
TypeMask _unionSpecialCases(TypeMask other, CommonMasks domain,
{bool isNullable}) =>
null;
@override
bool isDisjoint(TypeMask other, JClosedWorld closedWorld) {
return forwardTo.isDisjoint(other, closedWorld);
@ -111,7 +119,9 @@ abstract class ForwardingTypeMask implements TypeMask {
@override
TypeMask intersection(TypeMask other, CommonMasks domain) {
return forwardTo.intersection(other, domain);
TypeMask forwardIntersection = forwardTo.intersection(other, domain);
if (forwardIntersection.isEmptyOrFlagged) return forwardIntersection;
return withFlags(isNullable: forwardIntersection.isNullable);
}
@override
@ -130,28 +140,33 @@ abstract class ForwardingTypeMask implements TypeMask {
return forwardTo.locateSingleMember(selector, domain);
}
bool equalsDisregardNull(other) {
if (other is! ForwardingTypeMask) return false;
if (forwardTo.isNullable) {
return forwardTo == other.forwardTo.nullable();
} else {
return forwardTo == other.forwardTo.nonNullable();
}
}
@override
bool operator ==(other) {
return equalsDisregardNull(other) && isNullable == other.isNullable;
if (identical(this, other)) return true;
if (other is! ForwardingTypeMask) return false;
return forwardTo == other.forwardTo;
}
@override
int get hashCode => throw "Subclass should implement hashCode getter";
int get hashCode => forwardTo.hashCode;
}
abstract class AllocationTypeMask extends ForwardingTypeMask {
const AllocationTypeMask();
// The [ir.Node] where this type mask was created.
ir.Node get allocationNode;
// The [Entity] where this type mask was created.
MemberEntity get allocationElement;
@override
bool operator ==(other) {
if (identical(this, other)) return true;
if (other is! AllocationTypeMask) return false;
return super == other && allocationNode == other.allocationNode;
}
@override
int get hashCode => Hashing.objectHash(allocationNode, super.hashCode);
}

View file

@ -27,20 +27,20 @@ class MapTypeMask extends AllocationTypeMask {
// The key type of this map.
final TypeMask keyType;
MapTypeMask(this.forwardTo, this.allocationNode, this.allocationElement,
const MapTypeMask(this.forwardTo, this.allocationNode, this.allocationElement,
this.keyType, this.valueType);
/// Deserializes a [MapTypeMask] object from [source].
factory MapTypeMask.readFromDataSource(
DataSource source, CommonMasks domain) {
source.begin(tag);
TypeMask forwardTo = new TypeMask.readFromDataSource(source, domain);
TypeMask forwardTo = TypeMask.readFromDataSource(source, domain);
ir.TreeNode allocationNode = source.readTreeNodeOrNull();
MemberEntity allocationElement = source.readMemberOrNull();
TypeMask keyType = new TypeMask.readFromDataSource(source, domain);
TypeMask valueType = new TypeMask.readFromDataSource(source, domain);
TypeMask keyType = TypeMask.readFromDataSource(source, domain);
TypeMask valueType = TypeMask.readFromDataSource(source, domain);
source.end(tag);
return new MapTypeMask(
return MapTypeMask(
forwardTo, allocationNode, allocationElement, keyType, valueType);
}
@ -58,19 +58,13 @@ class MapTypeMask extends AllocationTypeMask {
}
@override
TypeMask nullable() {
return isNullable
? this
: new MapTypeMask(forwardTo.nullable(), allocationNode,
allocationElement, keyType, valueType);
}
@override
TypeMask nonNullable() {
return isNullable
? new MapTypeMask(forwardTo.nonNullable(), allocationNode,
allocationElement, keyType, valueType)
: this;
MapTypeMask withFlags({bool isNullable}) {
isNullable ??= this.isNullable;
if (isNullable == this.isNullable) {
return this;
}
return MapTypeMask(forwardTo.withFlags(isNullable: isNullable),
allocationNode, allocationElement, keyType, valueType);
}
@override
@ -81,30 +75,10 @@ class MapTypeMask extends AllocationTypeMask {
bool get isExact => true;
@override
bool equalsDisregardNull(other) {
if (other is! MapTypeMask) return false;
return super.equalsDisregardNull(other) &&
allocationNode == other.allocationNode &&
keyType == other.keyType &&
valueType == other.valueType;
}
@override
TypeMask intersection(TypeMask other, CommonMasks domain) {
TypeMask forwardIntersection = forwardTo.intersection(other, domain);
if (forwardIntersection.isEmptyOrNull) return forwardIntersection;
return forwardIntersection.isNullable ? nullable() : nonNullable();
}
@override
TypeMask union(dynamic other, CommonMasks domain) {
if (this == other) {
return this;
} else if (equalsDisregardNull(other)) {
return other.isNullable ? other : this;
} else if (other.isEmptyOrNull) {
return other.isNullable ? this.nullable() : this;
} else if (other.isMap &&
TypeMask _unionSpecialCases(TypeMask other, CommonMasks domain,
{bool isNullable}) {
assert(isNullable != null);
if (other is MapTypeMask &&
keyType != null &&
other.keyType != null &&
valueType != null &&
@ -112,19 +86,19 @@ class MapTypeMask extends AllocationTypeMask {
TypeMask newKeyType = keyType.union(other.keyType, domain);
TypeMask newValueType = valueType.union(other.valueType, domain);
TypeMask newForwardTo = forwardTo.union(other.forwardTo, domain);
return new MapTypeMask(
newForwardTo, null, null, newKeyType, newValueType);
} else if (other.isDictionary) {
return MapTypeMask(newForwardTo, null, null, newKeyType, newValueType);
}
if (other is DictionaryTypeMask) {
// TODO(johnniwinther): Find another way to check this invariant that
// doesn't need the compiler.
assert(other.keyType ==
new TypeMask.nonNullExact(
TypeMask.nonNullExact(
domain.commonElements.jsStringClass, domain._closedWorld));
TypeMask newKeyType = keyType.union(other.keyType, domain);
TypeMask newValueType =
other.typeMap.values.fold(keyType, (p, n) => p.union(n, domain));
other._typeMap.values.fold(keyType, (p, n) => p.union(n, domain));
TypeMask newForwardTo = forwardTo.union(other.forwardTo, domain);
MapTypeMask newMapTypeMask = new MapTypeMask(
MapTypeMask newMapTypeMask = MapTypeMask(
newForwardTo,
allocationNode == other.allocationNode ? allocationNode : null,
allocationElement == other.allocationElement
@ -133,19 +107,22 @@ class MapTypeMask extends AllocationTypeMask {
newKeyType,
newValueType);
return newMapTypeMask;
} else {
return forwardTo.union(other, domain);
}
return null;
}
@override
bool operator ==(other) => super == other;
bool operator ==(other) {
if (identical(this, other)) return true;
if (other is! MapTypeMask) return false;
return super == other &&
keyType == other.keyType &&
valueType == other.valueType;
}
@override
int get hashCode {
return computeHashCode(
allocationNode, isNullable, keyType, valueType, forwardTo);
}
int get hashCode => Hashing.objectHash(
valueType, Hashing.objectHash(keyType, super.hashCode));
@override
String toString() {

View file

@ -75,10 +75,10 @@ class CommonMasks implements AbstractValueDomain {
TypeMask _unmodifiableArrayType;
TypeMask _interceptorType;
/// Cache of [FlatTypeMask]s grouped by the 8 possible values of the
/// Cache of [FlatTypeMask]s grouped by the possible values of the
/// `FlatTypeMask.flags` property.
final List<Map<ClassEntity, TypeMask>> _canonicalizedTypeMasks =
new List<Map<ClassEntity, TypeMask>>.filled(8, null);
final List<Map<ClassEntity, TypeMask>> _canonicalizedTypeMasks = List.filled(
_FlatTypeMaskKind.values.length << FlatTypeMask._USED_INDICES, null);
/// Return the cached mask for [base] with the given flags, or
/// calls [createMask] to create the mask and cache it.
@ -89,126 +89,121 @@ class CommonMasks implements AbstractValueDomain {
}
@override
TypeMask get dynamicType => _dynamicType ??= new TypeMask.subclass(
_closedWorld.commonElements.objectClass, _closedWorld);
TypeMask get dynamicType => _dynamicType ??=
TypeMask.subclass(_closedWorld.commonElements.objectClass, _closedWorld);
@override
TypeMask get nonNullType => _nonNullType ??= new TypeMask.nonNullSubclass(
TypeMask get nonNullType => _nonNullType ??= TypeMask.nonNullSubclass(
_closedWorld.commonElements.objectClass, _closedWorld);
@override
TypeMask get intType => _intType ??=
new TypeMask.nonNullSubclass(commonElements.jsIntClass, _closedWorld);
TypeMask.nonNullSubclass(commonElements.jsIntClass, _closedWorld);
@override
TypeMask get uint32Type => _uint32Type ??=
new TypeMask.nonNullSubclass(commonElements.jsUInt32Class, _closedWorld);
TypeMask.nonNullSubclass(commonElements.jsUInt32Class, _closedWorld);
@override
TypeMask get uint31Type => _uint31Type ??=
new TypeMask.nonNullExact(commonElements.jsUInt31Class, _closedWorld);
TypeMask.nonNullExact(commonElements.jsUInt31Class, _closedWorld);
@override
TypeMask get positiveIntType =>
_positiveIntType ??= new TypeMask.nonNullSubclass(
commonElements.jsPositiveIntClass, _closedWorld);
TypeMask get positiveIntType => _positiveIntType ??=
TypeMask.nonNullSubclass(commonElements.jsPositiveIntClass, _closedWorld);
@override
TypeMask get numNotIntType => _numNotIntType ??=
new TypeMask.nonNullExact(commonElements.jsNumNotIntClass, _closedWorld);
TypeMask.nonNullExact(commonElements.jsNumNotIntClass, _closedWorld);
@override
TypeMask get numType => _numType ??=
new TypeMask.nonNullSubclass(commonElements.jsNumberClass, _closedWorld);
TypeMask.nonNullSubclass(commonElements.jsNumberClass, _closedWorld);
@override
TypeMask get boolType => _boolType ??=
new TypeMask.nonNullExact(commonElements.jsBoolClass, _closedWorld);
TypeMask.nonNullExact(commonElements.jsBoolClass, _closedWorld);
@override
TypeMask get functionType => _functionType ??=
new TypeMask.nonNullSubtype(commonElements.functionClass, _closedWorld);
TypeMask.nonNullSubtype(commonElements.functionClass, _closedWorld);
@override
TypeMask get listType => _listType ??=
new TypeMask.nonNullSubtype(commonElements.jsArrayClass, _closedWorld);
TypeMask.nonNullSubtype(commonElements.jsArrayClass, _closedWorld);
@override
TypeMask get constListType => _constListType ??= new TypeMask.nonNullExact(
TypeMask get constListType => _constListType ??= TypeMask.nonNullExact(
commonElements.jsUnmodifiableArrayClass, _closedWorld);
@override
TypeMask get fixedListType => _fixedListType ??=
new TypeMask.nonNullExact(commonElements.jsFixedArrayClass, _closedWorld);
TypeMask.nonNullExact(commonElements.jsFixedArrayClass, _closedWorld);
@override
TypeMask get growableListType =>
_growableListType ??= new TypeMask.nonNullExact(
commonElements.jsExtendableArrayClass, _closedWorld);
TypeMask get growableListType => _growableListType ??= TypeMask.nonNullExact(
commonElements.jsExtendableArrayClass, _closedWorld);
@override
TypeMask get setType => _setType ??=
new TypeMask.nonNullSubtype(commonElements.setLiteralClass, _closedWorld);
TypeMask.nonNullSubtype(commonElements.setLiteralClass, _closedWorld);
@override
TypeMask get constSetType => _constSetType ??= new TypeMask.nonNullSubtype(
TypeMask get constSetType => _constSetType ??= TypeMask.nonNullSubtype(
commonElements.constSetLiteralClass, _closedWorld);
@override
TypeMask get mapType => _mapType ??=
new TypeMask.nonNullSubtype(commonElements.mapLiteralClass, _closedWorld);
TypeMask.nonNullSubtype(commonElements.mapLiteralClass, _closedWorld);
@override
TypeMask get constMapType => _constMapType ??= new TypeMask.nonNullSubtype(
TypeMask get constMapType => _constMapType ??= TypeMask.nonNullSubtype(
commonElements.constMapLiteralClass, _closedWorld);
@override
TypeMask get stringType => _stringType ??=
new TypeMask.nonNullExact(commonElements.jsStringClass, _closedWorld);
TypeMask.nonNullExact(commonElements.jsStringClass, _closedWorld);
@override
TypeMask get typeType => _typeType ??=
new TypeMask.nonNullExact(commonElements.typeLiteralClass, _closedWorld);
TypeMask.nonNullExact(commonElements.typeLiteralClass, _closedWorld);
@override
TypeMask get syncStarIterableType => _syncStarIterableType ??=
new TypeMask.nonNullExact(commonElements.syncStarIterable, _closedWorld);
TypeMask.nonNullExact(commonElements.syncStarIterable, _closedWorld);
@override
TypeMask get asyncFutureType =>
_asyncFutureType ??= new TypeMask.nonNullExact(
commonElements.futureImplementation, _closedWorld);
TypeMask get asyncFutureType => _asyncFutureType ??=
TypeMask.nonNullExact(commonElements.futureImplementation, _closedWorld);
@override
TypeMask get asyncStarStreamType => _asyncStarStreamType ??=
new TypeMask.nonNullExact(commonElements.controllerStream, _closedWorld);
TypeMask.nonNullExact(commonElements.controllerStream, _closedWorld);
// TODO(johnniwinther): Assert that the null type has been resolved.
@override
TypeMask get nullType => _nullType ??= const TypeMask.empty();
TypeMask get nullType => _nullType ??= TypeMask.empty();
@override
TypeMask get emptyType => const TypeMask.nonNullEmpty();
TypeMask get emptyType => TypeMask.nonNullEmpty();
TypeMask get indexablePrimitiveType =>
_indexablePrimitiveType ??= new TypeMask.nonNullSubtype(
commonElements.jsIndexableClass, _closedWorld);
TypeMask get indexablePrimitiveType => _indexablePrimitiveType ??=
TypeMask.nonNullSubtype(commonElements.jsIndexableClass, _closedWorld);
TypeMask get readableArrayType => _readableArrayType ??=
new TypeMask.nonNullSubclass(commonElements.jsArrayClass, _closedWorld);
TypeMask.nonNullSubclass(commonElements.jsArrayClass, _closedWorld);
@override
TypeMask get mutableArrayType =>
_mutableArrayType ??= new TypeMask.nonNullSubclass(
_mutableArrayType ??= TypeMask.nonNullSubclass(
commonElements.jsMutableArrayClass, _closedWorld);
TypeMask get unmodifiableArrayType =>
_unmodifiableArrayType ??= new TypeMask.nonNullExact(
_unmodifiableArrayType ??= TypeMask.nonNullExact(
commonElements.jsUnmodifiableArrayClass, _closedWorld);
TypeMask get interceptorType =>
_interceptorType ??= new TypeMask.nonNullSubclass(
commonElements.jsInterceptorClass, _closedWorld);
TypeMask get interceptorType => _interceptorType ??=
TypeMask.nonNullSubclass(commonElements.jsInterceptorClass, _closedWorld);
@override
AbstractBool isTypedArray(TypeMask mask) {
@ -232,37 +227,37 @@ class CommonMasks implements AbstractValueDomain {
ClassEntity typedDataClass = _closedWorld.commonElements.typedDataClass;
return AbstractBool.maybeOrFalse(typedDataClass != null &&
_closedWorld.classHierarchy.isInstantiated(typedDataClass) &&
intersects(mask, new TypeMask.subtype(typedDataClass, _closedWorld)) &&
intersects(mask, TypeMask.subtype(typedDataClass, _closedWorld)) &&
intersects(
mask,
new TypeMask.subtype(
TypeMask.subtype(
_closedWorld.commonElements.jsIndexingBehaviorInterface,
_closedWorld)));
}
@override
TypeMask createNonNullExact(ClassEntity cls) {
return new TypeMask.nonNullExact(cls, _closedWorld);
return TypeMask.nonNullExact(cls, _closedWorld);
}
@override
TypeMask createNullableExact(ClassEntity cls) {
return new TypeMask.exact(cls, _closedWorld);
return TypeMask.exact(cls, _closedWorld);
}
@override
TypeMask createNonNullSubclass(ClassEntity cls) {
return new TypeMask.nonNullSubclass(cls, _closedWorld);
return TypeMask.nonNullSubclass(cls, _closedWorld);
}
@override
TypeMask createNonNullSubtype(ClassEntity cls) {
return new TypeMask.nonNullSubtype(cls, _closedWorld);
return TypeMask.nonNullSubtype(cls, _closedWorld);
}
@override
TypeMask createNullableSubtype(ClassEntity cls) {
return new TypeMask.subtype(cls, _closedWorld);
return TypeMask.subtype(cls, _closedWorld);
}
@override
@ -446,10 +441,6 @@ class CommonMasks implements AbstractValueDomain {
AbstractBool isExact(TypeMask value) =>
AbstractBool.trueOrMaybe(value.isExact && !value.isNullable);
@override
AbstractBool isExactOrNull(TypeMask value) =>
AbstractBool.trueOrMaybe(value.isExact || _isNull(value));
@override
ClassEntity getExactClass(TypeMask mask) {
return mask.singleClass(_closedWorld);
@ -470,7 +461,7 @@ class CommonMasks implements AbstractValueDomain {
@override
AbstractValue createPrimitiveValue(
covariant TypeMask originalValue, PrimitiveConstantValue value) {
return new ValueTypeMask(originalValue, value);
return ValueTypeMask(originalValue, value);
}
@override
@ -484,15 +475,13 @@ class CommonMasks implements AbstractValueDomain {
}
}
bool _isNull(TypeMask value) => value.isNull;
@override
AbstractBool isPrimitive(TypeMask value) {
return AbstractBool.maybeOrFalse(_canBePrimitiveNumber(value) ||
_canBePrimitiveArray(value) ||
_canBePrimitiveBoolean(value) ||
_canBePrimitiveString(value) ||
_isNull(value));
value.isNull);
}
@override
@ -518,10 +507,6 @@ class CommonMasks implements AbstractValueDomain {
return _containsType(value, commonElements.jsBoolClass);
}
@override
AbstractBool isPrimitiveArray(TypeMask value) =>
AbstractBool.maybeOrFalse(_canBePrimitiveArray(value));
bool _canBePrimitiveArray(TypeMask value) {
return _containsType(value, commonElements.jsArrayClass) ||
_containsType(value, commonElements.jsFixedArrayClass) ||
@ -672,7 +657,7 @@ class CommonMasks implements AbstractValueDomain {
return _isIndexablePrimitive(value) ||
_isNumberOrNull(value) ||
_isBooleanOrNull(value) ||
_isNull(value);
value.isNull;
}
@override
@ -731,13 +716,13 @@ class CommonMasks implements AbstractValueDomain {
MemberEntity allocationElement,
AbstractValue elementType,
int length) {
return new ContainerTypeMask(
return ContainerTypeMask(
forwardTo, allocationNode, allocationElement, elementType, length);
}
@override
AbstractValue unionOfMany(Iterable<AbstractValue> values) {
TypeMask result = const TypeMask.nonNullEmpty();
TypeMask result = TypeMask.nonNullEmpty();
for (TypeMask value in values) {
result = result.union(value, this);
}
@ -748,18 +733,18 @@ class CommonMasks implements AbstractValueDomain {
AbstractValue computeReceiver(Iterable<MemberEntity> members) {
assert(_closedWorld.classHierarchy
.hasAnyStrictSubclass(_closedWorld.commonElements.objectClass));
return new TypeMask.unionOf(
return TypeMask.unionOf(
members.expand((MemberEntity element) {
ClassEntity cls = element.enclosingClass;
return [cls]..addAll(_closedWorld.mixinUsesOf(cls));
}).map((cls) {
if (_closedWorld.commonElements.jsNullClass == cls) {
return const TypeMask.empty();
return TypeMask.empty();
} else if (_closedWorld.classHierarchy.isInstantiated(cls)) {
return new TypeMask.nonNullSubclass(cls, _closedWorld);
return TypeMask.nonNullSubclass(cls, _closedWorld);
} else {
// TODO(johnniwinther): Avoid the need for this case.
return const TypeMask.empty();
return TypeMask.empty();
}
}),
this);
@ -880,7 +865,7 @@ class CommonMasks implements AbstractValueDomain {
@override
AbstractValue createMapValue(AbstractValue forwardTo, Object allocationNode,
MemberEntity allocationElement, AbstractValue key, AbstractValue value) {
return new MapTypeMask(
return MapTypeMask(
forwardTo, allocationNode, allocationElement, key, value);
}
@ -892,14 +877,14 @@ class CommonMasks implements AbstractValueDomain {
AbstractValue key,
AbstractValue value,
Map<String, AbstractValue> mappings) {
return new DictionaryTypeMask(
forwardTo, allocationNode, allocationElement, key, value, mappings);
return DictionaryTypeMask(forwardTo, allocationNode, allocationElement, key,
value, Map.from(mappings));
}
@override
AbstractValue createSetValue(AbstractValue forwardTo, Object allocationNode,
MemberEntity allocationElement, AbstractValue elementType) {
return new SetTypeMask(
return SetTypeMask(
forwardTo, allocationNode, allocationElement, elementType);
}
@ -963,8 +948,8 @@ class CommonMasks implements AbstractValueDomain {
@override
TypeMask readAbstractValueFromDataSource(DataSource source) {
return source.readCached<TypeMask>(
() => new TypeMask.readFromDataSource(source, this));
return source
.readCached<TypeMask>(() => TypeMask.readFromDataSource(source, this));
}
@override
@ -985,7 +970,10 @@ String formatType(DartTypes dartTypes, TypeMask type) {
// can be really long and mess up the layout.
// Capitalize Null to emphasize that it's the null type mask and not
// a null value we accidentally printed out.
if (type.isEmptyOrNull) return type.isNullable ? 'Null' : 'Empty';
if (type.isEmpty) return 'Empty';
if (type.isEmptyOrFlagged) {
return [if (type.isNullable) 'Null'].join('');
}
String nullFlag = type.isNullable ? '?' : '';
String subFlag = type.isExact
? ''

View file

@ -24,19 +24,19 @@ class SetTypeMask extends AllocationTypeMask {
// The element type of this set.
final TypeMask elementType;
SetTypeMask(this.forwardTo, this.allocationNode, this.allocationElement,
const SetTypeMask(this.forwardTo, this.allocationNode, this.allocationElement,
this.elementType);
/// Deserializes a [SetTypeMask] object from [source].
factory SetTypeMask.readFromDataSource(
DataSource source, CommonMasks domain) {
source.begin(tag);
TypeMask forwardTo = new TypeMask.readFromDataSource(source, domain);
TypeMask forwardTo = TypeMask.readFromDataSource(source, domain);
ir.TreeNode allocationNode = source.readTreeNodeOrNull();
MemberEntity allocationElement = source.readMemberOrNull();
TypeMask elementType = new TypeMask.readFromDataSource(source, domain);
TypeMask elementType = TypeMask.readFromDataSource(source, domain);
source.end(tag);
return new SetTypeMask(
return SetTypeMask(
forwardTo, allocationNode, allocationElement, elementType);
}
@ -53,16 +53,14 @@ class SetTypeMask extends AllocationTypeMask {
}
@override
TypeMask nullable() => isNullable
? this
: new SetTypeMask(
forwardTo.nullable(), allocationNode, allocationElement, elementType);
@override
TypeMask nonNullable() => isNullable
? new SetTypeMask(forwardTo.nonNullable(), allocationNode,
allocationElement, elementType)
: this;
SetTypeMask withFlags({bool isNullable}) {
isNullable ??= this.isNullable;
if (isNullable == this.isNullable) {
return this;
}
return SetTypeMask(forwardTo.withFlags(isNullable: isNullable),
allocationNode, allocationElement, elementType);
}
@override
bool get isSet => true;
@ -71,45 +69,28 @@ class SetTypeMask extends AllocationTypeMask {
bool get isExact => true;
@override
bool equalsDisregardNull(other) {
if (other is! SetTypeMask) return false;
return super.equalsDisregardNull(other) &&
allocationNode == other.allocationNode &&
elementType == other.elementType;
}
@override
TypeMask intersection(TypeMask other, CommonMasks domain) {
TypeMask forwardIntersection = forwardTo.intersection(other, domain);
if (forwardIntersection.isEmptyOrNull) return forwardIntersection;
return forwardIntersection.isNullable ? nullable() : nonNullable();
}
@override
TypeMask union(dynamic other, CommonMasks domain) {
if (this == other) {
return this;
} else if (equalsDisregardNull(other)) {
return other.isNullable ? other : this;
} else if (other.isEmptyOrNull) {
return other.isNullable ? this.nullable() : this;
} else if (other.isSet &&
TypeMask _unionSpecialCases(TypeMask other, CommonMasks domain,
{bool isNullable}) {
assert(isNullable != null);
if (other is SetTypeMask &&
elementType != null &&
other.elementType != null) {
TypeMask newElementType = elementType.union(other.elementType, domain);
TypeMask newForwardTo = forwardTo.union(other.forwardTo, domain);
return new SetTypeMask(newForwardTo, null, null, newElementType);
} else {
return forwardTo.union(other, domain);
return SetTypeMask(newForwardTo, null, null, newElementType);
}
return null;
}
@override
bool operator ==(other) => super == other;
bool operator ==(other) {
if (identical(this, other)) return true;
if (other is! SetTypeMask) return false;
return super == other && elementType == other.elementType;
}
@override
int get hashCode =>
computeHashCode(allocationNode, isNullable, elementType, forwardTo);
int get hashCode => Hashing.objectHash(elementType, super.hashCode);
@override
String toString() => 'Set($forwardTo, element: $elementType)';

View file

@ -25,7 +25,7 @@ class IncreasingTypeMaskSet extends UniverseSelectorConstraints {
bool needsNoSuchMethodHandling(Selector selector, JClosedWorld world) {
if (isAll) {
TypeMask mask =
new TypeMask.subclass(world.commonElements.objectClass, world);
TypeMask.subclass(world.commonElements.objectClass, world);
return mask.needsNoSuchMethodHandling(selector, world);
}
for (TypeMask mask in _masks) {
@ -44,7 +44,7 @@ class IncreasingTypeMaskSet extends UniverseSelectorConstraints {
_masks = null;
return true;
}
_masks ??= new Set<TypeMask>();
_masks ??= {};
return _masks.add(mask);
}
@ -65,12 +65,12 @@ class TypeMaskStrategy implements AbstractValueStrategy {
@override
AbstractValueDomain createDomain(JClosedWorld closedWorld) {
return new CommonMasks(closedWorld);
return CommonMasks(closedWorld);
}
@override
SelectorConstraintsStrategy createSelectorStrategy() {
return new TypeMaskSelectorStrategy();
return TypeMaskSelectorStrategy();
}
}
@ -80,8 +80,7 @@ class TypeMaskSelectorStrategy implements SelectorConstraintsStrategy {
@override
UniverseSelectorConstraints createSelectorConstraints(
Selector selector, Object initialConstraint) {
return new IncreasingTypeMaskSet()
..addReceiverConstraint(initialConstraint);
return IncreasingTypeMaskSet()..addReceiverConstraint(initialConstraint);
}
@override
@ -109,13 +108,9 @@ enum TypeMaskKind {
/// operations on it are not guaranteed to be precise and they may
/// yield conservative answers that contain too many classes.
abstract class TypeMask implements AbstractValue {
factory TypeMask(
ClassEntity base, int kind, bool isNullable, CommonMasks domain) {
return new FlatTypeMask.normalized(
base, (kind << 1) | (isNullable ? 1 : 0), domain);
}
const TypeMask();
const factory TypeMask.empty() = FlatTypeMask.empty;
factory TypeMask.empty() = FlatTypeMask.empty;
factory TypeMask.exact(ClassEntity base, JClosedWorld closedWorld) {
assert(
@ -124,13 +119,13 @@ abstract class TypeMask implements AbstractValue {
base ?? CURRENT_ELEMENT_SPANNABLE,
"Cannot create exact type mask for uninstantiated "
"class $base.\n${closedWorld.classHierarchy.dump(base)}"));
return new FlatTypeMask.exact(base, closedWorld);
return FlatTypeMask.exact(base, closedWorld);
}
factory TypeMask.exactOrEmpty(ClassEntity base, JClosedWorld closedWorld) {
if (closedWorld.classHierarchy.isInstantiated(base))
return new FlatTypeMask.exact(base, closedWorld);
return const TypeMask.empty();
return FlatTypeMask.exact(base, closedWorld);
return TypeMask.empty();
}
factory TypeMask.subclass(ClassEntity base, JClosedWorld closedWorld) {
@ -142,30 +137,30 @@ abstract class TypeMask implements AbstractValue {
"class $base.\n${closedWorld.classHierarchy.dump(base)}"));
ClassEntity topmost = closedWorld.getLubOfInstantiatedSubclasses(base);
if (topmost == null) {
return new TypeMask.empty();
return TypeMask.empty();
} else if (closedWorld.classHierarchy.hasAnyStrictSubclass(topmost)) {
return new FlatTypeMask.subclass(topmost, closedWorld);
return FlatTypeMask.subclass(topmost, closedWorld);
} else {
return new TypeMask.exact(topmost, closedWorld);
return TypeMask.exact(topmost, closedWorld);
}
}
factory TypeMask.subtype(ClassEntity base, JClosedWorld closedWorld) {
ClassEntity topmost = closedWorld.getLubOfInstantiatedSubtypes(base);
if (topmost == null) {
return new TypeMask.empty();
return TypeMask.empty();
}
if (closedWorld.classHierarchy.hasOnlySubclasses(topmost)) {
return new TypeMask.subclass(topmost, closedWorld);
return TypeMask.subclass(topmost, closedWorld);
}
if (closedWorld.classHierarchy.hasAnyStrictSubtype(topmost)) {
return new FlatTypeMask.subtype(topmost, closedWorld);
return FlatTypeMask.subtype(topmost, closedWorld);
} else {
return new TypeMask.exact(topmost, closedWorld);
return TypeMask.exact(topmost, closedWorld);
}
}
const factory TypeMask.nonNullEmpty() = FlatTypeMask.nonNullEmpty;
factory TypeMask.nonNullEmpty() = FlatTypeMask.nonNullEmpty;
factory TypeMask.nonNullExact(ClassEntity base, JClosedWorld closedWorld) {
assert(
@ -174,15 +169,15 @@ abstract class TypeMask implements AbstractValue {
base ?? CURRENT_ELEMENT_SPANNABLE,
"Cannot create exact type mask for uninstantiated "
"class $base.\n${closedWorld.classHierarchy.dump(base)}"));
return new FlatTypeMask.nonNullExact(base, closedWorld);
return FlatTypeMask.nonNullExact(base, closedWorld);
}
factory TypeMask.nonNullExactOrEmpty(
ClassEntity base, JClosedWorld closedWorld) {
if (closedWorld.classHierarchy.isInstantiated(base)) {
return new FlatTypeMask.nonNullExact(base, closedWorld);
return FlatTypeMask.nonNullExact(base, closedWorld);
}
return const TypeMask.nonNullEmpty();
return TypeMask.nonNullEmpty();
}
factory TypeMask.nonNullSubclass(ClassEntity base, JClosedWorld closedWorld) {
@ -194,26 +189,26 @@ abstract class TypeMask implements AbstractValue {
"class $base.\n${closedWorld.classHierarchy.dump(base)}"));
ClassEntity topmost = closedWorld.getLubOfInstantiatedSubclasses(base);
if (topmost == null) {
return new TypeMask.nonNullEmpty();
return TypeMask.nonNullEmpty();
} else if (closedWorld.classHierarchy.hasAnyStrictSubclass(topmost)) {
return new FlatTypeMask.nonNullSubclass(topmost, closedWorld);
return FlatTypeMask.nonNullSubclass(topmost, closedWorld);
} else {
return new TypeMask.nonNullExact(topmost, closedWorld);
return TypeMask.nonNullExact(topmost, closedWorld);
}
}
factory TypeMask.nonNullSubtype(ClassEntity base, JClosedWorld closedWorld) {
ClassEntity topmost = closedWorld.getLubOfInstantiatedSubtypes(base);
if (topmost == null) {
return new TypeMask.nonNullEmpty();
return TypeMask.nonNullEmpty();
}
if (closedWorld.classHierarchy.hasOnlySubclasses(topmost)) {
return new TypeMask.nonNullSubclass(topmost, closedWorld);
return TypeMask.nonNullSubclass(topmost, closedWorld);
}
if (closedWorld.classHierarchy.hasAnyStrictSubtype(topmost)) {
return new FlatTypeMask.nonNullSubtype(topmost, closedWorld);
return FlatTypeMask.nonNullSubtype(topmost, closedWorld);
} else {
return new TypeMask.nonNullExact(topmost, closedWorld);
return TypeMask.nonNullExact(topmost, closedWorld);
}
}
@ -226,21 +221,21 @@ abstract class TypeMask implements AbstractValue {
TypeMaskKind kind = source.readEnum(TypeMaskKind.values);
switch (kind) {
case TypeMaskKind.flat:
return new FlatTypeMask.readFromDataSource(source, domain);
return FlatTypeMask.readFromDataSource(source, domain);
case TypeMaskKind.union:
return new UnionTypeMask.readFromDataSource(source, domain);
return UnionTypeMask.readFromDataSource(source, domain);
case TypeMaskKind.container:
return new ContainerTypeMask.readFromDataSource(source, domain);
return ContainerTypeMask.readFromDataSource(source, domain);
case TypeMaskKind.set:
return new SetTypeMask.readFromDataSource(source, domain);
return SetTypeMask.readFromDataSource(source, domain);
case TypeMaskKind.map:
return new MapTypeMask.readFromDataSource(source, domain);
return MapTypeMask.readFromDataSource(source, domain);
case TypeMaskKind.dictionary:
return new DictionaryTypeMask.readFromDataSource(source, domain);
return DictionaryTypeMask.readFromDataSource(source, domain);
case TypeMaskKind.value:
return new ValueTypeMask.readFromDataSource(source, domain);
return ValueTypeMask.readFromDataSource(source, domain);
}
throw new UnsupportedError("Unexpected TypeMaskKind $kind.");
throw UnsupportedError("Unexpected TypeMaskKind $kind.");
}
/// Serializes this [TypeMask] to [sink].
@ -271,7 +266,7 @@ abstract class TypeMask implements AbstractValue {
TypeMask mask, JClosedWorld closedWorld) {
mask = nonForwardingMask(mask);
if (mask is FlatTypeMask) {
if (mask.isEmptyOrNull) return null;
if (mask.isEmptyOrFlagged) return null;
if (mask.base == closedWorld.commonElements.nullClass) {
return 'The class ${mask.base} is not canonicalized.';
}
@ -308,10 +303,14 @@ abstract class TypeMask implements AbstractValue {
}
/// Returns a nullable variant of [this] type mask.
TypeMask nullable();
TypeMask nullable() => withFlags(isNullable: true);
/// Returns a non-nullable variant of [this] type mask.
TypeMask nonNullable();
TypeMask nonNullable() => withFlags(isNullable: false);
TypeMask withoutFlags() => withFlags(isNullable: false);
TypeMask withFlags({bool isNullable});
/// Whether nothing matches this mask, not even null.
bool get isEmpty;
@ -322,8 +321,8 @@ abstract class TypeMask implements AbstractValue {
/// Whether the only possible value in this mask is Null.
bool get isNull;
/// Whether [isEmpty] or [isNull] is true.
bool get isEmptyOrNull;
/// Whether [this] mask is empty or only represents values tracked by flags.
bool get isEmptyOrFlagged;
/// Whether this mask only includes instances of an exact class, and none of
/// it's subclasses or subtypes.

View file

@ -4,7 +4,7 @@
part of masks;
class UnionTypeMask implements TypeMask {
class UnionTypeMask extends TypeMask {
/// Tag used for identifying serialized [UnionTypeMask] objects in a
/// debugging data stream.
static const String tag = 'union-type-mask';
@ -22,21 +22,21 @@ class UnionTypeMask implements TypeMask {
@override
final bool isNullable;
UnionTypeMask._internal(this.disjointMasks, this.isNullable) {
assert(disjointMasks.length > 1);
assert(disjointMasks.every((TypeMask mask) => !mask.isUnion));
assert(disjointMasks.every((TypeMask mask) => !mask.isNullable));
}
UnionTypeMask._internal(this.disjointMasks, {this.isNullable})
: assert(isNullable != null),
assert(disjointMasks.length > 1),
assert(disjointMasks.every((TypeMask mask) => !mask.isUnion)),
assert(disjointMasks.every((TypeMask mask) => !mask.isNullable));
/// Deserializes a [UnionTypeMask] object from [source].
factory UnionTypeMask.readFromDataSource(
DataSource source, CommonMasks domain) {
source.begin(tag);
List<FlatTypeMask> disjointMasks =
source.readList(() => new TypeMask.readFromDataSource(source, domain));
source.readList(() => TypeMask.readFromDataSource(source, domain));
bool isNullable = source.readBool();
source.end(tag);
return new UnionTypeMask._internal(disjointMasks, isNullable);
return UnionTypeMask._internal(disjointMasks, isNullable: isNullable);
}
/// Serializes this [UnionTypeMask] to [sink].
@ -59,11 +59,12 @@ class UnionTypeMask implements TypeMask {
if (disjoint.isEmpty)
return isNullable ? TypeMask.empty() : TypeMask.nonNullEmpty();
if (disjoint.length > MAX_UNION_LENGTH) {
return flatten(disjoint, isNullable, domain);
return flatten(disjoint, domain, includeNull: isNullable);
}
if (disjoint.length == 1)
return isNullable ? disjoint[0].nullable() : disjoint[0];
UnionTypeMask union = new UnionTypeMask._internal(disjoint, isNullable);
return disjoint[0].withFlags(isNullable: isNullable);
UnionTypeMask union =
UnionTypeMask._internal(disjoint, isNullable: isNullable);
assert(TypeMask.assertIsNormalized(union, domain._closedWorld));
return union;
}
@ -73,7 +74,7 @@ class UnionTypeMask implements TypeMask {
// TODO(johnniwinther): Impose an order on the mask to ensure subclass masks
// are preferred to subtype masks.
for (TypeMask mask in masks) {
mask = TypeMask.nonForwardingMask(mask).nonNullable();
mask = TypeMask.nonForwardingMask(mask).withoutFlags();
if (mask.isUnion) {
UnionTypeMask union = mask;
unionOfHelper(union.disjointMasks, disjoint, domain);
@ -121,8 +122,10 @@ class UnionTypeMask implements TypeMask {
}
}
static TypeMask flatten(
List<FlatTypeMask> masks, bool includeNull, CommonMasks domain) {
static TypeMask flatten(List<FlatTypeMask> masks, CommonMasks domain,
{bool includeNull}) {
assert(includeNull != null);
// TODO(johnniwinther): Move this computation to [ClosedWorld] and use the
// class set structures.
if (masks.isEmpty) throw ArgumentError.value(masks, 'masks');
@ -136,7 +139,7 @@ class UnionTypeMask implements TypeMask {
// Compute the best candidate and its kind.
ClassEntity bestElement;
int bestKind;
_FlatTypeMaskKind bestKind;
int bestSize;
for (ClassEntity candidate in candidates) {
bool isInstantiatedStrictSubclass(cls) =>
@ -145,13 +148,13 @@ class UnionTypeMask implements TypeMask {
domain._closedWorld.classHierarchy.isSubclassOf(cls, candidate);
int size;
int kind;
_FlatTypeMaskKind kind;
if (useSubclass && masksBases.every(isInstantiatedStrictSubclass)) {
// If both [this] and [other] are subclasses of the supertype,
// then we prefer to construct a subclass type mask because it
// will always be at least as small as the corresponding
// subtype type mask.
kind = FlatTypeMask.SUBCLASS;
kind = _FlatTypeMaskKind.subclass;
// TODO(sigmund, johnniwinther): computing length here (and below) is
// expensive. If we can't prevent `flatten` from being called a lot, it
// might be worth caching results.
@ -160,7 +163,7 @@ class UnionTypeMask implements TypeMask {
assert(size <=
domain._closedWorld.classHierarchy.strictSubtypeCount(candidate));
} else {
kind = FlatTypeMask.SUBTYPE;
kind = _FlatTypeMaskKind.subtype;
size = domain._closedWorld.classHierarchy.strictSubtypeCount(candidate);
}
// Update the best candidate if the new one is better.
@ -170,56 +173,57 @@ class UnionTypeMask implements TypeMask {
bestKind = kind;
}
}
return new TypeMask(bestElement, bestKind, includeNull, domain);
int flags = FlatTypeMask._computeFlags(bestKind, hasNull: includeNull);
return FlatTypeMask.normalized(bestElement, flags, domain);
}
@override
TypeMask union(dynamic other, CommonMasks domain) {
TypeMask union(TypeMask other, CommonMasks domain) {
other = TypeMask.nonForwardingMask(other);
bool isNullable = this.isNullable || other.isNullable;
if (other is UnionTypeMask) {
if (_containsNonNullableUnion(other)) {
return other.isNullable ? nullable() : this;
if (_containsDisjointMasks(other)) {
return withFlags(isNullable: isNullable);
}
if (other._containsNonNullableUnion(this)) {
return isNullable ? other.nullable() : other;
if (other._containsDisjointMasks(this)) {
return other.withFlags(isNullable: isNullable);
}
} else {
if (disjointMasks.contains(other.nonNullable())) {
return other.isNullable ? nullable() : this;
if (disjointMasks.contains(other.withoutFlags())) {
return withFlags(isNullable: isNullable);
}
}
List<FlatTypeMask> newList = new List<FlatTypeMask>.of(disjointMasks);
if (!other.isUnion) {
newList.add(other);
} else {
assert(other is UnionTypeMask);
List<FlatTypeMask> newList = List<FlatTypeMask>.of(disjointMasks);
if (other is UnionTypeMask) {
newList.addAll(other.disjointMasks);
} else {
newList.add(other);
}
TypeMask newMask = new TypeMask.unionOf(newList, domain);
return isNullable || other.isNullable ? newMask.nullable() : newMask;
TypeMask newMask = TypeMask.unionOf(newList, domain);
return newMask.withFlags(isNullable: isNullable);
}
@override
TypeMask intersection(dynamic other, CommonMasks domain) {
TypeMask intersection(TypeMask other, CommonMasks domain) {
other = TypeMask.nonForwardingMask(other);
bool isNullable = this.isNullable && other.isNullable;
if (other is UnionTypeMask) {
if (_containsNonNullableUnion(other)) {
return isNullable ? other : other.nonNullable();
if (_containsDisjointMasks(other)) {
return other.withFlags(isNullable: isNullable);
}
if (other._containsNonNullableUnion(this)) {
return other.isNullable ? this : nonNullable();
if (other._containsDisjointMasks(this)) {
return withFlags(isNullable: isNullable);
}
} else {
TypeMask otherNonNullable = other.nonNullable();
if (disjointMasks.contains(otherNonNullable)) {
return isNullable ? other : otherNonNullable;
if (disjointMasks.contains(other.withoutFlags())) {
return other.withFlags(isNullable: isNullable);
}
}
List<TypeMask> intersections = <TypeMask>[];
for (TypeMask current in disjointMasks) {
if (other.isUnion) {
if (other is UnionTypeMask) {
if (other.disjointMasks.contains(current)) {
intersections.add(current);
} else {
@ -232,7 +236,7 @@ class UnionTypeMask implements TypeMask {
}
}
TypeMask newMask = TypeMask.unionOf(intersections, domain);
return isNullable && other.isNullable ? newMask.nullable() : newMask;
return newMask.withFlags(isNullable: isNullable);
}
@override
@ -245,21 +249,17 @@ class UnionTypeMask implements TypeMask {
}
@override
TypeMask nullable() {
if (isNullable) return this;
List<FlatTypeMask> newList = new List<FlatTypeMask>.of(disjointMasks);
return new UnionTypeMask._internal(newList, true);
UnionTypeMask withFlags({bool isNullable}) {
isNullable ??= this.isNullable;
if (isNullable == this.isNullable) {
return this;
}
List<FlatTypeMask> newList = List<FlatTypeMask>.of(disjointMasks);
return UnionTypeMask._internal(newList, isNullable: isNullable);
}
@override
TypeMask nonNullable() {
if (!isNullable) return this;
List<FlatTypeMask> newList = new List<FlatTypeMask>.of(disjointMasks);
return new UnionTypeMask._internal(newList, false);
}
@override
bool get isEmptyOrNull => false;
bool get isEmptyOrFlagged => false;
@override
bool get isEmpty => false;
@override
@ -292,7 +292,7 @@ class UnionTypeMask implements TypeMask {
assert(!other.isUnion);
// Likewise, nullness should be covered.
assert(isNullable || !other.isNullable);
other = other.nonNullable();
other = other.withoutFlags();
// Ensure the cheap test fails.
assert(!disjointMasks.any((mask) => mask.containsMask(other, closedWorld)));
// If we cover object, we should never get here.
@ -342,7 +342,7 @@ class UnionTypeMask implements TypeMask {
other = TypeMask.nonForwardingMask(other);
if (other.isNullable && !isNullable) return false;
if (other.isUnion) return other.isInMask(this, closedWorld);
other = other.nonNullable();
other = other.withoutFlags();
bool contained =
disjointMasks.any((mask) => mask.containsMask(other, closedWorld));
if (PERFORM_EXTRA_CONTAINS_CHECK &&
@ -448,20 +448,16 @@ class UnionTypeMask implements TypeMask {
return other is UnionTypeMask &&
other.isNullable == isNullable &&
other.disjointMasks.length == disjointMasks.length &&
_containsNonNullableUnion(other);
_containsDisjointMasks(other);
}
@override
int get hashCode {
int hashCode = isNullable ? 86 : 43;
// The order of the masks in [disjointMasks] must not affect the
// hashCode.
for (var mask in disjointMasks) {
hashCode = (hashCode ^ mask.hashCode) & 0x3fffffff;
}
return hashCode;
return Hashing.setHash(disjointMasks, isNullable.hashCode);
}
bool _containsNonNullableUnion(UnionTypeMask other) =>
bool _containsDisjointMasks(UnionTypeMask other) =>
other.disjointMasks.every((e) => disjointMasks.contains(e));
}

View file

@ -13,16 +13,16 @@ class ValueTypeMask extends ForwardingTypeMask {
final TypeMask forwardTo;
final PrimitiveConstantValue value;
ValueTypeMask(this.forwardTo, this.value);
const ValueTypeMask(this.forwardTo, this.value);
/// Deserializes a [ValueTypeMask] object from [source].
factory ValueTypeMask.readFromDataSource(
DataSource source, CommonMasks domain) {
source.begin(tag);
TypeMask forwardTo = new TypeMask.readFromDataSource(source, domain);
TypeMask forwardTo = TypeMask.readFromDataSource(source, domain);
ConstantValue constant = source.readConstant();
source.end(tag);
return new ValueTypeMask(forwardTo, constant);
return ValueTypeMask(forwardTo, constant);
}
/// Serializes this [ValueTypeMask] to [sink].
@ -36,40 +36,38 @@ class ValueTypeMask extends ForwardingTypeMask {
}
@override
TypeMask nullable() {
return isNullable ? this : new ValueTypeMask(forwardTo.nullable(), value);
}
@override
TypeMask nonNullable() {
return isNullable
? new ValueTypeMask(forwardTo.nonNullable(), value)
: this;
ValueTypeMask withFlags({bool isNullable}) {
isNullable ??= this.isNullable;
if (isNullable == this.isNullable) {
return this;
}
return ValueTypeMask(forwardTo.withFlags(isNullable: isNullable), value);
}
@override
bool get isValue => true;
@override
bool equalsDisregardNull(other) {
TypeMask _unionSpecialCases(TypeMask other, CommonMasks domain,
{bool isNullable}) {
assert(isNullable != null);
if (other is ValueTypeMask &&
forwardTo.withoutFlags() == other.forwardTo.withoutFlags() &&
value == other.value) {
return withFlags(isNullable: isNullable);
}
return null;
}
@override
bool operator ==(other) {
if (identical(this, other)) return true;
if (other is! ValueTypeMask) return false;
return super.equalsDisregardNull(other) && value == other.value;
return super == other && value == other.value;
}
@override
TypeMask intersection(TypeMask other, CommonMasks domain) {
TypeMask forwardIntersection = forwardTo.intersection(other, domain);
if (forwardIntersection.isEmptyOrNull) return forwardIntersection;
return forwardIntersection.isNullable ? nullable() : nonNullable();
}
@override
bool operator ==(other) => super == other;
@override
int get hashCode {
return computeHashCode(value, isNullable, forwardTo);
}
int get hashCode => Hashing.objectHash(value, super.hashCode);
@override
String toString() {

View file

@ -1159,9 +1159,6 @@ abstract class HInstruction implements Spannable {
AbstractBool isPrimitiveBoolean(AbstractValueDomain domain) =>
domain.isPrimitiveBoolean(instructionType);
AbstractBool isPrimitiveArray(AbstractValueDomain domain) =>
domain.isPrimitiveArray(instructionType);
AbstractBool isIndexablePrimitive(AbstractValueDomain domain) =>
domain.isIndexablePrimitive(instructionType);

View file

@ -233,15 +233,6 @@ void writeJsonEscapedCharsOn(String string, buffer) {
buffer.write(string);
}
int computeHashCode(part1, [part2, part3, part4, part5]) {
return (part1.hashCode ^
part2.hashCode ^
part3.hashCode ^
part4.hashCode ^
part5.hashCode) &
0x3fffffff;
}
class Pair<A, B> {
final A a;
final B b;

View file

@ -47,66 +47,10 @@
"Dynamic access of 'memberContext'.": 1,
"Dynamic access of 'name'.": 1
},
"pkg/compiler/lib/src/inferrer/typemasks/container_type_mask.dart": {
"Dynamic access of 'isNullable'.": 2,
"Dynamic access of 'isEmptyOrNull'.": 1,
"Dynamic access of 'isContainer'.": 1,
"Dynamic access of 'elementType'.": 2,
"Dynamic access of 'length'.": 1,
"Dynamic access of 'forwardTo'.": 1,
"Dynamic access of 'allocationNode'.": 1,
"Dynamic access of 'allocationElement'.": 1
},
"pkg/compiler/lib/src/inferrer/typemasks/dictionary_type_mask.dart": {
"Dynamic access of 'isNullable'.": 2,
"Dynamic access of 'isEmptyOrNull'.": 1,
"Dynamic access of 'isDictionary'.": 1,
"Dynamic access of 'forwardTo'.": 2,
"Dynamic access of 'keyType'.": 3,
"Dynamic access of 'valueType'.": 3,
"Dynamic access of 'masks::_typeMap'.": 2,
"Dynamic invocation of 'containsKey'.": 1,
"Dynamic invocation of 'nullable'.": 2,
"Dynamic invocation of 'union'.": 1,
"Dynamic invocation of 'forEach'.": 1,
"Dynamic access of 'isMap'.": 1
},
"pkg/compiler/lib/src/inferrer/typemasks/forwarding_type_mask.dart": {
"Dynamic access of 'isNullable'.": 1
},
"pkg/compiler/lib/src/inferrer/typemasks/map_type_mask.dart": {
"Dynamic access of 'isNullable'.": 2,
"Dynamic access of 'isEmptyOrNull'.": 1,
"Dynamic access of 'isMap'.": 1,
"Dynamic access of 'keyType'.": 4,
"Dynamic access of 'valueType'.": 2,
"Dynamic access of 'forwardTo'.": 2,
"Dynamic access of 'isDictionary'.": 1,
"Dynamic invocation of 'union'.": 1,
"Dynamic access of 'typeMap'.": 1,
"Dynamic access of 'values'.": 1,
"Dynamic invocation of 'fold'.": 1,
"Dynamic access of 'allocationNode'.": 1,
"Dynamic access of 'allocationElement'.": 1
},
"pkg/compiler/lib/src/inferrer/typemasks/set_type_mask.dart": {
"Dynamic access of 'isNullable'.": 2,
"Dynamic access of 'isEmptyOrNull'.": 1,
"Dynamic access of 'isSet'.": 1,
"Dynamic access of 'elementType'.": 2,
"Dynamic access of 'forwardTo'.": 1
},
"pkg/compiler/lib/src/inferrer/typemasks/type_mask.dart": {
"Dynamic access of 'isForwarding'.": 1,
"Dynamic access of 'forwardTo'.": 1
},
"pkg/compiler/lib/src/inferrer/typemasks/union_type_mask.dart": {
"Dynamic invocation of 'nonNullable'.": 2,
"Dynamic access of 'isNullable'.": 3,
"Dynamic access of 'isUnion'.": 2,
"Dynamic access of 'disjointMasks'.": 3,
"Dynamic invocation of 'contains'.": 1
},
"pkg/compiler/lib/src/helpers/expensive_map.dart": {
"Dynamic access of 'length'.": 1,
"Dynamic access of 'isEmpty'.": 1,

View file

@ -69,19 +69,23 @@ class Pair {
class RuleSet {
final name;
final operate;
final Set typesSeen = new Set();
final Set pairsSeen = new Set();
final Set typesSeen = {};
final Set pairsSeen = {};
RuleSet(this.name, this.operate);
void rule(type1, type2, result) {
typesSeen..add(type1)..add(type2);
var pair1 = new Pair(type1, type2);
var pair2 = new Pair(type2, type1);
typesSeen
..add(type1)
..add(type2);
var pair1 = Pair(type1, type2);
var pair2 = Pair(type2, type1);
if (pairsSeen.contains(pair1)) {
Expect.isFalse(true, 'Redundant rule ($type1, $type2, ...)');
}
pairsSeen..add(pair1)..add(pair2);
pairsSeen
..add(pair1)
..add(pair2);
var r1 = operate(type1, type2);
var r2 = operate(type2, type1);
@ -90,8 +94,10 @@ class RuleSet {
}
void check(type1, type2, predicate) {
typesSeen..add(type1)..add(type2);
var pair = new Pair(type1, type2);
typesSeen
..add(type1)
..add(type2);
var pair = Pair(type1, type2);
pairsSeen..add(pair);
var result = operate(type1, type2);
Expect.isTrue(predicate(result));
@ -100,7 +106,7 @@ class RuleSet {
void validateCoverage() {
for (var type1 in typesSeen) {
for (var type2 in typesSeen) {
var pair = new Pair(type1, type2);
var pair = Pair(type1, type2);
if (!pairsSeen.contains(pair)) {
Expect.isTrue(false, 'Missing rule: $name($type1, $type2)');
}
@ -111,7 +117,7 @@ class RuleSet {
void testUnion(JClosedWorld closedWorld) {
AbstractValueDomain commonMasks = closedWorld.abstractValueDomain;
RuleSet ruleSet = new RuleSet(
RuleSet ruleSet = RuleSet(
'union', (t1, t2) => simplify(t1.union(t2, commonMasks), commonMasks));
rule(type1, type2, result) => ruleSet.rule(type1, type2, result);
check(type1, type2, predicate) => ruleSet.check(type1, type2, predicate);
@ -422,7 +428,7 @@ void testUnion(JClosedWorld closedWorld) {
}
void testIntersection(JClosedWorld closedWorld) {
RuleSet ruleSet = new RuleSet('intersection',
RuleSet ruleSet = RuleSet('intersection',
(t1, t2) => t1.intersection(t2, closedWorld.abstractValueDomain));
rule(type1, type2, result) => ruleSet.rule(type1, type2, result);
@ -566,12 +572,12 @@ void testIntersection(JClosedWorld closedWorld) {
rule(
jsIndexable,
potentialArray,
new TypeMask.nonNullSubtype(
TypeMask.nonNullSubtype(
closedWorld.commonElements.jsArrayClass, closedWorld));
rule(
jsIndexable,
potentialString,
new TypeMask.nonNullSubtype(
TypeMask.nonNullSubtype(
closedWorld.commonElements.jsStringClass, closedWorld));
rule(jsIndexable, jsBooleanOrNull, emptyType);
rule(jsIndexable, jsNumberOrNull, emptyType);
@ -738,7 +744,7 @@ void testIntersection(JClosedWorld closedWorld) {
void testRegressions(JClosedWorld closedWorld) {
TypeMask nonNullPotentialString =
new TypeMask.nonNullSubtype(patternClass, closedWorld);
TypeMask.nonNullSubtype(patternClass, closedWorld);
Expect.equals(
potentialString,
jsStringOrNull.union(
@ -776,67 +782,67 @@ runTests() async {
LibraryEntity coreLibrary = commonElements.coreLibrary;
patternClass = elementEnvironment.lookupClass(coreLibrary, 'Pattern');
nonPrimitive1 = new TypeMask.nonNullSubtype(
closedWorld.commonElements.mapClass, closedWorld);
nonPrimitive2 = new TypeMask.nonNullSubtype(
nonPrimitive1 =
TypeMask.nonNullSubtype(closedWorld.commonElements.mapClass, closedWorld);
nonPrimitive2 = TypeMask.nonNullSubtype(
closedWorld.commonElements.functionClass, closedWorld);
potentialArray =
new TypeMask.subtype(closedWorld.commonElements.listClass, closedWorld);
potentialString = new TypeMask.subtype(patternClass, closedWorld);
jsInterceptor = new TypeMask.nonNullSubclass(
TypeMask.subtype(closedWorld.commonElements.listClass, closedWorld);
potentialString = TypeMask.subtype(patternClass, closedWorld);
jsInterceptor = TypeMask.nonNullSubclass(
closedWorld.commonElements.jsInterceptorClass, closedWorld);
jsArrayOrNull = new TypeMask.subclass(
jsArrayOrNull =
TypeMask.subclass(closedWorld.commonElements.jsArrayClass, closedWorld);
jsReadableArray = TypeMask.nonNullSubclass(
closedWorld.commonElements.jsArrayClass, closedWorld);
jsReadableArray = new TypeMask.nonNullSubclass(
closedWorld.commonElements.jsArrayClass, closedWorld);
jsMutableArrayOrNull = new TypeMask.subclass(
jsMutableArrayOrNull = TypeMask.subclass(
closedWorld.commonElements.jsMutableArrayClass, closedWorld);
jsMutableArray = new TypeMask.nonNullSubclass(
jsMutableArray = TypeMask.nonNullSubclass(
closedWorld.commonElements.jsMutableArrayClass, closedWorld);
jsFixedArrayOrNull = new TypeMask.exact(
jsFixedArrayOrNull =
TypeMask.exact(closedWorld.commonElements.jsFixedArrayClass, closedWorld);
jsFixedArray = TypeMask.nonNullExact(
closedWorld.commonElements.jsFixedArrayClass, closedWorld);
jsFixedArray = new TypeMask.nonNullExact(
closedWorld.commonElements.jsFixedArrayClass, closedWorld);
jsExtendableArrayOrNull = new TypeMask.exact(
jsExtendableArrayOrNull = TypeMask.exact(
closedWorld.commonElements.jsExtendableArrayClass, closedWorld);
jsExtendableArray = new TypeMask.nonNullExact(
jsExtendableArray = TypeMask.nonNullExact(
closedWorld.commonElements.jsExtendableArrayClass, closedWorld);
jsUnmodifiableArrayOrNull = new TypeMask.exact(
jsUnmodifiableArrayOrNull = TypeMask.exact(
closedWorld.commonElements.jsUnmodifiableArrayClass, closedWorld);
jsUnmodifiableArray = new TypeMask.nonNullExact(
jsUnmodifiableArray = TypeMask.nonNullExact(
closedWorld.commonElements.jsUnmodifiableArrayClass, closedWorld);
jsIndexableOrNull = new TypeMask.subtype(
jsIndexableOrNull = TypeMask.subtype(
closedWorld.commonElements.jsIndexableClass, closedWorld);
jsIndexable = new TypeMask.nonNullSubtype(
jsIndexable = TypeMask.nonNullSubtype(
closedWorld.commonElements.jsIndexableClass, closedWorld);
jsInterceptorOrNull = new TypeMask.subclass(
jsInterceptorOrNull = TypeMask.subclass(
closedWorld.commonElements.jsInterceptorClass, closedWorld);
jsStringOrNull =
new TypeMask.exact(closedWorld.commonElements.jsStringClass, closedWorld);
jsString = new TypeMask.nonNullExact(
TypeMask.exact(closedWorld.commonElements.jsStringClass, closedWorld);
jsString = TypeMask.nonNullExact(
closedWorld.commonElements.jsStringClass, closedWorld);
jsBoolean = new TypeMask.nonNullExact(
jsBoolean = TypeMask.nonNullExact(
closedWorld.commonElements.jsBoolClass, closedWorld);
jsNumber = new TypeMask.nonNullSubclass(
jsNumber = TypeMask.nonNullSubclass(
closedWorld.commonElements.jsNumberClass, closedWorld);
jsInteger = new TypeMask.nonNullExact(
closedWorld.commonElements.jsIntClass, closedWorld);
jsNumNotInt = new TypeMask.nonNullExact(
jsInteger =
TypeMask.nonNullExact(closedWorld.commonElements.jsIntClass, closedWorld);
jsNumNotInt = TypeMask.nonNullExact(
closedWorld.commonElements.jsNumNotIntClass, closedWorld);
jsBooleanOrNull =
new TypeMask.exact(closedWorld.commonElements.jsBoolClass, closedWorld);
jsNumberOrNull = new TypeMask.subclass(
closedWorld.commonElements.jsNumberClass, closedWorld);
TypeMask.exact(closedWorld.commonElements.jsBoolClass, closedWorld);
jsNumberOrNull =
TypeMask.subclass(closedWorld.commonElements.jsNumberClass, closedWorld);
jsIntegerOrNull =
new TypeMask.exact(closedWorld.commonElements.jsIntClass, closedWorld);
jsNumNotIntOrNull = new TypeMask.exact(
closedWorld.commonElements.jsNumNotIntClass, closedWorld);
nullType = const TypeMask.empty();
objectType = new TypeMask.nonNullSubclass(
closedWorld.commonElements.objectClass, closedWorld);
emptyType = const TypeMask.nonNullEmpty();
dynamicType = new TypeMask.subclass(
TypeMask.exact(closedWorld.commonElements.jsIntClass, closedWorld);
jsNumNotIntOrNull =
TypeMask.exact(closedWorld.commonElements.jsNumNotIntClass, closedWorld);
nullType = TypeMask.empty();
objectType = TypeMask.nonNullSubclass(
closedWorld.commonElements.objectClass, closedWorld);
emptyType = TypeMask.nonNullEmpty();
dynamicType =
TypeMask.subclass(closedWorld.commonElements.objectClass, closedWorld);
jsInterceptorOrComparable =
interceptorOrComparable(closedWorld, nullable: false);

View file

@ -41,12 +41,13 @@ checkMasks(JClosedWorld closedWorld, List<ClassEntity> allClasses,
'Unexpected disjoint masks: $disjoint, expected $disjointMasks.');
if (flattened == null) {
Expect.throws(
() => UnionTypeMask.flatten(disjoint, isNullable, commonMasks),
() => UnionTypeMask.flatten(disjoint, commonMasks,
includeNull: isNullable),
(e) => e is ArgumentError,
'Expect argument error on flattening of $disjoint.');
} else {
TypeMask flattenResult =
UnionTypeMask.flatten(disjoint, isNullable, commonMasks);
UnionTypeMask.flatten(disjoint, commonMasks, includeNull: isNullable);
Expect.equals(
flattened,
flattenResult,
@ -120,16 +121,16 @@ Future testUnionTypeMaskFlatten() async {
containedClasses: containedClasses);
}
TypeMask empty = const TypeMask.nonNullEmpty();
TypeMask subclassObject = new TypeMask.nonNullSubclass(Object_, closedWorld);
TypeMask exactA = new TypeMask.nonNullExact(A, closedWorld);
TypeMask subclassA = new TypeMask.nonNullSubclass(A, closedWorld);
TypeMask subtypeA = new TypeMask.nonNullSubtype(A, closedWorld);
TypeMask exactB = new TypeMask.nonNullExact(B, closedWorld);
TypeMask subclassB = new TypeMask.nonNullSubclass(B, closedWorld);
TypeMask exactC = new TypeMask.nonNullExact(C, closedWorld);
TypeMask exactD = new TypeMask.nonNullExact(D, closedWorld);
TypeMask exactE = new TypeMask.nonNullExact(E, closedWorld);
TypeMask empty = TypeMask.nonNullEmpty();
TypeMask subclassObject = TypeMask.nonNullSubclass(Object_, closedWorld);
TypeMask exactA = TypeMask.nonNullExact(A, closedWorld);
TypeMask subclassA = TypeMask.nonNullSubclass(A, closedWorld);
TypeMask subtypeA = TypeMask.nonNullSubtype(A, closedWorld);
TypeMask exactB = TypeMask.nonNullExact(B, closedWorld);
TypeMask subclassB = TypeMask.nonNullSubclass(B, closedWorld);
TypeMask exactC = TypeMask.nonNullExact(C, closedWorld);
TypeMask exactD = TypeMask.nonNullExact(D, closedWorld);
TypeMask exactE = TypeMask.nonNullExact(E, closedWorld);
check([],
result: empty,
@ -241,11 +242,10 @@ Future testStringSubtypes() async {
Expect.isFalse(closedWorld.classHierarchy.isIndirectlyInstantiated(JSString));
Expect.isTrue(closedWorld.classHierarchy.isInstantiated(JSString));
TypeMask subtypeString = new TypeMask.nonNullSubtype(String_, closedWorld);
TypeMask exactJSString = new TypeMask.nonNullExact(JSString, closedWorld);
TypeMask subtypeJSString = new TypeMask.nonNullSubtype(JSString, closedWorld);
TypeMask subclassJSString =
new TypeMask.nonNullSubclass(JSString, closedWorld);
TypeMask subtypeString = TypeMask.nonNullSubtype(String_, closedWorld);
TypeMask exactJSString = TypeMask.nonNullExact(JSString, closedWorld);
TypeMask subtypeJSString = TypeMask.nonNullSubtype(JSString, closedWorld);
TypeMask subclassJSString = TypeMask.nonNullSubclass(JSString, closedWorld);
Expect.equals(exactJSString, subtypeString);
Expect.equals(exactJSString, subtypeJSString);

View file

@ -16,7 +16,8 @@ AbstractValue simplify(AbstractValue value, AbstractValueDomain domain) {
if (value is ForwardingTypeMask) {
return simplify(value.forwardTo, domain);
} else if (value is UnionTypeMask) {
return UnionTypeMask.flatten(value.disjointMasks, value.isNullable, domain);
return UnionTypeMask.flatten(value.disjointMasks, domain,
includeNull: value.isNullable);
} else {
return value;
}