| Index: pkg/compiler/lib/src/cps_ir/type_mask_system.dart
|
| diff --git a/pkg/compiler/lib/src/cps_ir/type_mask_system.dart b/pkg/compiler/lib/src/cps_ir/type_mask_system.dart
|
| index 58d7330cc754175d428d52499d86a7e1c60d0ad0..a0da73e51282875c0c5004737d4d991fd609b624 100644
|
| --- a/pkg/compiler/lib/src/cps_ir/type_mask_system.dart
|
| +++ b/pkg/compiler/lib/src/cps_ir/type_mask_system.dart
|
| @@ -11,17 +11,13 @@ import '../dart_types.dart' as types;
|
| import '../elements/elements.dart';
|
| import '../js_backend/backend_helpers.dart' show BackendHelpers;
|
| import '../js_backend/js_backend.dart' show JavaScriptBackend;
|
| +import '../types/abstract_value_domain.dart';
|
| import '../types/types.dart';
|
| import '../types/constants.dart' show computeTypeMask;
|
| import '../universe/selector.dart' show Selector;
|
| -import '../universe/call_structure.dart' show CallStructure;
|
| import '../world.dart' show World;
|
|
|
| -enum AbstractBool {
|
| - True, False, Maybe, Nothing
|
| -}
|
| -
|
| -class TypeMaskSystem {
|
| +class TypeMaskSystem implements AbstractValueDomain {
|
| final TypesTask inferrer;
|
| final World classWorld;
|
| final JavaScriptBackend backend;
|
| @@ -33,27 +29,62 @@ class TypeMaskSystem {
|
|
|
| TypeMask __indexableTypeTest;
|
|
|
| + @override
|
| TypeMask get dynamicType => inferrer.dynamicType;
|
| +
|
| + @override
|
| TypeMask get typeType => inferrer.typeType;
|
| +
|
| + @override
|
| TypeMask get functionType => inferrer.functionType;
|
| +
|
| + @override
|
| TypeMask get boolType => inferrer.boolType;
|
| +
|
| + @override
|
| TypeMask get intType => inferrer.intType;
|
| +
|
| + @override
|
| TypeMask get doubleType => inferrer.doubleType;
|
| +
|
| + @override
|
| TypeMask get numType => inferrer.numType;
|
| +
|
| + @override
|
| TypeMask get stringType => inferrer.stringType;
|
| +
|
| + @override
|
| TypeMask get listType => inferrer.listType;
|
| +
|
| + @override
|
| TypeMask get mapType => inferrer.mapType;
|
| +
|
| + @override
|
| TypeMask get nonNullType => inferrer.nonNullType;
|
| +
|
| + @override
|
| TypeMask get nullType => inferrer.nullType;
|
| +
|
| + @override
|
| TypeMask get extendableArrayType => backend.extendableArrayType;
|
| +
|
| + @override
|
| TypeMask get fixedArrayType => backend.fixedArrayType;
|
| +
|
| + @override
|
| TypeMask get arrayType =>
|
| new TypeMask.nonNullSubclass(helpers.jsArrayClass, classWorld);
|
|
|
| + @override
|
| TypeMask get uint31Type => inferrer.uint31Type;
|
| +
|
| + @override
|
| TypeMask get uint32Type => inferrer.uint32Type;
|
| +
|
| + @override
|
| TypeMask get uintType => inferrer.positiveIntType;
|
|
|
| + @override
|
| TypeMask get numStringBoolType {
|
| if (_numStringBoolType == null) {
|
| // Build the number+string+bool type. To make containment tests more
|
| @@ -72,6 +103,7 @@ class TypeMaskSystem {
|
| return _numStringBoolType;
|
| }
|
|
|
| + @override
|
| TypeMask get fixedLengthType {
|
| if (_fixedLengthType == null) {
|
| List<TypeMask> fixedLengthTypes =
|
| @@ -84,6 +116,7 @@ class TypeMaskSystem {
|
| return _fixedLengthType;
|
| }
|
|
|
| + @override
|
| TypeMask get interceptorType {
|
| if (_interceptorType == null) {
|
| _interceptorType =
|
| @@ -92,6 +125,7 @@ class TypeMaskSystem {
|
| return _interceptorType;
|
| }
|
|
|
| + @override
|
| TypeMask get interceptedTypes { // Does not include null.
|
| if (_interceptedTypes == null) {
|
| // We redundantly include subtypes of num/string/bool as intercepted
|
| @@ -130,6 +164,7 @@ class TypeMaskSystem {
|
| backend = compiler.backend {
|
| }
|
|
|
| + @override
|
| bool methodUsesReceiverArgument(FunctionElement function) {
|
| assert(backend.isInterceptedMethod(function));
|
| ClassElement clazz = function.enclosingClass.declaration;
|
| @@ -137,18 +172,22 @@ class TypeMaskSystem {
|
| classWorld.isUsedAsMixin(clazz);
|
| }
|
|
|
| + @override
|
| Element locateSingleElement(TypeMask mask, Selector selector) {
|
| return mask.locateSingleElement(selector, mask, classWorld.compiler);
|
| }
|
|
|
| + @override
|
| ClassElement singleClass(TypeMask mask) {
|
| return mask.singleClass(classWorld);
|
| }
|
|
|
| + @override
|
| bool needsNoSuchMethodHandling(TypeMask mask, Selector selector) {
|
| return mask.needsNoSuchMethodHandling(selector, classWorld);
|
| }
|
|
|
| + @override
|
| TypeMask getReceiverType(MethodElement method) {
|
| assert(method.isInstanceMember);
|
| if (classWorld.isUsedAsMixin(method.enclosingClass.declaration)) {
|
| @@ -161,14 +200,17 @@ class TypeMaskSystem {
|
| }
|
| }
|
|
|
| + @override
|
| TypeMask getParameterType(ParameterElement parameter) {
|
| return inferrer.getGuaranteedTypeOfElement(parameter);
|
| }
|
|
|
| + @override
|
| TypeMask getReturnType(FunctionElement function) {
|
| return inferrer.getGuaranteedReturnTypeOfElement(function);
|
| }
|
|
|
| + @override
|
| TypeMask getInvokeReturnType(Selector selector, TypeMask mask) {
|
| TypeMask result = inferrer.getGuaranteedTypeOfSelector(selector, mask);
|
| // Tearing off .call from a function returns the function itself.
|
| @@ -180,26 +222,29 @@ class TypeMaskSystem {
|
| return result;
|
| }
|
|
|
| + @override
|
| TypeMask getFieldType(FieldElement field) {
|
| return inferrer.getGuaranteedTypeOfElement(field);
|
| }
|
|
|
| + @override
|
| TypeMask join(TypeMask a, TypeMask b) {
|
| return a.union(b, classWorld);
|
| }
|
|
|
| + @override
|
| TypeMask intersection(TypeMask a, TypeMask b) {
|
| if (a == null) return b;
|
| if (b == null) return a;
|
| return a.intersection(b, classWorld);
|
| }
|
|
|
| + @override
|
| TypeMask getTypeOf(ConstantValue constant) {
|
| return computeTypeMask(inferrer.compiler, constant);
|
| }
|
|
|
| - // Returns the constant value if a TypeMask represents a single value.
|
| - // Returns `null` if [mask] is not a constant.
|
| + @override
|
| ConstantValue getConstantOf(TypeMask mask) {
|
| if (!mask.isValue) return null;
|
| if (mask.isNullable) return null; // e.g. 'true or null'.
|
| @@ -209,7 +254,9 @@ class TypeMaskSystem {
|
| return null;
|
| }
|
|
|
| + @override
|
| TypeMask nonNullExact(ClassElement element) {
|
| + // TODO(johnniwinther): I don't think the follow is valid anymore.
|
| // The class world does not know about classes created by
|
| // closure conversion, so just treat those as a subtypes of Function.
|
| // TODO(asgerf): Maybe closure conversion should create a new ClassWorld?
|
| @@ -217,36 +264,43 @@ class TypeMaskSystem {
|
| return new TypeMask.nonNullExact(element.declaration, classWorld);
|
| }
|
|
|
| + @override
|
| TypeMask nonNullSubclass(ClassElement element) {
|
| if (element.isClosure) return functionType;
|
| return new TypeMask.nonNullSubclass(element.declaration, classWorld);
|
| }
|
|
|
| + @override
|
| TypeMask nonNullSubtype(ClassElement element) {
|
| if (element.isClosure) return functionType;
|
| return new TypeMask.nonNullSubtype(element.declaration, classWorld);
|
| }
|
|
|
| + @override
|
| bool isDefinitelyBool(TypeMask t, {bool allowNull: false}) {
|
| if (!allowNull && t.isNullable) return false;
|
| return t.nonNullable().containsOnlyBool(classWorld);
|
| }
|
|
|
| + @override
|
| bool isDefinitelyNum(TypeMask t, {bool allowNull: false}) {
|
| if (!allowNull && t.isNullable) return false;
|
| return t.nonNullable().containsOnlyNum(classWorld);
|
| }
|
|
|
| + @override
|
| bool isDefinitelyString(TypeMask t, {bool allowNull: false}) {
|
| if (!allowNull && t.isNullable) return false;
|
| return t.nonNullable().containsOnlyString(classWorld);
|
| }
|
|
|
| + @override
|
| bool isDefinitelyNumStringBool(TypeMask t, {bool allowNull: false}) {
|
| if (!allowNull && t.isNullable) return false;
|
| return numStringBoolType.containsMask(t.nonNullable(), classWorld);
|
| }
|
|
|
| + @override
|
| bool isDefinitelyNotNumStringBool(TypeMask t) {
|
| return areDisjoint(t, numStringBoolType);
|
| }
|
| @@ -255,6 +309,7 @@ class TypeMaskSystem {
|
| ///
|
| /// This does not imply that the value is an integer, since most other values
|
| /// such as null are also not a non-integer double.
|
| + @override
|
| bool isDefinitelyNotNonIntegerDouble(TypeMask t) {
|
| // Even though int is a subclass of double in the JS type system, we can
|
| // still check this with disjointness, because [doubleType] is the *exact*
|
| @@ -265,69 +320,82 @@ class TypeMaskSystem {
|
| return areDisjoint(t, doubleType);
|
| }
|
|
|
| + @override
|
| bool isDefinitelyNonNegativeInt(TypeMask t, {bool allowNull: false}) {
|
| if (!allowNull && t.isNullable) return false;
|
| // The JSPositiveInt class includes zero, despite the name.
|
| return t.satisfies(helpers.jsPositiveIntClass, classWorld);
|
| }
|
|
|
| + @override
|
| bool isDefinitelyInt(TypeMask t, {bool allowNull: false}) {
|
| if (!allowNull && t.isNullable) return false;
|
| return t.nonNullable().containsOnlyInt(classWorld);
|
| }
|
|
|
| + @override
|
| bool isDefinitelyUint31(TypeMask t, {bool allowNull: false}) {
|
| if (!allowNull && t.isNullable) return false;
|
| return t.satisfies(helpers.jsUInt31Class, classWorld);
|
| }
|
|
|
| + @override
|
| bool isDefinitelyUint32(TypeMask t, {bool allowNull: false}) {
|
| if (!allowNull && t.isNullable) return false;
|
| return t.satisfies(helpers.jsUInt32Class, classWorld);
|
| }
|
|
|
| + @override
|
| bool isDefinitelyUint(TypeMask t, {bool allowNull: false}) {
|
| if (!allowNull && t.isNullable) return false;
|
| return t.satisfies(helpers.jsPositiveIntClass, classWorld);
|
| }
|
|
|
| + @override
|
| bool isDefinitelyArray(TypeMask t, {bool allowNull: false}) {
|
| if (!allowNull && t.isNullable) return false;
|
| return t.nonNullable().satisfies(helpers.jsArrayClass, classWorld);
|
| }
|
|
|
| + @override
|
| bool isDefinitelyMutableArray(TypeMask t, {bool allowNull: false}) {
|
| if (!allowNull && t.isNullable) return false;
|
| return t.nonNullable().satisfies(helpers.jsMutableArrayClass, classWorld);
|
| }
|
|
|
| + @override
|
| bool isDefinitelyFixedArray(TypeMask t, {bool allowNull: false}) {
|
| if (!allowNull && t.isNullable) return false;
|
| return t.nonNullable().satisfies(helpers.jsFixedArrayClass, classWorld);
|
| }
|
|
|
| + @override
|
| bool isDefinitelyExtendableArray(TypeMask t, {bool allowNull: false}) {
|
| if (!allowNull && t.isNullable) return false;
|
| return t.nonNullable().satisfies(helpers.jsExtendableArrayClass,
|
| classWorld);
|
| }
|
|
|
| + @override
|
| bool isDefinitelyIndexable(TypeMask t, {bool allowNull: false}) {
|
| if (!allowNull && t.isNullable) return false;
|
| return _indexableTypeTest.containsMask(t.nonNullable(), classWorld);
|
| }
|
|
|
| + @override
|
| bool isDefinitelyMutableIndexable(TypeMask t, {bool allowNull: false}) {
|
| if (!allowNull && t.isNullable) return false;
|
| return t.nonNullable().satisfies(helpers.jsMutableIndexableClass,
|
| classWorld);
|
| }
|
|
|
| + @override
|
| bool isDefinitelyFixedLengthIndexable(TypeMask t, {bool allowNull: false}) {
|
| if (!allowNull && t.isNullable) return false;
|
| return fixedLengthType.containsMask(t.nonNullable(), classWorld);
|
| }
|
|
|
| + @override
|
| bool isDefinitelyIntercepted(TypeMask t, {bool allowNull}) {
|
| assert(allowNull != null);
|
| if (!allowNull && t.isNullable) return false;
|
| @@ -336,6 +404,7 @@ class TypeMaskSystem {
|
|
|
| /// Given a class from the interceptor hierarchy, returns a [TypeMask]
|
| /// matching all values with that interceptor (or a subtype thereof).
|
| + @override
|
| TypeMask getInterceptorSubtypes(ClassElement class_) {
|
| if (class_ == helpers.jsInterceptorClass) {
|
| return interceptorType.nullable();
|
| @@ -346,15 +415,18 @@ class TypeMaskSystem {
|
| }
|
| }
|
|
|
| + @override
|
| bool areDisjoint(TypeMask leftType, TypeMask rightType) {
|
| TypeMask intersected = intersection(leftType, rightType);
|
| return intersected.isEmpty && !intersected.isNullable;
|
| }
|
|
|
| + @override
|
| bool isMorePreciseOrEqual(TypeMask t1, TypeMask t2) {
|
| return t2.containsMask(t1, classWorld);
|
| }
|
|
|
| + @override
|
| AbstractBool isSubtypeOf(TypeMask value,
|
| types.DartType type,
|
| {bool allowNull}) {
|
| @@ -390,6 +462,7 @@ class TypeMaskSystem {
|
|
|
| /// Returns whether [type] is one of the falsy values: false, 0, -0, NaN,
|
| /// the empty string, or null.
|
| + @override
|
| AbstractBool boolify(TypeMask type) {
|
| if (isDefinitelyNotNumStringBool(type) && !type.isNullable) {
|
| return AbstractBool.True;
|
| @@ -397,12 +470,14 @@ class TypeMaskSystem {
|
| return AbstractBool.Maybe;
|
| }
|
|
|
| + @override
|
| AbstractBool strictBoolify(TypeMask type) {
|
| if (areDisjoint(type, boolType)) return AbstractBool.False;
|
| return AbstractBool.Maybe;
|
| }
|
|
|
| /// Create a type mask containing at least all subtypes of [type].
|
| + @override
|
| TypeMask subtypesOf(types.DartType type) {
|
| if (type is types.InterfaceType) {
|
| ClassElement element = type.element;
|
| @@ -435,12 +510,14 @@ class TypeMaskSystem {
|
|
|
| /// Returns a subset of [mask] containing at least the types
|
| /// that can respond to [selector] without throwing.
|
| + @override
|
| TypeMask receiverTypeFor(Selector selector, TypeMask mask) {
|
| return classWorld.allFunctions.receiverType(selector, mask);
|
| }
|
|
|
| /// The result of an index operation on something of [type], or the dynamic
|
| /// type if unknown.
|
| + @override
|
| TypeMask elementTypeOfIndexable(TypeMask type) {
|
| if (type is UnionTypeMask) {
|
| return new TypeMask.unionOf(
|
| @@ -462,6 +539,7 @@ class TypeMaskSystem {
|
| }
|
|
|
| /// The length of something of [type], or `null` if unknown.
|
| + @override
|
| int getContainerLength(TypeMask type) {
|
| if (type is ContainerTypeMask) {
|
| return type.length;
|
|
|