| Index: pkg/compiler/lib/src/cps_ir/type_propagation.dart
|
| diff --git a/pkg/compiler/lib/src/cps_ir/type_propagation.dart b/pkg/compiler/lib/src/cps_ir/type_propagation.dart
|
| index 5ef491bd3195c94721de5189ef157bd044c059f2..d59a717da9dabbfcaed82bde628b410fb82f1682 100644
|
| --- a/pkg/compiler/lib/src/cps_ir/type_propagation.dart
|
| +++ b/pkg/compiler/lib/src/cps_ir/type_propagation.dart
|
| @@ -30,248 +30,7 @@ import '../universe/universe.dart';
|
| import '../world.dart' show World;
|
| import 'cps_fragment.dart';
|
| import 'cps_ir_nodes.dart';
|
| -import 'cps_ir_nodes_sexpr.dart' show SExpressionStringifier;
|
| -
|
| -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 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;
|
| - }
|
| -}
|
| +import 'type_mask_system.dart';
|
|
|
| class ConstantPropagationLattice {
|
| final TypeMaskSystem typeSystem;
|
| @@ -575,17 +334,17 @@ class TypePropagator extends Pass {
|
|
|
| final dart2js.Compiler _compiler;
|
| final CpsFunctionCompiler _functionCompiler;
|
| - // The constant system is used for evaluation of expressions with constant
|
| - // arguments.
|
| final ConstantPropagationLattice _lattice;
|
| final dart2js.InternalErrorFunction _internalError;
|
| final Map<Definition, AbstractValue> _values = <Definition, AbstractValue>{};
|
|
|
| - TypePropagator(dart2js.Compiler compiler, this._functionCompiler)
|
| + TypePropagator(dart2js.Compiler compiler,
|
| + TypeMaskSystem typeSystem,
|
| + this._functionCompiler)
|
| : _compiler = compiler,
|
| _internalError = compiler.internalError,
|
| _lattice = new ConstantPropagationLattice(
|
| - new TypeMaskSystem(compiler),
|
| + typeSystem,
|
| compiler.backend.constantSystem,
|
| compiler.types);
|
|
|
| @@ -2572,13 +2331,10 @@ class TypePropagationVisitor implements Visitor {
|
|
|
| case AbstractBool.Maybe:
|
| setReachable(cont);
|
| - TypeMask type = input.type;
|
| - if (node.type.element is ClassElement) {
|
| - // Narrow type of output to those that survive the cast.
|
| - type = type.intersection(
|
| - new TypeMask.subtype(node.type.element, classWorld),
|
| - classWorld);
|
| - }
|
| + // Narrow type of output to those that survive the cast.
|
| + TypeMask type = input.type.intersection(
|
| + typeSystem.subtypesOf(node.type),
|
| + classWorld);
|
| setValue(cont.parameters.single, nonConstant(type));
|
| break;
|
| }
|
|
|