| 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
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a5fa5a8dc9618e51ca53f4b51496735ff404cdbf
|
| --- /dev/null
|
| +++ b/pkg/compiler/lib/src/cps_ir/type_mask_system.dart
|
| @@ -0,0 +1,298 @@
|
| +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
| +// for details. All rights reserved. Use of this source code is governed by a
|
| +// BSD-style license that can be found in the LICENSE file.
|
| +
|
| +library dart2js.type_mask_system;
|
| +
|
| +import '../closure.dart' show ClosureClassElement, Identifiers;
|
| +import '../common/names.dart' show Selectors, Identifiers;
|
| +import '../compiler.dart' as dart2js show Compiler;
|
| +import '../constants/constant_system.dart';
|
| +import '../constants/values.dart';
|
| +import '../dart_types.dart' as types;
|
| +import '../elements/elements.dart';
|
| +import '../io/source_information.dart' show SourceInformation;
|
| +import '../js_backend/js_backend.dart' show JavaScriptBackend;
|
| +import '../types/types.dart';
|
| +import '../types/constants.dart' show computeTypeMask;
|
| +import '../universe/universe.dart';
|
| +import '../world.dart' show World;
|
| +
|
| +enum AbstractBool {
|
| + True, False, Maybe, Nothing
|
| +}
|
| +
|
| +class TypeMaskSystem {
|
| + final TypesTask inferrer;
|
| + final World classWorld;
|
| + final JavaScriptBackend backend;
|
| +
|
| + TypeMask get dynamicType => inferrer.dynamicType;
|
| + TypeMask get typeType => inferrer.typeType;
|
| + TypeMask get functionType => inferrer.functionType;
|
| + TypeMask get boolType => inferrer.boolType;
|
| + TypeMask get intType => inferrer.intType;
|
| + TypeMask get doubleType => inferrer.doubleType;
|
| + TypeMask get numType => inferrer.numType;
|
| + TypeMask get stringType => inferrer.stringType;
|
| + TypeMask get listType => inferrer.listType;
|
| + TypeMask get mapType => inferrer.mapType;
|
| + TypeMask get nonNullType => inferrer.nonNullType;
|
| + TypeMask get nullType => inferrer.nullType;
|
| + TypeMask get extendableNativeListType => backend.extendableArrayType;
|
| +
|
| + TypeMask numStringBoolType;
|
| +
|
| + ClassElement get jsNullClass => backend.jsNullClass;
|
| +
|
| + // TODO(karlklose): remove compiler here.
|
| + TypeMaskSystem(dart2js.Compiler compiler)
|
| + : inferrer = compiler.typesTask,
|
| + classWorld = compiler.world,
|
| + backend = compiler.backend {
|
| +
|
| + // Build the number+string+bool type. To make containment tests more
|
| + // inclusive, we use the num, String, bool types for this, not
|
| + // the JSNumber, JSString, JSBool subclasses.
|
| + TypeMask anyNum =
|
| + new TypeMask.nonNullSubtype(classWorld.numClass, classWorld);
|
| + TypeMask anyString =
|
| + new TypeMask.nonNullSubtype(classWorld.stringClass, classWorld);
|
| + TypeMask anyBool =
|
| + new TypeMask.nonNullSubtype(classWorld.boolClass, classWorld);
|
| + numStringBoolType =
|
| + new TypeMask.unionOf(<TypeMask>[anyNum, anyString, anyBool],
|
| + classWorld);
|
| + }
|
| +
|
| + bool methodUsesReceiverArgument(FunctionElement function) {
|
| + assert(backend.isInterceptedMethod(function));
|
| + ClassElement clazz = function.enclosingClass.declaration;
|
| + return clazz.isSubclassOf(backend.jsInterceptorClass) ||
|
| + classWorld.isUsedAsMixin(clazz);
|
| + }
|
| +
|
| + Element locateSingleElement(TypeMask mask, Selector selector) {
|
| + return mask.locateSingleElement(selector, mask, classWorld.compiler);
|
| + }
|
| +
|
| + ClassElement singleClass(TypeMask mask) {
|
| + return mask.singleClass(classWorld);
|
| + }
|
| +
|
| + bool needsNoSuchMethodHandling(TypeMask mask, Selector selector) {
|
| + return mask.needsNoSuchMethodHandling(selector, classWorld);
|
| + }
|
| +
|
| + TypeMask getReceiverType(MethodElement method) {
|
| + assert(method.isInstanceMember);
|
| + if (classWorld.isUsedAsMixin(method.enclosingClass.declaration)) {
|
| + // If used as a mixin, the receiver could be any of the classes that mix
|
| + // in the class, and these are not considered subclasses.
|
| + // TODO(asgerf): Exclude the subtypes that only `implement` the class.
|
| + return nonNullSubtype(method.enclosingClass);
|
| + } else {
|
| + return nonNullSubclass(method.enclosingClass);
|
| + }
|
| + }
|
| +
|
| + TypeMask getParameterType(ParameterElement parameter) {
|
| + return inferrer.getGuaranteedTypeOfElement(parameter);
|
| + }
|
| +
|
| + TypeMask getReturnType(FunctionElement function) {
|
| + return inferrer.getGuaranteedReturnTypeOfElement(function);
|
| + }
|
| +
|
| + TypeMask getInvokeReturnType(Selector selector, TypeMask mask) {
|
| + return inferrer.getGuaranteedTypeOfSelector(selector, mask);
|
| + }
|
| +
|
| + TypeMask getFieldType(FieldElement field) {
|
| + return inferrer.getGuaranteedTypeOfElement(field);
|
| + }
|
| +
|
| + TypeMask join(TypeMask a, TypeMask b) {
|
| + return a.union(b, classWorld);
|
| + }
|
| +
|
| + TypeMask getTypeOf(ConstantValue constant) {
|
| + return computeTypeMask(inferrer.compiler, constant);
|
| + }
|
| +
|
| + TypeMask nonNullExact(ClassElement element) {
|
| + // 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?
|
| + if (element.isClosure) return functionType;
|
| + return new TypeMask.nonNullExact(element.declaration, classWorld);
|
| + }
|
| +
|
| + TypeMask nonNullSubclass(ClassElement element) {
|
| + if (element.isClosure) return functionType;
|
| + return new TypeMask.nonNullSubclass(element.declaration, classWorld);
|
| + }
|
| +
|
| + TypeMask nonNullSubtype(ClassElement element) {
|
| + if (element.isClosure) return functionType;
|
| + return new TypeMask.nonNullSubtype(element.declaration, classWorld);
|
| + }
|
| +
|
| + bool isDefinitelyBool(TypeMask t, {bool allowNull: false}) {
|
| + if (!allowNull && t.isNullable) return false;
|
| + return t.nonNullable().containsOnlyBool(classWorld);
|
| + }
|
| +
|
| + bool isDefinitelyNum(TypeMask t, {bool allowNull: false}) {
|
| + if (!allowNull && t.isNullable) return false;
|
| + return t.nonNullable().containsOnlyNum(classWorld);
|
| + }
|
| +
|
| + bool isDefinitelyString(TypeMask t, {bool allowNull: false}) {
|
| + if (!allowNull && t.isNullable) return false;
|
| + return t.nonNullable().containsOnlyString(classWorld);
|
| + }
|
| +
|
| + bool isDefinitelyNumStringBool(TypeMask t, {bool allowNull: false}) {
|
| + if (!allowNull && t.isNullable) return false;
|
| + return numStringBoolType.containsMask(t.nonNullable(), classWorld);
|
| + }
|
| +
|
| + bool isDefinitelyNotNumStringBool(TypeMask t) {
|
| + return areDisjoint(t, numStringBoolType);
|
| + }
|
| +
|
| + /// True if all values of [t] are either integers or not numbers at all.
|
| + ///
|
| + /// This does not imply that the value is an integer, since most other values
|
| + /// such as null are also not a non-integer double.
|
| + 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*
|
| + // double class, so this excludes things that are known to be instances of a
|
| + // more specific class.
|
| + // We currently exploit that there are no subclasses of double that are
|
| + // not integers (e.g. there is no UnsignedDouble class or whatever).
|
| + return areDisjoint(t, doubleType);
|
| + }
|
| +
|
| + bool isDefinitelyInt(TypeMask t, {bool allowNull: false}) {
|
| + if (!allowNull && t.isNullable) return false;
|
| + return t.satisfies(backend.jsIntClass, classWorld);
|
| + }
|
| +
|
| + bool isDefinitelyNativeList(TypeMask t, {bool allowNull: false}) {
|
| + if (!allowNull && t.isNullable) return false;
|
| + return t.nonNullable().satisfies(backend.jsArrayClass, classWorld);
|
| + }
|
| +
|
| + bool isDefinitelyMutableNativeList(TypeMask t, {bool allowNull: false}) {
|
| + if (!allowNull && t.isNullable) return false;
|
| + return t.nonNullable().satisfies(backend.jsMutableArrayClass, classWorld);
|
| + }
|
| +
|
| + bool isDefinitelyFixedNativeList(TypeMask t, {bool allowNull: false}) {
|
| + if (!allowNull && t.isNullable) return false;
|
| + return t.nonNullable().satisfies(backend.jsFixedArrayClass, classWorld);
|
| + }
|
| +
|
| + bool isDefinitelyExtendableNativeList(TypeMask t, {bool allowNull: false}) {
|
| + if (!allowNull && t.isNullable) return false;
|
| + return t.nonNullable().satisfies(backend.jsExtendableArrayClass,
|
| + classWorld);
|
| + }
|
| +
|
| + bool isDefinitelyIndexable(TypeMask t, {bool allowNull: false}) {
|
| + if (!allowNull && t.isNullable) return false;
|
| + return t.nonNullable().satisfies(backend.jsIndexableClass, classWorld);
|
| + }
|
| +
|
| + bool areDisjoint(TypeMask leftType, TypeMask rightType) {
|
| + TypeMask intersection = leftType.intersection(rightType, classWorld);
|
| + return intersection.isEmpty && !intersection.isNullable;
|
| + }
|
| +
|
| + AbstractBool isSubtypeOf(TypeMask value,
|
| + types.DartType type,
|
| + {bool allowNull}) {
|
| + assert(allowNull != null);
|
| + if (type is types.DynamicType) {
|
| + return AbstractBool.True;
|
| + }
|
| + if (type is types.InterfaceType) {
|
| + TypeMask typeAsMask = allowNull
|
| + ? new TypeMask.subtype(type.element, classWorld)
|
| + : new TypeMask.nonNullSubtype(type.element, classWorld);
|
| + if (areDisjoint(value, typeAsMask)) {
|
| + // Disprove the subtype relation based on the class alone.
|
| + return AbstractBool.False;
|
| + }
|
| + if (!type.treatAsRaw) {
|
| + // If there are type arguments, we cannot prove the subtype relation,
|
| + // because the type arguments are unknown on both the value and type.
|
| + return AbstractBool.Maybe;
|
| + }
|
| + if (typeAsMask.containsMask(value, classWorld)) {
|
| + // All possible values are contained in the set of allowed values.
|
| + // Note that we exploit the fact that [typeAsMask] is an exact
|
| + // representation of [type], not an approximation.
|
| + return AbstractBool.True;
|
| + }
|
| + // The value is neither contained in the type, nor disjoint from the type.
|
| + return AbstractBool.Maybe;
|
| + }
|
| + // TODO(asgerf): Support function types, and what else might be missing.
|
| + return AbstractBool.Maybe;
|
| + }
|
| +
|
| + /// Returns whether [type] is one of the falsy values: false, 0, -0, NaN,
|
| + /// the empty string, or null.
|
| + AbstractBool boolify(TypeMask type) {
|
| + if (isDefinitelyNotNumStringBool(type) && !type.isNullable) {
|
| + return AbstractBool.True;
|
| + }
|
| + return AbstractBool.Maybe;
|
| + }
|
| +
|
| + 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].
|
| + TypeMask subtypesOf(types.DartType type) {
|
| + if (type is types.InterfaceType) {
|
| + ClassElement element = type.element;
|
| + if (element.isObject) {
|
| + return dynamicType;
|
| + }
|
| + if (element == classWorld.nullClass) {
|
| + return nullType;
|
| + }
|
| + if (element == classWorld.stringClass) {
|
| + return stringType;
|
| + }
|
| + if (element == classWorld.numClass ||
|
| + element == classWorld.doubleClass) {
|
| + return numType;
|
| + }
|
| + if (element == classWorld.intClass) {
|
| + return intType;
|
| + }
|
| + if (element == classWorld.boolClass) {
|
| + return boolType;
|
| + }
|
| + return new TypeMask.nonNullSubtype(element, classWorld);
|
| + }
|
| + if (type is types.FunctionType) {
|
| + return functionType;
|
| + }
|
| + return dynamicType;
|
| + }
|
| +
|
| + /// Returns a subset of [mask] containing at least the types
|
| + /// that can respond to [selector] without throwing.
|
| + TypeMask receiverTypeFor(Selector selector, TypeMask mask) {
|
| + return classWorld.allFunctions.receiverType(selector, mask);
|
| + }
|
| +}
|
|
|