mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 09:58:32 +00:00
[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:
parent
4744f66f6f
commit
39fd70e816
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
? ''
|
||||
|
|
|
@ -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)';
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue