| Index: sdk/lib/_internal/compiler/implementation/inferrer/concrete_types_inferrer.dart
|
| diff --git a/sdk/lib/_internal/compiler/implementation/inferrer/concrete_types_inferrer.dart b/sdk/lib/_internal/compiler/implementation/inferrer/concrete_types_inferrer.dart
|
| deleted file mode 100644
|
| index 129f49c1bfc0cdd84542d05fa2f39677b810b73f..0000000000000000000000000000000000000000
|
| --- a/sdk/lib/_internal/compiler/implementation/inferrer/concrete_types_inferrer.dart
|
| +++ /dev/null
|
| @@ -1,2381 +0,0 @@
|
| -// Copyright (c) 2012, 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 concrete_types_inferrer;
|
| -
|
| -import 'dart:collection' show Queue, IterableBase;
|
| -import '../native/native.dart' as native;
|
| -import '../dart2jslib.dart' hide Selector, TypedSelector;
|
| -import '../dart_types.dart' show DartType, TypeKind;
|
| -import '../elements/elements.dart';
|
| -import '../tree/tree.dart';
|
| -import '../universe/universe.dart';
|
| -import '../util/util.dart';
|
| -
|
| -import 'inferrer_visitor.dart';
|
| -import '../types/types.dart' show TypeMask, FlatTypeMask, UnionTypeMask,
|
| - TypesInferrer;
|
| -import 'simple_types_inferrer.dart';
|
| -
|
| -/**
|
| - * A singleton concrete type. More precisely, a [BaseType] is one of the
|
| - * following:
|
| - *
|
| - * - a non-asbtract class like [:int:] or [:Uri:] (but not [:List:])
|
| - * - the null base type
|
| - * - the unknown base type
|
| - */
|
| -abstract class BaseType {
|
| - bool isClass();
|
| - bool isUnknown();
|
| - bool isNull();
|
| -}
|
| -
|
| -/**
|
| - * A non-asbtract class like [:int:] or [:Uri:] (but not [:List:]).
|
| - */
|
| -class ClassBaseType implements BaseType {
|
| - final ClassElement element;
|
| -
|
| - ClassBaseType(this.element);
|
| -
|
| - bool operator ==(var other) {
|
| - if (identical(this, other)) return true;
|
| - if (other is! ClassBaseType) return false;
|
| - return element == other.element;
|
| - }
|
| - int get hashCode => element.hashCode;
|
| - String toString() {
|
| - return element == null ? 'toplevel' : element.name;
|
| - }
|
| - bool isClass() => true;
|
| - bool isUnknown() => false;
|
| - bool isNull() => false;
|
| -}
|
| -
|
| -/**
|
| - * The unknown base type.
|
| - */
|
| -class UnknownBaseType implements BaseType {
|
| - const UnknownBaseType();
|
| - bool operator ==(BaseType other) => other is UnknownBaseType;
|
| - int get hashCode => 0;
|
| - bool isClass() => false;
|
| - bool isUnknown() => true;
|
| - bool isNull() => false;
|
| - toString() => "unknown";
|
| -}
|
| -
|
| -/**
|
| - * The null base type.
|
| - */
|
| -class NullBaseType implements BaseType {
|
| - const NullBaseType();
|
| - bool operator ==(BaseType other) => identical(other, this);
|
| - int get hashCode => 1;
|
| - bool isClass() => false;
|
| - bool isUnknown() => false;
|
| - bool isNull() => true;
|
| - toString() => "null";
|
| -}
|
| -
|
| -/**
|
| - * An immutable set of base types like [:{int, bool}:], or the unknown concrete
|
| - * type.
|
| - */
|
| -abstract class ConcreteType {
|
| - ConcreteType();
|
| -
|
| - factory ConcreteType.empty(int maxConcreteTypeSize,
|
| - BaseTypes classBaseTypes) {
|
| - return new UnionType(maxConcreteTypeSize, classBaseTypes,
|
| - new Set<BaseType>());
|
| - }
|
| -
|
| - /**
|
| - * The singleton constituted of the unknown base type is the unknown concrete
|
| - * type.
|
| - */
|
| - factory ConcreteType.singleton(int maxConcreteTypeSize,
|
| - BaseTypes classBaseTypes,
|
| - BaseType baseType) {
|
| - if (baseType.isUnknown() || maxConcreteTypeSize < 1) {
|
| - return const UnknownConcreteType();
|
| - }
|
| - Set<BaseType> singletonSet = new Set<BaseType>();
|
| - singletonSet.add(baseType);
|
| - return new UnionType(maxConcreteTypeSize, classBaseTypes, singletonSet);
|
| - }
|
| -
|
| - factory ConcreteType.unknown() {
|
| - return const UnknownConcreteType();
|
| - }
|
| -
|
| - ConcreteType union(ConcreteType other);
|
| - ConcreteType intersection(ConcreteType other);
|
| - ConcreteType refine(Selector selector, Compiler compiler);
|
| - bool isUnknown();
|
| - bool isEmpty();
|
| - Set<BaseType> get baseTypes;
|
| -
|
| - /**
|
| - * Returns the unique element of [:this:] if [:this:] is a singleton, null
|
| - * otherwise.
|
| - */
|
| - ClassElement getUniqueType();
|
| -}
|
| -
|
| -/**
|
| - * The unkown concrete type: it is absorbing for the union.
|
| - */
|
| -class UnknownConcreteType implements ConcreteType {
|
| - const UnknownConcreteType();
|
| - bool isUnknown() => true;
|
| - bool isEmpty() => false;
|
| - bool operator ==(ConcreteType other) => identical(this, other);
|
| - Set<BaseType> get baseTypes =>
|
| - new Set<BaseType>.from([const UnknownBaseType()]);
|
| - int get hashCode => 0;
|
| - ConcreteType union(ConcreteType other) => this;
|
| - ConcreteType intersection(ConcreteType other) => other;
|
| - ConcreteType refine(Selector selector, Compiler compiler) => this;
|
| - ClassElement getUniqueType() => null;
|
| - toString() => "unknown";
|
| -}
|
| -
|
| -/**
|
| - * An immutable set of base types, like [: {int, bool} :].
|
| - */
|
| -class UnionType implements ConcreteType {
|
| - final int maxConcreteTypeSize;
|
| - final BaseTypes classBaseTypes;
|
| -
|
| - final Set<BaseType> baseTypes;
|
| -
|
| - /**
|
| - * The argument should NOT be mutated later. Do not call directly, use
|
| - * ConcreteType.singleton instead.
|
| - */
|
| - UnionType(this.maxConcreteTypeSize, this.classBaseTypes, this.baseTypes);
|
| -
|
| - bool isUnknown() => false;
|
| - bool isEmpty() => baseTypes.isEmpty;
|
| -
|
| - bool operator ==(ConcreteType other) {
|
| - if (other is! UnionType) return false;
|
| - if (baseTypes.length != other.baseTypes.length) return false;
|
| - return baseTypes.containsAll(other.baseTypes);
|
| - }
|
| -
|
| - int get hashCode {
|
| - int result = 1;
|
| - for (final baseType in baseTypes) {
|
| - result = 31 * result + baseType.hashCode;
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - ConcreteType _simplify(Set<BaseType> baseTypes) {
|
| - // normalize all flavors of ints to int
|
| - // TODO(polux): handle different ints better
|
| - if (baseTypes.contains(classBaseTypes.uint31Type)) {
|
| - baseTypes.remove(classBaseTypes.uint31Type);
|
| - baseTypes.add(classBaseTypes.intBaseType);
|
| - }
|
| - if (baseTypes.contains(classBaseTypes.uint32Type)) {
|
| - baseTypes.remove(classBaseTypes.uint32Type);
|
| - baseTypes.add(classBaseTypes.intBaseType);
|
| - }
|
| - if (baseTypes.contains(classBaseTypes.positiveIntType)) {
|
| - baseTypes.remove(classBaseTypes.positiveIntType);
|
| - baseTypes.add(classBaseTypes.intBaseType);
|
| - }
|
| - // normalize {int, float}, {int, num} or {float, num} into num
|
| - // TODO(polux): generalize this to all types when we extend the concept of
|
| - // "concrete type" to other abstract classes than num
|
| - if (baseTypes.contains(classBaseTypes.numBaseType) ||
|
| - (baseTypes.contains(classBaseTypes.intBaseType)
|
| - && baseTypes.contains(classBaseTypes.doubleBaseType))) {
|
| - baseTypes.remove(classBaseTypes.intBaseType);
|
| - baseTypes.remove(classBaseTypes.doubleBaseType);
|
| - baseTypes.add(classBaseTypes.numBaseType);
|
| - }
|
| -
|
| - // widen big types to dynamic
|
| - return baseTypes.length > maxConcreteTypeSize
|
| - ? const UnknownConcreteType()
|
| - : new UnionType(maxConcreteTypeSize, classBaseTypes, baseTypes);
|
| - }
|
| -
|
| - ConcreteType union(ConcreteType other) {
|
| - if (other.isUnknown()) {
|
| - return const UnknownConcreteType();
|
| - }
|
| - UnionType otherUnion = other; // cast
|
| - Set<BaseType> newBaseTypes = new Set<BaseType>.from(baseTypes);
|
| - newBaseTypes.addAll(otherUnion.baseTypes);
|
| - return _simplify(newBaseTypes);
|
| - }
|
| -
|
| - ConcreteType intersection(ConcreteType other) {
|
| - if (other.isUnknown()) {
|
| - return this;
|
| - }
|
| - Set<BaseType> thisBaseTypes = new Set<BaseType>.from(baseTypes);
|
| - Set<BaseType> otherBaseTypes = new Set<BaseType>.from(other.baseTypes);
|
| - return _simplify(thisBaseTypes.intersection(otherBaseTypes));
|
| - }
|
| -
|
| - ConcreteType refine(Selector selector, Compiler compiler) {
|
| - Set<BaseType> newBaseTypes = new Set<BaseType>();
|
| - for (BaseType baseType in baseTypes) {
|
| - if (baseType.isClass()) {
|
| - ClassBaseType classBaseType = baseType;
|
| - if (classBaseType.element.lookupSelector(selector) != null) {
|
| - newBaseTypes.add(baseType);
|
| - }
|
| - } else {
|
| - newBaseTypes.add(baseType);
|
| - }
|
| - }
|
| - return _simplify(newBaseTypes);
|
| - }
|
| -
|
| - ClassElement getUniqueType() {
|
| - if (baseTypes.length == 1) {
|
| - var iterator = baseTypes.iterator;
|
| - iterator.moveNext();
|
| - BaseType uniqueBaseType = iterator.current;
|
| - if (uniqueBaseType.isClass()) {
|
| - ClassBaseType uniqueClassType = uniqueBaseType;
|
| - return uniqueClassType.element;
|
| - }
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - String toString() => baseTypes.toString();
|
| -}
|
| -
|
| -class ConcreteTypeSystem extends TypeSystem<ConcreteType> {
|
| - final Compiler compiler;
|
| - final ConcreteTypesInferrer inferrer;
|
| - final BaseTypes baseTypes;
|
| -
|
| - final ConcreteType nullType;
|
| - final ConcreteType _intType;
|
| - final ConcreteType _uint31Type;
|
| - final ConcreteType _uint32Type;
|
| - final ConcreteType _positiveIntType;
|
| - final ConcreteType _doubleType;
|
| - final ConcreteType _numType;
|
| - final ConcreteType _boolType;
|
| - final ConcreteType _functionType;
|
| - final ConcreteType _listType;
|
| - final ConcreteType _constListType;
|
| - final ConcreteType _fixedListType;
|
| - final ConcreteType _growableListType;
|
| - final ConcreteType _mapType;
|
| - final ConcreteType _constMapType;
|
| - final ConcreteType _stringType;
|
| -
|
| - final ConcreteType dynamicType;
|
| - final ConcreteType typeType;
|
| - final ConcreteType nonNullEmptyType;
|
| -
|
| - ConcreteTypeSystem.internal(ConcreteTypesInferrer inferrer,
|
| - BaseTypes baseTypes,
|
| - ConcreteType singleton(BaseType baseType))
|
| - : this.compiler = inferrer.compiler
|
| - , this.inferrer = inferrer
|
| - , this.baseTypes = baseTypes
|
| - , this._constListType = singleton(baseTypes.constMapBaseType)
|
| - , this._constMapType = singleton(baseTypes.constMapBaseType)
|
| - , this._doubleType = singleton(baseTypes.doubleBaseType)
|
| - , this._fixedListType = singleton(baseTypes.fixedListBaseType)
|
| - , this._functionType = singleton(baseTypes.functionBaseType)
|
| - , this._growableListType = singleton(baseTypes.growableListBaseType)
|
| - , this._intType = singleton(baseTypes.intBaseType)
|
| - , this._listType = singleton(baseTypes.listBaseType)
|
| - , this._mapType = singleton(baseTypes.mapBaseType)
|
| - , this._numType = singleton(baseTypes.numBaseType)
|
| - , this._boolType = singleton(baseTypes.boolBaseType)
|
| - , this._stringType = singleton(baseTypes.stringBaseType)
|
| - , this.typeType = singleton(baseTypes.typeBaseType)
|
| - , this.dynamicType = const UnknownConcreteType()
|
| - , this.nullType = singleton(const NullBaseType())
|
| - , this.nonNullEmptyType = new ConcreteType.empty(
|
| - inferrer.compiler.maxConcreteTypeSize, baseTypes)
|
| - // TODO(polux): have better types here
|
| - , this._uint31Type = singleton(baseTypes.intBaseType)
|
| - , this._uint32Type = singleton(baseTypes.intBaseType)
|
| - , this._positiveIntType = singleton(baseTypes.intBaseType);
|
| -
|
| - factory ConcreteTypeSystem(ConcreteTypesInferrer inferrer) {
|
| - Compiler compiler = inferrer.compiler;
|
| - BaseTypes baseTypes = new BaseTypes(compiler);
|
| - return new ConcreteTypeSystem.internal(
|
| - inferrer,
|
| - baseTypes,
|
| - (BaseType baseType) => new ConcreteType.singleton(
|
| - compiler.maxConcreteTypeSize, baseTypes, baseType));
|
| - }
|
| -
|
| - @override
|
| - ConcreteType get intType {
|
| - inferrer.augmentSeenClasses(compiler.backend.intImplementation);
|
| - return _intType;
|
| - }
|
| -
|
| - @override
|
| - ConcreteType get uint31Type {
|
| - inferrer.augmentSeenClasses(compiler.backend.uint31Implementation);
|
| - return _uint31Type;
|
| - }
|
| -
|
| - @override
|
| - ConcreteType get uint32Type {
|
| - inferrer.augmentSeenClasses(compiler.backend.uint32Implementation);
|
| - return _uint32Type;
|
| - }
|
| -
|
| - @override
|
| - ConcreteType get positiveIntType {
|
| - inferrer.augmentSeenClasses(compiler.backend.positiveIntImplementation);
|
| - return _positiveIntType;
|
| - }
|
| -
|
| - @override
|
| - ConcreteType get doubleType {
|
| - inferrer.augmentSeenClasses(compiler.backend.doubleImplementation);
|
| - return _doubleType;
|
| - }
|
| -
|
| - @override
|
| - ConcreteType get numType {
|
| - inferrer.augmentSeenClasses(compiler.backend.numImplementation);
|
| - return _numType;
|
| - }
|
| -
|
| - @override
|
| - ConcreteType get boolType {
|
| - inferrer.augmentSeenClasses(compiler.backend.boolImplementation);
|
| - return _boolType;
|
| - }
|
| -
|
| - @override
|
| - ConcreteType get functionType {
|
| - inferrer.augmentSeenClasses(compiler.backend.functionImplementation);
|
| - return _functionType;
|
| - }
|
| -
|
| - @override
|
| - ConcreteType get listType {
|
| - inferrer.augmentSeenClasses(compiler.backend.listImplementation);
|
| - return _listType;
|
| - }
|
| -
|
| - @override
|
| - ConcreteType get constListType {
|
| - inferrer.augmentSeenClasses(compiler.backend.constListImplementation);
|
| - return _constListType;
|
| - }
|
| -
|
| - @override
|
| - ConcreteType get fixedListType {
|
| - inferrer.augmentSeenClasses(compiler.backend.fixedListImplementation);
|
| - return _fixedListType;
|
| - }
|
| -
|
| - @override
|
| - ConcreteType get growableListType {
|
| - inferrer.augmentSeenClasses(compiler.backend.growableListImplementation);
|
| - return _growableListType;
|
| - }
|
| -
|
| - @override
|
| - ConcreteType get mapType {
|
| - inferrer.augmentSeenClasses(compiler.backend.mapImplementation);
|
| - return _mapType;
|
| - }
|
| -
|
| - @override
|
| - ConcreteType get constMapType {
|
| - inferrer.augmentSeenClasses(compiler.backend.constMapImplementation);
|
| - return _constMapType;
|
| - }
|
| -
|
| - @override
|
| - ConcreteType get stringType {
|
| - inferrer.augmentSeenClasses(compiler.backend.stringImplementation);
|
| - return _stringType;
|
| - }
|
| -
|
| - @override
|
| - ConcreteType stringLiteralType(_) {
|
| - inferrer.augmentSeenClasses(compiler.backend.stringImplementation);
|
| - return _stringType;
|
| - }
|
| -
|
| - /**
|
| - * Returns the [TypeMask] representation of [baseType].
|
| - */
|
| - TypeMask baseTypeToTypeMask(BaseType baseType) {
|
| - if (baseType.isUnknown()) {
|
| - return const DynamicTypeMask();
|
| - } else if (baseType.isNull()) {
|
| - return new TypeMask.empty();
|
| - } else {
|
| - ClassBaseType classBaseType = baseType;
|
| - final element = classBaseType.element;
|
| - assert(element != null);
|
| - if (element == compiler.backend.numImplementation) {
|
| - return new TypeMask.nonNullSubclass(compiler.backend.numImplementation,
|
| - compiler.world);
|
| - } else if (element == compiler.backend.intImplementation) {
|
| - return new TypeMask.nonNullSubclass(compiler.backend.intImplementation,
|
| - compiler.world);
|
| - } else {
|
| - return new TypeMask.nonNullExact(element.declaration, compiler.world);
|
| - }
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Returns the [TypeMask] representation of [concreteType].
|
| - */
|
| - TypeMask concreteTypeToTypeMask(ConcreteType concreteType) {
|
| - if (concreteType == null) return null;
|
| - TypeMask typeMask = new TypeMask.nonNullEmpty();
|
| - for (BaseType baseType in concreteType.baseTypes) {
|
| - TypeMask baseMask = baseTypeToTypeMask(baseType);
|
| - if (baseMask == const DynamicTypeMask()) return baseMask;
|
| - typeMask = typeMask.union(baseMask, compiler.world);
|
| - }
|
| - return typeMask;
|
| - }
|
| -
|
| - @override
|
| - ConcreteType addPhiInput(Local variable,
|
| - ConcreteType phiType,
|
| - ConcreteType newType) {
|
| - return computeLUB(phiType, newType);
|
| - }
|
| -
|
| - @override
|
| - ConcreteType allocateDiamondPhi(ConcreteType firstInput,
|
| - ConcreteType secondInput) {
|
| - return computeLUB(firstInput, secondInput);
|
| - }
|
| -
|
| - @override
|
| - ConcreteType allocatePhi(Node node, Local variable, ConcreteType inputType) {
|
| - return inputType;
|
| - }
|
| -
|
| - @override
|
| - ConcreteType allocateLoopPhi(Node node, Local variable,
|
| - ConcreteType inputType) {
|
| - return inputType;
|
| - }
|
| -
|
| - @override
|
| - ConcreteType computeLUB(ConcreteType firstType, ConcreteType secondType) {
|
| - if (firstType == null) {
|
| - return secondType;
|
| - } else if (secondType == null) {
|
| - return firstType;
|
| - } else {
|
| - return firstType.union(secondType);
|
| - }
|
| - }
|
| -
|
| - // Implementation Inspired by
|
| - // type_graph_inferrer.TypeInformationSystem.narrowType
|
| - @override
|
| - ConcreteType narrowType(ConcreteType type,
|
| - DartType annotation,
|
| - {bool isNullable: true}) {
|
| - if (annotation.treatAsDynamic) return type;
|
| - if (annotation.isVoid) return nullType;
|
| - if (annotation.element == compiler.objectClass) return type;
|
| - ConcreteType otherType;
|
| - if (annotation.isTypedef || annotation.isFunctionType) {
|
| - otherType = functionType;
|
| - } else if (annotation.isTypeVariable) {
|
| - // TODO(polux): Narrow to bound.
|
| - return type;
|
| - } else {
|
| - assert(annotation.isInterfaceType);
|
| - otherType = nonNullSubtype(annotation.element);
|
| - }
|
| - if (isNullable) otherType = otherType.union(nullType);
|
| - if (type == null) return otherType;
|
| - return type.intersection(otherType);
|
| - }
|
| -
|
| - @override
|
| - Selector newTypedSelector(ConcreteType receiver, Selector selector) {
|
| - return new TypedSelector(concreteTypeToTypeMask(receiver), selector,
|
| - compiler.world);
|
| - }
|
| -
|
| - @override
|
| - ConcreteType nonNullEmpty() {
|
| - return nonNullEmptyType;
|
| - }
|
| -
|
| - @override
|
| - ConcreteType nonNullExact(ClassElement cls) {
|
| - return nonNullSubtype(cls);
|
| - }
|
| -
|
| - /**
|
| - * Helper method for [nonNullSubtype] and [nonNullSubclass].
|
| - */
|
| - ConcreteType nonNullSubX(ClassElement cls,
|
| - Iterable<ClassElement> extractor(ClassElement cls)) {
|
| - if (cls == compiler.objectClass) {
|
| - return dynamicType;
|
| - }
|
| - ConcreteType result = nonNullEmptyType;
|
| - void registerClass(ClassElement element) {
|
| - if (!element.isAbstract) {
|
| - result = result.union(
|
| - new ConcreteType.singleton(compiler.maxConcreteTypeSize,
|
| - baseTypes,
|
| - new ClassBaseType(element)));
|
| - inferrer.augmentSeenClasses(element);
|
| - }
|
| - }
|
| - registerClass(cls);
|
| - Iterable<ClassElement> subtypes = extractor(cls);
|
| - subtypes.forEach(registerClass);
|
| - return result;
|
| - }
|
| -
|
| - @override
|
| - ConcreteType nonNullSubclass(ClassElement cls) {
|
| - return nonNullSubX(cls, compiler.world.subclassesOf);
|
| - }
|
| -
|
| - @override
|
| - ConcreteType nonNullSubtype(ClassElement cls) {
|
| - return nonNullSubX(cls, compiler.world.subtypesOf);
|
| - }
|
| -
|
| - @override
|
| - ConcreteType simplifyPhi(Node node,
|
| - Local variable,
|
| - ConcreteType phiType) {
|
| - return phiType;
|
| - }
|
| -
|
| - @override
|
| - bool selectorNeedsUpdate(ConcreteType type, Selector selector) {
|
| - return concreteTypeToTypeMask(type) != selector.mask;
|
| - }
|
| -
|
| - @override
|
| - ConcreteType refineReceiver(Selector selector, ConcreteType receiverType) {
|
| - return receiverType.refine(selector, compiler);
|
| - }
|
| -
|
| - @override
|
| - bool isNull(ConcreteType type) {
|
| - return (type.baseTypes.length == 1) && (type.baseTypes.first.isNull());
|
| - }
|
| -
|
| - @override
|
| - ConcreteType allocateClosure(Node node, Element element) {
|
| - // TODO(polux): register closure here instead of in visitor?
|
| - return functionType;
|
| - }
|
| -
|
| - @override
|
| - ConcreteType allocateList(ConcreteType type,
|
| - Node node,
|
| - Element enclosing,
|
| - [ConcreteType elementType, int length]) {
|
| - if (elementType != null) {
|
| - inferrer.augmentListElementType(elementType);
|
| - }
|
| - return type;
|
| - }
|
| -
|
| - @override
|
| - ConcreteType allocateMap(ConcreteType type,
|
| - Node node,
|
| - Element element,
|
| - [List<ConcreteType> keyTypes,
|
| - List<ConcreteType> valueTypes]) {
|
| - // TODO(polux): treat maps the same way we treat lists
|
| - return type;
|
| - }
|
| -
|
| - @override
|
| - ConcreteType getConcreteTypeFor(TypeMask mask) {
|
| - if (mask.isUnion) {
|
| - UnionTypeMask union = mask;
|
| - return union.disjointMasks.fold(
|
| - nonNullEmptyType,
|
| - (type1, type2) => type1.union(getConcreteTypeFor(type2)));
|
| - } else {
|
| - FlatTypeMask flat = mask;
|
| - ConcreteType result;
|
| - if (flat.isEmpty) {
|
| - result = nonNullEmptyType;
|
| - } else if (flat.isExact) {
|
| - result = nonNullExact(flat.base);
|
| - } else if (flat.isSubclass) {
|
| - result = nonNullSubclass(flat.base);
|
| - } else if (flat.isSubtype) {
|
| - result = nonNullSubtype(flat.base);
|
| - } else {
|
| - throw new ArgumentError("unexpected mask");
|
| - }
|
| - return flat.isNullable ? result.union(nullType) : result;
|
| - }
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * The cartesian product of concrete types: an iterable of
|
| - * [ConcreteTypesEnvironment]s.
|
| - */
|
| -class ConcreteTypeCartesianProduct
|
| - extends IterableBase<ConcreteTypesEnvironment> {
|
| - final ConcreteTypesInferrer inferrer;
|
| - final ClassElement typeOfThis;
|
| - final Map<Element, ConcreteType> concreteTypes;
|
| - ConcreteTypeCartesianProduct(this.inferrer, this.typeOfThis,
|
| - this.concreteTypes);
|
| - Iterator get iterator => concreteTypes.isEmpty
|
| - ? [new ConcreteTypesEnvironment(typeOfThis)].iterator
|
| - : new ConcreteTypeCartesianProductIterator(inferrer, typeOfThis,
|
| - concreteTypes);
|
| - String toString() => this.toList().toString();
|
| -}
|
| -
|
| -/**
|
| - * An helper class for [ConcreteTypeCartesianProduct].
|
| - */
|
| -class ConcreteTypeCartesianProductIterator
|
| - implements Iterator<ConcreteTypesEnvironment> {
|
| - final ConcreteTypesInferrer inferrer;
|
| - final ClassElement classOfThis;
|
| - final Map<Element, ConcreteType> concreteTypes;
|
| - final Map<Element, BaseType> nextValues;
|
| - final Map<Element, Iterator> state;
|
| - int size = 1;
|
| - int counter = 0;
|
| - ConcreteTypesEnvironment _current;
|
| -
|
| - ConcreteTypeCartesianProductIterator(this.inferrer, this.classOfThis,
|
| - Map<Element, ConcreteType> concreteTypes)
|
| - : this.concreteTypes = concreteTypes,
|
| - nextValues = new Map<Element, BaseType>(),
|
| - state = new Map<Element, Iterator>() {
|
| - if (concreteTypes.isEmpty) {
|
| - size = 0;
|
| - return;
|
| - }
|
| - for (final e in concreteTypes.keys) {
|
| - final baseTypes = concreteTypes[e].baseTypes;
|
| - size *= baseTypes.length;
|
| - }
|
| - }
|
| -
|
| - ConcreteTypesEnvironment get current => _current;
|
| -
|
| - ConcreteTypesEnvironment takeSnapshot() {
|
| - Map<Element, ConcreteType> result = new Map<Element, ConcreteType>();
|
| - nextValues.forEach((k, v) {
|
| - result[k] = inferrer.singletonConcreteType(v);
|
| - });
|
| - return new ConcreteTypesEnvironment.of(result, classOfThis);
|
| - }
|
| -
|
| - bool moveNext() {
|
| - if (counter >= size) {
|
| - _current = null;
|
| - return false;
|
| - }
|
| - Element keyToIncrement = null;
|
| - for (final key in concreteTypes.keys) {
|
| - final iterator = state[key];
|
| - if (iterator != null && iterator.moveNext()) {
|
| - nextValues[key] = state[key].current;
|
| - break;
|
| - }
|
| - Iterator newIterator = concreteTypes[key].baseTypes.iterator;
|
| - state[key] = newIterator;
|
| - newIterator.moveNext();
|
| - nextValues[key] = newIterator.current;
|
| - }
|
| - counter++;
|
| - _current = takeSnapshot();
|
| - return true;
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * [BaseType] Constants.
|
| - */
|
| -class BaseTypes {
|
| - final ClassBaseType intBaseType;
|
| - final ClassBaseType doubleBaseType;
|
| - final ClassBaseType numBaseType;
|
| - final ClassBaseType boolBaseType;
|
| - final ClassBaseType stringBaseType;
|
| - final ClassBaseType listBaseType;
|
| - final ClassBaseType growableListBaseType;
|
| - final ClassBaseType fixedListBaseType;
|
| - final ClassBaseType constListBaseType;
|
| - final ClassBaseType mapBaseType;
|
| - final ClassBaseType constMapBaseType;
|
| - final ClassBaseType objectBaseType;
|
| - final ClassBaseType typeBaseType;
|
| - final ClassBaseType functionBaseType;
|
| - final ClassBaseType uint31Type;
|
| - final ClassBaseType uint32Type;
|
| - final ClassBaseType positiveIntType;
|
| -
|
| - BaseTypes(Compiler compiler) :
|
| - intBaseType = new ClassBaseType(compiler.backend.intImplementation),
|
| - doubleBaseType = new ClassBaseType(compiler.backend.doubleImplementation),
|
| - numBaseType = new ClassBaseType(compiler.backend.numImplementation),
|
| - boolBaseType = new ClassBaseType(compiler.backend.boolImplementation),
|
| - stringBaseType = new ClassBaseType(compiler.backend.stringImplementation),
|
| - listBaseType = new ClassBaseType(compiler.backend.listImplementation),
|
| - growableListBaseType =
|
| - new ClassBaseType(compiler.backend.growableListImplementation),
|
| - fixedListBaseType =
|
| - new ClassBaseType(compiler.backend.fixedListImplementation),
|
| - constListBaseType =
|
| - new ClassBaseType(compiler.backend.constListImplementation),
|
| - mapBaseType = new ClassBaseType(compiler.backend.mapImplementation),
|
| - constMapBaseType =
|
| - new ClassBaseType(compiler.backend.constMapImplementation),
|
| - objectBaseType = new ClassBaseType(compiler.objectClass),
|
| - typeBaseType = new ClassBaseType(compiler.backend.typeImplementation),
|
| - functionBaseType =
|
| - new ClassBaseType(compiler.backend.functionImplementation),
|
| - uint31Type = new ClassBaseType(compiler.backend.uint31Implementation),
|
| - uint32Type = new ClassBaseType(compiler.backend.uint32Implementation),
|
| - positiveIntType =
|
| - new ClassBaseType(compiler.backend.positiveIntImplementation);
|
| -}
|
| -
|
| -/**
|
| - * An immutable mapping from method arguments to [ConcreteTypes].
|
| - */
|
| -class ConcreteTypesEnvironment {
|
| - final Map<Element, ConcreteType> environment;
|
| - final ClassElement classOfThis;
|
| -
|
| - ConcreteTypesEnvironment([this.classOfThis]) :
|
| - environment = new Map<Element, ConcreteType>();
|
| - ConcreteTypesEnvironment.of(this.environment, this.classOfThis);
|
| -
|
| - ConcreteType lookupType(Element element) => environment[element];
|
| -
|
| - bool operator ==(ConcreteTypesEnvironment other) {
|
| - if (other is! ConcreteTypesEnvironment) return false;
|
| - if (classOfThis != other.classOfThis) return false;
|
| - if (environment.length != other.environment.length) return false;
|
| - for (Element key in environment.keys) {
|
| - if (!other.environment.containsKey(key)
|
| - || (environment[key] != other.environment[key])) {
|
| - return false;
|
| - }
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - int get hashCode {
|
| - int result = (classOfThis != null) ? classOfThis.hashCode : 1;
|
| - environment.forEach((element, concreteType) {
|
| - result = 31 * (31 * result + element.hashCode) + concreteType.hashCode;
|
| - });
|
| - return result;
|
| - }
|
| -
|
| - String toString() => "{ this: $classOfThis, env: $environment }";
|
| -}
|
| -
|
| -class ClosureEnvironment {
|
| - ConcreteType thisType;
|
| - final LocalsHandler locals;
|
| -
|
| - ClosureEnvironment(this.thisType, this.locals);
|
| -
|
| - bool mergeLocals(LocalsHandler newLocals) {
|
| - assert((locals == null) == (newLocals == null));
|
| - return (locals != null) ? locals.mergeAll([newLocals]) : false;
|
| - }
|
| -
|
| - /// Returns true if changed.
|
| - bool merge(ConcreteType thisType, LocalsHandler locals) {
|
| - ConcreteType oldThisType = this.thisType;
|
| - if (this.thisType == null) {
|
| - this.thisType = thisType;
|
| - } else if (thisType != null) {
|
| - this.thisType = this.thisType.union(thisType);
|
| - }
|
| - return mergeLocals(locals) || (this.thisType != oldThisType);
|
| - }
|
| -
|
| - toString() => "ClosureEnvironment { thisType = $thisType, locals = ... }";
|
| -}
|
| -
|
| -/**
|
| - * A set of encoutered closures.
|
| - */
|
| -class Closures {
|
| - final Compiler compiler;
|
| - final Map<FunctionElement, ClosureEnvironment> closures =
|
| - new Map<FunctionElement, ClosureEnvironment>();
|
| -
|
| - Closures(this.compiler);
|
| -
|
| - /// Returns true if the environment of the closure has changed.
|
| - bool put(FunctionElement closure,
|
| - ConcreteType typeOfThis,
|
| - LocalsHandler locals) {
|
| - ClosureEnvironment oldEnvironent = closures[closure];
|
| - if (oldEnvironent == null) {
|
| - closures[closure] = new ClosureEnvironment(typeOfThis, locals);
|
| - return true;
|
| - } else {
|
| - return oldEnvironent.merge(typeOfThis, locals);
|
| - }
|
| - }
|
| -
|
| - ClosureEnvironment getEnvironmentOrNull(FunctionElement function) {
|
| - return closures[function];
|
| - }
|
| -
|
| - Iterable<FunctionElement> get functionElements => closures.keys;
|
| -
|
| - bool contains(FunctionElement function) => closures.containsKey(function);
|
| -
|
| - String toString() => closures.toString();
|
| -}
|
| -
|
| -/**
|
| - * A work item for the type inference queue.
|
| - */
|
| -class InferenceWorkItem {
|
| - Element method;
|
| - ConcreteTypesEnvironment environment;
|
| - InferenceWorkItem(this.method, this.environment);
|
| -
|
| - toString() => "{ method = $method, environment = $environment }";
|
| -
|
| - bool operator ==(other) {
|
| - return (other is InferenceWorkItem)
|
| - && method == other.method
|
| - && environment == other.environment;
|
| - }
|
| -
|
| - int get hashCode => 31 * method.hashCode + environment.hashCode;
|
| -}
|
| -
|
| -/**
|
| - * A sentinel type mask class representing the dynamicType. It is absorbing
|
| - * for [:ConcreteTypesEnvironment.typeMaskUnion:].
|
| - */
|
| -class DynamicTypeMask implements TypeMask {
|
| - const DynamicTypeMask();
|
| -
|
| - String toString() => 'sentinel type mask';
|
| -
|
| - TypeMask nullable() {
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - TypeMask nonNullable() {
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - bool get isEmpty {
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - bool get isNullable {
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - bool get isExact {
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - bool get isUnion {
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - bool get isContainer {
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - bool get isMap {
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - bool get isDictionary {
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - bool get isForwarding {
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - bool get isValue {
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - bool containsOnlyInt(ClassWorld classWorld) {
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - bool containsOnlyDouble(ClassWorld classWorld) {
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - bool containsOnlyNum(ClassWorld classWorld) {
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - bool containsOnlyBool(ClassWorld classWorld) {
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - bool containsOnlyString(ClassWorld classWorld) {
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - bool containsOnly(ClassElement element) {
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - bool satisfies(ClassElement cls, ClassWorld classWorld) {
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - bool contains(ClassElement type, ClassWorld classWorld) {
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - bool containsAll(ClassWorld classWorld) {
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - ClassElement singleClass(ClassWorld classWorld) {
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - TypeMask union(TypeMask other, ClassWorld classWorld) {
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - TypeMask intersection(TypeMask other, ClassWorld classWorld) {
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - bool canHit(Element element, Selector selector, ClassWorld classWorld) {
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - Element locateSingleElement(Selector selector, Compiler compiler) {
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - bool needsNoSuchMethodHandling(Selector selector, ClassWorld classWorld) {
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - bool isInMask(TypeMask other, ClassWorld classWorld) {
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - bool containsMask(TypeMask other, ClassWorld classWorld) {
|
| - throw new UnsupportedError("");
|
| - }
|
| -}
|
| -
|
| -class WorkQueue {
|
| - final Queue<InferenceWorkItem> queue = new Queue<InferenceWorkItem>();
|
| -
|
| - void add(InferenceWorkItem workItem) {
|
| - if (!queue.contains(workItem)) {
|
| - queue.addLast(workItem);
|
| - }
|
| - }
|
| -
|
| - InferenceWorkItem remove() {
|
| - return queue.removeFirst();
|
| - }
|
| -
|
| - bool get isEmpty => queue.isEmpty;
|
| -}
|
| -
|
| -/**
|
| - * A task which conservatively infers a [ConcreteType] for each sub expression
|
| - * of the program. The entry point is [analyzeMain].
|
| - */
|
| -class ConcreteTypesInferrer
|
| - extends InferrerEngine<ConcreteType, ConcreteTypeSystem>
|
| - implements TypesInferrer {
|
| -
|
| - final String name = "Type inferrer";
|
| -
|
| - /**
|
| - * When true, the string literal [:"__dynamic_for_test":] is inferred to
|
| - * have the unknown type.
|
| - */
|
| - // TODO(polux): get rid of this hack once we have a natural way of inferring
|
| - // the unknown type.
|
| - bool testMode = false;
|
| -
|
| - // --- constants ---
|
| -
|
| - /**
|
| - * Constants representing builtin base types. Initialized by [initialize]
|
| - * and not by the constructor because the compiler elements are not yet
|
| - * populated.
|
| - */
|
| - BaseTypes baseTypes;
|
| -
|
| - /** The associated type system */
|
| - ConcreteTypeSystem types;
|
| -
|
| - /**
|
| - * Constant representing [:ConcreteList#[]:] where [:ConcreteList:] is the
|
| - * concrete implementation of lists for the selected backend.
|
| - */
|
| - FunctionElement listIndex;
|
| -
|
| - /**
|
| - * Constant representing [:ConcreteList#[]=:] where [:ConcreteList:] is the
|
| - * concrete implementation of lists for the selected backend.
|
| - */
|
| - FunctionElement listIndexSet;
|
| -
|
| - /**
|
| - * Constant representing [:ConcreteList#add:] where [:ConcreteList:] is the
|
| - * concrete implementation of lists for the selected backend.
|
| - */
|
| - FunctionElement listAdd;
|
| -
|
| - /**
|
| - * Constant representing [:ConcreteList#removeAt:] where [:ConcreteList:] is
|
| - * the concrete implementation of lists for the selected backend.
|
| - */
|
| - FunctionElement listRemoveAt;
|
| -
|
| - /**
|
| - * Constant representing [:ConcreteList#insert:] where [:ConcreteList:] is
|
| - * the concrete implementation of lists for the selected backend.
|
| - */
|
| - FunctionElement listInsert;
|
| -
|
| - /**
|
| - * Constant representing [:ConcreteList#removeLast:] where [:ConcreteList:] is
|
| - * the concrete implementation of lists for the selected backend.
|
| - */
|
| - FunctionElement listRemoveLast;
|
| -
|
| - /** Constant representing [:List():]. */
|
| - FunctionElement listConstructor;
|
| -
|
| - /** The unknown concrete type */
|
| - final ConcreteType unknownConcreteType;
|
| -
|
| - /** The empty concrete type */
|
| - ConcreteType emptyConcreteType;
|
| -
|
| - /** The null concrete type */
|
| - ConcreteType nullConcreteType;
|
| -
|
| - // --- state updated by the inference ---
|
| -
|
| - /**
|
| - * A map from (function x argument base types) to their inferred concrete
|
| - * type. Another way of seeing [methodToTemplates] is as a map from
|
| - * [FunctionElement]s to "templates" in the sense of "The Cartesian Product
|
| - * Algorithm - Simple and Precise Type Inference of Parametric Polymorphism"
|
| - * by Ole Agesen.
|
| - */
|
| - // TODO(polux): build a better abstraction, like Closures
|
| - final Map<FunctionElement, Map<ConcreteTypesEnvironment, ConcreteType>>
|
| - methodToTemplates;
|
| -
|
| - /** The set of encountered closures. */
|
| - final Closures closures;
|
| -
|
| - /** A map from expressions to their inferred concrete types. */
|
| - final Map<Node, ConcreteType> inferredTypes;
|
| -
|
| - /** A map from fields to their inferred concrete types. */
|
| - final Map<Element, ConcreteType> inferredFieldTypes;
|
| -
|
| - /**
|
| - * [:callers[f]:] is the list of [:f:]'s possible callers or fields
|
| - * whose initialization is a call to [:f:].
|
| - */
|
| - final Map<FunctionElement, Set<Element>> callers;
|
| -
|
| - /**
|
| - * [:readers[field]:] is the list of [:field:]'s possible readers or fields
|
| - * whose initialization is a read of [:field:].
|
| - */
|
| - final Map<Element, Set<Element>> fieldReaders;
|
| -
|
| - /**
|
| - * [:readers[local]:] is the list of [:local:]'s possible readers.
|
| - */
|
| - final Map<Local, Set<FunctionElement>> capturedLocalsReaders;
|
| -
|
| - /// The set of classes encountered so far.
|
| - final Set<ClassElement> seenClasses;
|
| -
|
| - /**
|
| - * A map from selector names to callers of methods with this name on objects
|
| - * of unknown inferred type.
|
| - */
|
| - final Map<String, Set<FunctionElement>> dynamicCallers;
|
| -
|
| - /** The inferred type of elements stored in Lists. */
|
| - ConcreteType listElementType;
|
| -
|
| - /**
|
| - * A map from parameters to their inferred concrete types. It plays no role
|
| - * in the analysis, it is write only.
|
| - */
|
| - final Map<VariableElement, ConcreteType> inferredParameterTypes;
|
| -
|
| - /**
|
| - * A map from selectors to their inferred type masks, indexed by the mask
|
| - * of the receiver. It plays no role in the analysis, it is write only.
|
| - */
|
| - final Map<Selector, Map<TypeMask, TypeMask>> inferredSelectorTypes;
|
| -
|
| - /** The work queue consumed by [analyzeMain]. */
|
| - final WorkQueue workQueue;
|
| -
|
| - /** The item being worked on. */
|
| - InferenceWorkItem currentWorkItem;
|
| -
|
| - ConcreteTypesInferrer(Compiler compiler)
|
| - : methodToTemplates = new Map<FunctionElement,
|
| - Map<ConcreteTypesEnvironment, ConcreteType>>(),
|
| - closures = new Closures(compiler),
|
| - inferredTypes = new Map<Node, ConcreteType>(),
|
| - inferredFieldTypes = new Map<Element, ConcreteType>(),
|
| - inferredParameterTypes = new Map<VariableElement, ConcreteType>(),
|
| - workQueue = new WorkQueue(),
|
| - callers = new Map<FunctionElement, Set<Element>>(),
|
| - fieldReaders = new Map<Element, Set<Element>>(),
|
| - capturedLocalsReaders = new Map<Local, Set<FunctionElement>>(),
|
| - seenClasses = new Set<ClassElement>(),
|
| - dynamicCallers = new Map<String, Set<FunctionElement>>(),
|
| - inferredSelectorTypes = new Map<Selector, Map<TypeMask, TypeMask>>(),
|
| - unknownConcreteType = new ConcreteType.unknown(),
|
| - super(compiler, null);
|
| -
|
| - /* Initialization code that cannot be run in the constructor because it
|
| - * requires the compiler's elements to be populated.
|
| - */
|
| - void initialize() {
|
| - baseTypes = new BaseTypes(compiler);
|
| - types = new ConcreteTypeSystem(this);
|
| - ClassElement jsArrayClass = baseTypes.listBaseType.element;
|
| - listIndex = jsArrayClass.lookupMember('[]');
|
| - listIndexSet = jsArrayClass.lookupMember('[]=');
|
| - listAdd = jsArrayClass.lookupMember('add');
|
| - listRemoveAt = jsArrayClass.lookupMember('removeAt');
|
| - listInsert = jsArrayClass.lookupMember('insert');
|
| - listRemoveLast =
|
| - jsArrayClass.lookupMember('removeLast');
|
| - List<String> typePreservingOps = const ['+', '-', '*'];
|
| - listConstructor =
|
| - compiler.listClass.lookupConstructor(
|
| - new Selector.callConstructor(
|
| - '',
|
| - compiler.listClass.library)).implementation;
|
| - emptyConcreteType = new ConcreteType.empty(compiler.maxConcreteTypeSize,
|
| - baseTypes);
|
| - nullConcreteType = singletonConcreteType(const NullBaseType());
|
| - listElementType = emptyConcreteType;
|
| - }
|
| -
|
| - // --- utility methods ---
|
| -
|
| - /** Creates a singleton concrete type containing [baseType]. */
|
| - ConcreteType singletonConcreteType(BaseType baseType) {
|
| - return new ConcreteType.singleton(compiler.maxConcreteTypeSize, baseTypes,
|
| - baseType);
|
| - }
|
| -
|
| - /**
|
| - * Computes the union of [mask1] and [mask2] where [mask1] and [mask2] are
|
| - * possibly equal to [: DynamicTypeMask.instance :].
|
| - */
|
| - TypeMask typeMaskUnion(TypeMask mask1, TypeMask mask2) {
|
| - if (mask1 == const DynamicTypeMask() || mask2 == const DynamicTypeMask()) {
|
| - return const DynamicTypeMask();
|
| - }
|
| - return mask1.union(mask2, compiler.world);
|
| - }
|
| -
|
| - /**
|
| - * Returns all the members matching [selector].
|
| - */
|
| - Set<Element> getMembersBySelector(Selector selector) {
|
| - // TODO(polux): memoize?
|
| - Set<Element> result = new Set<Element>();
|
| - for (ClassElement cls in seenClasses) {
|
| - Element elem = cls.lookupSelector(selector);
|
| - if (elem != null) {
|
| - result.add(elem.implementation);
|
| - }
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - /**
|
| - * Returns all the subtypes of [cls], [cls] included.
|
| - */
|
| - Set<ClassElement> getReflexiveSubtypesOf(ClassElement cls) {
|
| - // TODO(polux): memoize?
|
| - Set<ClassElement> result = new Set<ClassElement>()..add(cls);
|
| - for (ClassElement candidate in seenClasses) {
|
| - if (compiler.world.isSubtypeOf(candidate, cls)) {
|
| - result.add(candidate);
|
| - }
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - /**
|
| - * Sets the concrete type associated to [node] to the union of the inferred
|
| - * concrete type so far and of [type].
|
| - */
|
| - void augmentInferredType(Node node, ConcreteType type) {
|
| - ConcreteType currentType = inferredTypes[node];
|
| - inferredTypes[node] =
|
| - (currentType == null) ? type : currentType.union(type);
|
| - }
|
| -
|
| - /**
|
| - * Sets the concrete type associated to [selector] to the union of the
|
| - * inferred concrete type so far and of [returnType].
|
| - *
|
| - * Precondition: [:(typeOfThis != null) && (returnType != null):]
|
| - */
|
| - void augmentInferredSelectorType(Selector selector, TypeMask typeOfThis,
|
| - TypeMask returnType) {
|
| - assert(returnType != null);
|
| - assert(typeOfThis != null);
|
| -
|
| - selector = selector.asUntyped;
|
| - Map<TypeMask, TypeMask> currentMap = inferredSelectorTypes.putIfAbsent(
|
| - selector, () => new Map<TypeMask, TypeMask>());
|
| - TypeMask currentReturnType = currentMap[typeOfThis];
|
| - currentMap[typeOfThis] = (currentReturnType == null)
|
| - ? returnType
|
| - : typeMaskUnion(currentReturnType, returnType);
|
| - }
|
| -
|
| - /**
|
| - * Returns the current inferred concrete type of [field].
|
| - */
|
| - ConcreteType getFieldType(Selector selector, Element field) {
|
| - ensureFieldInitialized(field);
|
| - ConcreteType result = inferredFieldTypes[field];
|
| - result = (result == null) ? emptyConcreteType : result;
|
| - if (selector != null) {
|
| - Element enclosing = field.enclosingElement;
|
| - if (enclosing.isClass) {
|
| - ClassElement cls = enclosing;
|
| - TypeMask receiverMask = new TypeMask.exact(cls.declaration, classWorld);
|
| - TypeMask resultMask = types.concreteTypeToTypeMask(result);
|
| - augmentInferredSelectorType(selector, receiverMask, resultMask);
|
| - }
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - /**
|
| - * Sets the concrete type associated to [field] to the union of the inferred
|
| - * concrete type so far and of [type].
|
| - */
|
| - void augmentFieldType(Element field, ConcreteType type) {
|
| - ensureFieldInitialized(field);
|
| - ConcreteType oldType = inferredFieldTypes[field];
|
| - ConcreteType newType = (oldType != null) ? oldType.union(type) : type;
|
| - if (oldType != newType) {
|
| - inferredFieldTypes[field] = newType;
|
| - invalidateReaders(field);
|
| - }
|
| - }
|
| -
|
| - /** Augment the inferred type of elements stored in Lists. */
|
| - void augmentListElementType(ConcreteType type) {
|
| - ConcreteType newType = listElementType.union(type);
|
| - if (newType != listElementType) {
|
| - invalidateCallers(listIndex);
|
| - listElementType = newType;
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Sets the concrete type associated to [parameter] to the union of the
|
| - * inferred concrete type so far and of [type].
|
| - */
|
| - void augmentParameterType(VariableElement parameter, ConcreteType type) {
|
| - ConcreteType oldType = inferredParameterTypes[parameter];
|
| - inferredParameterTypes[parameter] =
|
| - (oldType == null) ? type : oldType.union(type);
|
| - }
|
| -
|
| - /** Augments the set of classes encountered so far. */
|
| - void augmentSeenClasses(ClassElement cls) {
|
| - if (!seenClasses.contains(cls)) {
|
| - seenClasses.add(cls);
|
| - cls.forEachMember((_, Element member) {
|
| - Set<FunctionElement> functions = dynamicCallers[member.name];
|
| - if (functions != null) {
|
| - functions.forEach(invalidate);
|
| - }
|
| - }, includeSuperAndInjectedMembers: true);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Add [caller] to the set of [callee]'s callers.
|
| - */
|
| - void addCaller(FunctionElement callee, Element caller) {
|
| - callers.putIfAbsent(callee, () => new Set<Element>())
|
| - .add(caller);
|
| - }
|
| -
|
| - /**
|
| - * Add [caller] to the set of [callee]'s dynamic callers.
|
| - */
|
| - void addDynamicCaller(Selector callee, FunctionElement caller) {
|
| - dynamicCallers
|
| - .putIfAbsent(callee.name, () => new Set<FunctionElement>())
|
| - .add(caller);
|
| - }
|
| -
|
| - /**
|
| - * Add [reader] to the set of [field]'s readers.
|
| - */
|
| - void addFieldReader(Element field, Element reader) {
|
| - fieldReaders.putIfAbsent(field, () => new Set<Element>())
|
| - .add(reader);
|
| - }
|
| -
|
| - /**
|
| - * Add [reader] to the set of [local]'s readers.
|
| - */
|
| - void addCapturedLocalReader(Local local, FunctionElement reader) {
|
| - capturedLocalsReaders.putIfAbsent(local, () => new Set<FunctionElement>())
|
| - .add(reader);
|
| - }
|
| -
|
| - /**
|
| - * Add a closure to the set of seen closures. Invalidate callers if
|
| - * the set of locals has changed.
|
| - */
|
| - void addClosure(FunctionElement closure,
|
| - ConcreteType typeOfThis,
|
| - LocalsHandler locals) {
|
| - if (closures.put(closure, typeOfThis, locals)) {
|
| - invalidateCallers(closure);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Invalidate all callers of [function].
|
| - */
|
| - void invalidateCallers(FunctionElement function) {
|
| - Set<Element> methodCallers = callers[function];
|
| - if (methodCallers != null) {
|
| - methodCallers.forEach(invalidate);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Invalidate all reader of [field].
|
| - */
|
| - void invalidateReaders(Element field) {
|
| - Set<Element> readers = fieldReaders[field];
|
| - if (readers != null) {
|
| - readers.forEach(invalidate);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Add all templates of [methodOrField] to the workqueue.
|
| - */
|
| - void invalidate(Element methodOrField) {
|
| - if (methodOrField.isField) {
|
| - workQueue.add(new InferenceWorkItem(
|
| - methodOrField, new ConcreteTypesEnvironment()));
|
| - } else {
|
| - Map<ConcreteTypesEnvironment, ConcreteType> templates =
|
| - methodToTemplates[methodOrField];
|
| - if (templates != null) {
|
| - templates.forEach((environment, _) {
|
| - workQueue.add(
|
| - new InferenceWorkItem(methodOrField, environment));
|
| - });
|
| - }
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Returns the template associated to [function] or create an empty template
|
| - * for [function] return it.
|
| - */
|
| - // TODO(polux): encapsulate this in an abstraction for templates
|
| - Map<ConcreteTypesEnvironment, ConcreteType>
|
| - getTemplatesOrEmpty(FunctionElement function) {
|
| - return methodToTemplates.putIfAbsent(
|
| - function,
|
| - () => new Map<ConcreteTypesEnvironment, ConcreteType>());
|
| - }
|
| -
|
| - // -- methods of types.TypesInferrer (interface with the backend) --
|
| -
|
| - /** Get the inferred concrete type of [node]. */
|
| - @override
|
| - TypeMask getTypeOfNode(Element owner, Node node) {
|
| - TypeMask result = types.concreteTypeToTypeMask(inferredTypes[node]);
|
| - return (result == const DynamicTypeMask()) ? null : result;
|
| - }
|
| -
|
| - /** Get the inferred concrete type of [element]. */
|
| - @override
|
| - TypeMask getTypeOfElement(Element element) {
|
| - final result = types.concreteTypeToTypeMask(typeOfElement(element));
|
| - return (result == const DynamicTypeMask()) ? null : result;
|
| - }
|
| -
|
| - /**
|
| - * Get the inferred concrete return type of [element]. A null return value
|
| - * means "I don't know".
|
| - */
|
| - @override
|
| - TypeMask getReturnTypeOfElement(Element element) {
|
| - assert(element is FunctionElement);
|
| - Map<ConcreteTypesEnvironment, ConcreteType> templates =
|
| - methodToTemplates[element];
|
| - if (templates == null) return null;
|
| - ConcreteType returnType = emptyConcreteType;
|
| - templates.forEach((_, concreteType) {
|
| - returnType = returnType.union(concreteType);
|
| - });
|
| - TypeMask result = types.concreteTypeToTypeMask(returnType);
|
| - return (result == const DynamicTypeMask()) ? null : result;
|
| - }
|
| -
|
| - /**
|
| - * Get the inferred concrete type of [selector]. A null return value means
|
| - * "I don't know".
|
| - */
|
| - @override
|
| - TypeMask getTypeOfSelector(Selector selector) {
|
| - Map<TypeMask, TypeMask> candidates =
|
| - inferredSelectorTypes[selector.asUntyped];
|
| - if (candidates == null) {
|
| - return null;
|
| - }
|
| - TypeMask result = new TypeMask.nonNullEmpty();
|
| - if (selector.mask == null) {
|
| - candidates.forEach((TypeMask receiverType, TypeMask returnType) {
|
| - result = typeMaskUnion(result, returnType);
|
| - });
|
| - } else {
|
| - candidates.forEach((TypeMask receiverType, TypeMask returnType) {
|
| - TypeMask intersection =
|
| - receiverType.intersection(selector.mask, compiler.world);
|
| - if (!intersection.isEmpty || intersection.isNullable) {
|
| - result = typeMaskUnion(result, returnType);
|
| - }
|
| - });
|
| - }
|
| - return result == const DynamicTypeMask() ? null : result;
|
| - }
|
| -
|
| - @override
|
| - void clear() {
|
| - throw new UnsupportedError("clearing is not yet implemented");
|
| - }
|
| -
|
| - @override
|
| - bool isCalledOnce(Element element) {
|
| - // Never called by SimpleTypeInferrer.
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - @override
|
| - bool isFixedArrayCheckedForGrowable(Node node) {
|
| - // Never called by SimpleTypeInferrer.
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - // --- analysis ---
|
| -
|
| - /**
|
| - * Returns the concrete type returned by [function] given arguments of
|
| - * concrete types [argumentsTypes]. If [function] is static then
|
| - * [receiverType] must be null, else [function] must be a member of
|
| - * [receiverType].
|
| - */
|
| - ConcreteType getSendReturnType(Selector selector,
|
| - FunctionElement function,
|
| - ClassElement receiverType,
|
| - ArgumentsTypes<ConcreteType> argumentsTypes) {
|
| - assert(function != null);
|
| -
|
| - ConcreteType result = emptyConcreteType;
|
| - Map<Element, ConcreteType> argumentMap =
|
| - associateArguments(function, argumentsTypes);
|
| - // if the association failed, this send will never occur or will fail
|
| - if (argumentMap == null) {
|
| - return emptyConcreteType;
|
| - }
|
| -
|
| - argumentMap.forEach(augmentParameterType);
|
| - ConcreteTypeCartesianProduct product =
|
| - new ConcreteTypeCartesianProduct(this, receiverType, argumentMap);
|
| - for (ConcreteTypesEnvironment environment in product) {
|
| - result = result.union(
|
| - getMonomorphicSendReturnType(function, environment));
|
| - }
|
| -
|
| - if (selector != null && receiverType != null) {
|
| - // TODO(polux): generalize to any abstract class if we ever handle other
|
| - // abstract classes than num.
|
| - TypeMask receiverMask =
|
| - (receiverType == compiler.backend.numImplementation
|
| - || receiverType == compiler.backend.intImplementation)
|
| - ? new TypeMask.nonNullSubclass(receiverType.declaration,
|
| - compiler.world)
|
| - : new TypeMask.nonNullExact(receiverType.declaration,
|
| - compiler.world);
|
| - TypeMask resultMask = types.concreteTypeToTypeMask(result);
|
| - augmentInferredSelectorType(selector, receiverMask, resultMask);
|
| - }
|
| -
|
| - return result;
|
| - }
|
| -
|
| - /**
|
| - * Given a method signature and a list of concrete types, builds a map from
|
| - * formals to their corresponding concrete types. Returns null if the
|
| - * association is impossible (for instance: too many arguments).
|
| - */
|
| - Map<Element, ConcreteType> associateArguments(
|
| - FunctionElement function,
|
| - ArgumentsTypes<ConcreteType> argumentsTypes) {
|
| - final Map<Element, ConcreteType> result = new Map<Element, ConcreteType>();
|
| - final FunctionSignature signature = function.functionSignature;
|
| -
|
| - // guard 1: too many arguments
|
| - if (argumentsTypes.length > signature.parameterCount) {
|
| - return null;
|
| - }
|
| - // guard 2: not enough arguments
|
| - if (argumentsTypes.positional.length < signature.requiredParameterCount) {
|
| - return null;
|
| - }
|
| - // guard 3: too many positional arguments
|
| - if (signature.optionalParametersAreNamed &&
|
| - argumentsTypes.positional.length > signature.requiredParameterCount) {
|
| - return null;
|
| - }
|
| -
|
| - handleLeftoverOptionalParameter(ParameterElement parameter) {
|
| - Expression initializer = parameter.initializer;
|
| - result[parameter] = (initializer == null)
|
| - ? nullConcreteType
|
| - : analyzeDefaultValue(function, initializer);
|
| - }
|
| -
|
| - final Iterator<ConcreteType> remainingPositionalArguments =
|
| - argumentsTypes.positional.iterator;
|
| - // we attach each positional parameter to its corresponding positional
|
| - // argument
|
| - for (Link<Element> requiredParameters = signature.requiredParameters;
|
| - !requiredParameters.isEmpty;
|
| - requiredParameters = requiredParameters.tail) {
|
| - final Element requiredParameter = requiredParameters.head;
|
| - // we know moveNext() succeeds because of guard 2
|
| - remainingPositionalArguments.moveNext();
|
| - result[requiredParameter] = remainingPositionalArguments.current;
|
| - }
|
| - if (signature.optionalParametersAreNamed) {
|
| - // we build a map out of the remaining named parameters
|
| - Link<Element> remainingOptionalParameters = signature.optionalParameters;
|
| - final Map<String, Element> leftOverNamedParameters =
|
| - new Map<String, Element>();
|
| - for (;
|
| - !remainingOptionalParameters.isEmpty;
|
| - remainingOptionalParameters = remainingOptionalParameters.tail) {
|
| - final Element namedParameter = remainingOptionalParameters.head;
|
| - leftOverNamedParameters[namedParameter.name] = namedParameter;
|
| - }
|
| - // we attach the named arguments to their corresponding optional
|
| - // parameters
|
| - for (String source in argumentsTypes.named.keys) {
|
| - final ConcreteType concreteType = argumentsTypes.named[source];
|
| - final Element namedParameter = leftOverNamedParameters[source];
|
| - // unexisting or already used named parameter
|
| - if (namedParameter == null) return null;
|
| - result[namedParameter] = concreteType;
|
| - leftOverNamedParameters.remove(source);
|
| - }
|
| - leftOverNamedParameters.forEach((_, Element parameter) {
|
| - handleLeftoverOptionalParameter(parameter);
|
| - });
|
| - } else { // optional parameters are positional
|
| - // we attach the remaining positional arguments to their corresponding
|
| - // optional parameters
|
| - Link<Element> remainingOptionalParameters = signature.optionalParameters;
|
| - while (remainingPositionalArguments.moveNext()) {
|
| - final Element optionalParameter = remainingOptionalParameters.head;
|
| - result[optionalParameter] = remainingPositionalArguments.current;
|
| - // we know tail is defined because of guard 1
|
| - remainingOptionalParameters = remainingOptionalParameters.tail;
|
| - }
|
| - for (;
|
| - !remainingOptionalParameters.isEmpty;
|
| - remainingOptionalParameters = remainingOptionalParameters.tail) {
|
| - handleLeftoverOptionalParameter(remainingOptionalParameters.head);
|
| - }
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - ConcreteType getMonomorphicSendReturnType(
|
| - FunctionElement function,
|
| - ConcreteTypesEnvironment environment) {
|
| - Map<ConcreteTypesEnvironment, ConcreteType> template =
|
| - getTemplatesOrEmpty(function);
|
| - ConcreteType type = template[environment];
|
| - ConcreteType specialType = getSpecialCaseReturnType(function, environment);
|
| - if (type != null) {
|
| - return specialType != null ? specialType : type;
|
| - } else {
|
| - workQueue.add(new InferenceWorkItem(function, environment));
|
| - return specialType != null ? specialType : emptyConcreteType;
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Handles external methods that cannot be cached because they depend on some
|
| - * other state of [ConcreteTypesInferrer] like [:List#[]:] and
|
| - * [:List#[]=:]. Returns null if [function] and [environment] don't form a
|
| - * special case
|
| - */
|
| - ConcreteType getSpecialCaseReturnType(FunctionElement function,
|
| - ConcreteTypesEnvironment environment) {
|
| - // Handles int + int, double + double, int - int, ...
|
| - // We cannot compare function to int#+, int#-, etc. because int and double
|
| - // don't override these methods. So for 1+2, getSpecialCaseReturnType will
|
| - // be invoked with function = num#+. We use environment.typeOfThis instead.
|
| - ClassElement cls = environment.classOfThis;
|
| - if (cls != null) {
|
| - String name = function.name;
|
| - if ((cls == baseTypes.intBaseType.element
|
| - || cls == baseTypes.doubleBaseType.element)
|
| - && (name == '+' || name == '-' || name == '*')) {
|
| - Link<Element> parameters =
|
| - function.functionSignature.requiredParameters;
|
| - ConcreteType argumentType = environment.lookupType(parameters.head);
|
| - if (argumentType.getUniqueType() == cls) {
|
| - return singletonConcreteType(new ClassBaseType(cls));
|
| - }
|
| - }
|
| - }
|
| -
|
| - if (function == listIndex || function == listRemoveAt) {
|
| - Link<Element> parameters = function.functionSignature.requiredParameters;
|
| - ConcreteType indexType = environment.lookupType(parameters.head);
|
| - if (!indexType.baseTypes.contains(baseTypes.intBaseType)) {
|
| - return emptyConcreteType;
|
| - }
|
| - return listElementType;
|
| - } else if (function == listIndexSet || function == listInsert) {
|
| - Link<Element> parameters = function.functionSignature.requiredParameters;
|
| - ConcreteType indexType = environment.lookupType(parameters.head);
|
| - if (!indexType.baseTypes.contains(baseTypes.intBaseType)) {
|
| - return emptyConcreteType;
|
| - }
|
| - ConcreteType elementType = environment.lookupType(parameters.tail.head);
|
| - augmentListElementType(elementType);
|
| - return emptyConcreteType;
|
| - } else if (function == listAdd) {
|
| - Link<Element> parameters = function.functionSignature.requiredParameters;
|
| - ConcreteType elementType = environment.lookupType(parameters.head);
|
| - augmentListElementType(elementType);
|
| - return emptyConcreteType;
|
| - } else if (function == listRemoveLast) {
|
| - return listElementType;
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - ConcreteType analyzeMethodOrClosure(Element element,
|
| - ConcreteTypesEnvironment environment) {
|
| - ConcreteType specialResult = handleSpecialMethod(element, environment);
|
| - if (specialResult != null) return specialResult;
|
| - ClosureEnvironment closureEnv = closures.getEnvironmentOrNull(element);
|
| - return (closureEnv == null)
|
| - ? analyzeMethod(element, environment)
|
| - : analyzeClosure(element, closureEnv, environment);
|
| - }
|
| -
|
| - ConcreteType analyzeMethod(Element element,
|
| - ConcreteTypesEnvironment environment) {
|
| - TypeInferrerVisitor visitor = new TypeInferrerVisitor(
|
| - element,
|
| - this,
|
| - singletonConcreteType(new ClassBaseType(environment.classOfThis)),
|
| - environment.environment);
|
| - visitor.run();
|
| - return visitor.returnType;
|
| - }
|
| -
|
| - ConcreteType analyzeClosure(Element element,
|
| - ClosureEnvironment closureEnv,
|
| - ConcreteTypesEnvironment environment) {
|
| - assert(environment.classOfThis == null);
|
| - LocalsHandler locals = (closureEnv.locals != null)
|
| - ? new LocalsHandler.deepCopyOf(closureEnv.locals)
|
| - : null;
|
| - TypeInferrerVisitor visitor = new TypeInferrerVisitor(element, this,
|
| - closureEnv.thisType, environment.environment, locals);
|
| - visitor.run();
|
| - return visitor.returnType;
|
| - }
|
| -
|
| - /**
|
| - * Analyze the initializer of a field if it has not yet been done and update
|
| - * [inferredFieldTypes] accordingly. Invalidate the readers of the field if
|
| - * needed.
|
| - */
|
| - void ensureFieldInitialized(Element field) {
|
| - // This is test is needed for fitering out BoxFieldElements.
|
| - if (field is FieldElement && inferredFieldTypes[field] == null) {
|
| - analyzeFieldInitialization(field);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Analyze the initializer of a field and update [inferredFieldTypes]
|
| - * accordingly. Invalidate the readers of the field if needed.
|
| - */
|
| - ConcreteType analyzeFieldInitialization(VariableElement field) {
|
| - Visitor visitor = new TypeInferrerVisitor(field, this, null, new Map());
|
| - ConcreteType type;
|
| - if (field.initializer != null) {
|
| - type = field.initializer.accept(visitor);
|
| - inferredFieldTypes[field] = type;
|
| - invalidateReaders(field);
|
| - }
|
| - return type;
|
| - }
|
| -
|
| - /**
|
| - * Analyze a default value.
|
| - */
|
| - ConcreteType analyzeDefaultValue(Element function, Node expression) {
|
| - assert((function != null) && (expression != null));
|
| - Visitor visitor = new TypeInferrerVisitor(function, this, null, {});
|
| - return expression.accept(visitor);
|
| - }
|
| -
|
| - /**
|
| - * Hook that performs side effects on some special method calls (like
|
| - * [:List(length):]) and possibly returns a concrete type.
|
| - */
|
| - ConcreteType handleSpecialMethod(FunctionElement element,
|
| - ConcreteTypesEnvironment environment) {
|
| - // We trust the return type of native elements
|
| - if (isNativeElement(element)) {
|
| - var elementType = element.type;
|
| - assert(elementType.isFunctionType);
|
| - return typeOfNativeBehavior(
|
| - native.NativeBehavior.ofMethod(element, compiler));
|
| - }
|
| - // When List([length]) is called with some length, we must augment
|
| - // listElementType with {null}.
|
| - if (element == listConstructor) {
|
| - Link<Element> parameters =
|
| - listConstructor.functionSignature.optionalParameters;
|
| - ConcreteType lengthType = environment.lookupType(parameters.head);
|
| - if (lengthType.baseTypes.contains(baseTypes.intBaseType)) {
|
| - augmentListElementType(nullConcreteType);
|
| - }
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - /**
|
| - * Performs concrete type inference of the code reachable from [element].
|
| - */
|
| - @override
|
| - bool analyzeMain(Element element) {
|
| - initialize();
|
| - workQueue.add(
|
| - new InferenceWorkItem(element, new ConcreteTypesEnvironment()));
|
| - while (!workQueue.isEmpty) {
|
| - currentWorkItem = workQueue.remove();
|
| - if (currentWorkItem.method.isField) {
|
| - analyzeFieldInitialization(currentWorkItem.method);
|
| - } else {
|
| - Map<ConcreteTypesEnvironment, ConcreteType> template =
|
| - getTemplatesOrEmpty(currentWorkItem.method);
|
| - template.putIfAbsent(
|
| - currentWorkItem.environment, () => emptyConcreteType);
|
| - recordReturnType(
|
| - currentWorkItem.method,
|
| - analyzeMethodOrClosure(currentWorkItem.method,
|
| - currentWorkItem.environment));
|
| - }
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - /**
|
| - * Dumps debugging information on the standard output.
|
| - */
|
| - void debug() {
|
| - print("queue:");
|
| - for (InferenceWorkItem workItem in workQueue.queue) {
|
| - print(" $workItem");
|
| - }
|
| - print("seen classes:");
|
| - for (ClassElement cls in seenClasses) {
|
| - print(" ${cls.name}");
|
| - }
|
| - print("callers:");
|
| - callers.forEach((k,v) {
|
| - print(" $k: $v");
|
| - });
|
| - print("dynamic callers:");
|
| - dynamicCallers.forEach((k,v) {
|
| - print(" $k: $v");
|
| - });
|
| - print("readers:");
|
| - fieldReaders.forEach((k,v) {
|
| - print(" $k: $v");
|
| - });
|
| - print("readers of captured locals:");
|
| - capturedLocalsReaders.forEach((k,v) {
|
| - print(" $k: $v");
|
| - });
|
| - print("inferredFieldTypes:");
|
| - inferredFieldTypes.forEach((k,v) {
|
| - print(" $k: $v");
|
| - });
|
| - print("listElementType:");
|
| - print(" $listElementType");
|
| - print("inferredParameterTypes:");
|
| - inferredParameterTypes.forEach((k,v) {
|
| - print(" $k: $v");
|
| - });
|
| - print("inferred selector types:");
|
| - inferredSelectorTypes.forEach((selector, map) {
|
| - print(" $selector:");
|
| - map.forEach((k, v) {
|
| - print(" $k: $v");
|
| - });
|
| - });
|
| - print("cache:");
|
| - methodToTemplates.forEach((k,v) {
|
| - print(" $k: $v");
|
| - });
|
| - print("closures:");
|
| - closures.closures.forEach((k, ClosureEnvironment v) {
|
| - print(" $k");
|
| - print(" this: ${v.thisType}");
|
| - if (v.locals != null) {
|
| - v.locals.locals.forEachLocal((local, type) {
|
| - print(" $local: $type");
|
| - });
|
| - }
|
| - });
|
| - print("inferred expression types:");
|
| - inferredTypes.forEach((k,v) {
|
| - print(" $k: $v");
|
| - });
|
| - }
|
| -
|
| - @override
|
| - ConcreteType addReturnTypeFor(Element analyzedElement,
|
| - ConcreteType currentType,
|
| - ConcreteType newType) {
|
| - return (currentType == null) ? newType : currentType.union(newType);
|
| - }
|
| -
|
| - @override
|
| - void forEachElementMatching(Selector selector, bool f(Element element)) {
|
| - getMembersBySelector(selector).forEach(f);
|
| - }
|
| -
|
| - @override
|
| - void recordReturnType(Element element, ConcreteType type) {
|
| - assert((type != null) && (element == currentWorkItem.method));
|
| - Map<ConcreteTypesEnvironment, ConcreteType> template =
|
| - getTemplatesOrEmpty(element);
|
| - if (template[currentWorkItem.environment] != type) {
|
| - template[currentWorkItem.environment] = type;
|
| - invalidateCallers(element);
|
| - }
|
| - }
|
| -
|
| - @override
|
| - void recordType(Element element, ConcreteType type) {
|
| - assert(element is FieldElement);
|
| - augmentFieldType(element, type);
|
| - }
|
| -
|
| - @override
|
| - void recordTypeOfFinalField(Node node,
|
| - Element nodeHolder,
|
| - Element field,
|
| - ConcreteType type) {
|
| - augmentFieldType(field, type);
|
| - }
|
| -
|
| - @override
|
| - void recordTypeOfNonFinalField(Spannable node, Element field,
|
| - ConcreteType type) {
|
| - augmentFieldType(field, type);
|
| - }
|
| -
|
| - @override
|
| - void recordCapturedLocalRead(Local local) {
|
| - addCapturedLocalReader(local, currentWorkItem.method);
|
| - }
|
| -
|
| - @override
|
| - void recordLocalUpdate(Local local, ConcreteType type) {
|
| - Set<FunctionElement> localReaders = capturedLocalsReaders[local];
|
| - if (localReaders != null) {
|
| - localReaders.forEach(invalidate);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Returns the caller of the current analyzed element, given the alleged
|
| - * caller provided by SimpleTypeInferrer.
|
| - *
|
| - * SimpleTypeInferrer lies about the caller when it's a closure.
|
| - * Unfortunately we cannot always trust currentWorkItem.method either because
|
| - * it is wrong for fields initializers.
|
| - */
|
| - Element getRealCaller(Element allegedCaller) {
|
| - Element currentMethod = currentWorkItem.method;
|
| - if ((currentMethod != allegedCaller)
|
| - && currentMethod.isFunction
|
| - && closures.contains(currentMethod)) {
|
| - return currentMethod;
|
| - } else {
|
| - return allegedCaller;
|
| - }
|
| - }
|
| -
|
| - @override
|
| - ConcreteType registerCalledElement(Spannable node,
|
| - Selector selector,
|
| - Element caller,
|
| - Element callee,
|
| - ArgumentsTypes<ConcreteType> arguments,
|
| - SideEffects sideEffects,
|
| - bool inLoop) {
|
| - caller = getRealCaller(caller);
|
| - if ((selector == null) || (selector.kind == SelectorKind.CALL)) {
|
| - callee = callee.implementation;
|
| - if (selector != null && selector.name == 'JS') {
|
| - return null;
|
| - }
|
| - if (callee.isField) { // toplevel closure call
|
| - getFieldType(selector, callee); // trigger toplevel field analysis
|
| - addFieldReader(callee, caller);
|
| - ConcreteType result = emptyConcreteType;
|
| - for (FunctionElement function in closures.functionElements) {
|
| - addCaller(function, caller);
|
| - result = result.union(
|
| - getSendReturnType(selector, function, null, arguments));
|
| - }
|
| - return result;
|
| - } else { // method or constructor call
|
| - addCaller(callee, caller);
|
| - ClassElement receiverClass = null;
|
| - if (callee.isGenerativeConstructor) {
|
| - receiverClass = callee.enclosingClass;
|
| - } else if (node is Send) {
|
| - Send send = node;
|
| - if (send.receiver != null) {
|
| - if (send.receiver.isSuper()) {
|
| - receiverClass =
|
| - currentWorkItem.environment.classOfThis.superclass;
|
| - } else {
|
| - receiverClass = currentWorkItem.environment.classOfThis;
|
| - }
|
| - }
|
| - }
|
| - return getSendReturnType(selector, callee, receiverClass, arguments);
|
| - }
|
| - } else if (selector.kind == SelectorKind.GETTER) {
|
| - if (callee.isField) {
|
| - addFieldReader(callee, caller);
|
| - return getFieldType(selector, callee);
|
| - } else if (callee.isGetter) {
|
| - Element enclosing = callee.enclosingElement.isCompilationUnit
|
| - ? null : callee.enclosingElement;
|
| - addCaller(callee, caller);
|
| - ArgumentsTypes noArguments = new ArgumentsTypes([], new Map());
|
| - return getSendReturnType(selector, callee, enclosing, noArguments);
|
| - } else if (callee.isFunction) {
|
| - addClosure(callee, null, null);
|
| - return singletonConcreteType(baseTypes.functionBaseType);
|
| - }
|
| - } else if (selector.kind == SelectorKind.SETTER) {
|
| - ConcreteType argumentType = arguments.positional.first;
|
| - if (callee.isField) {
|
| - augmentFieldType(callee, argumentType);
|
| - } else if (callee.isSetter) {
|
| - FunctionElement setter = callee;
|
| - // TODO(polux): A setter always returns void so there's no need to
|
| - // invalidate its callers even if it is called with new arguments.
|
| - // However, if we start to record more than returned types, like
|
| - // exceptions for instance, we need to do it by uncommenting the
|
| - // following line.
|
| - // inferrer.addCaller(setter, currentMethod);
|
| - Element enclosing = callee.enclosingElement.isCompilationUnit
|
| - ? null : callee.enclosingElement;
|
| - return getSendReturnType(selector, setter, enclosing,
|
| - new ArgumentsTypes([argumentType], new Map()));
|
| - }
|
| - } else {
|
| - throw new ArgumentError("unexpected selector kind");
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - @override
|
| - ConcreteType registerCalledSelector(Node node,
|
| - Selector selector,
|
| - ConcreteType receiverType,
|
| - Element caller,
|
| - ArgumentsTypes<ConcreteType> arguments,
|
| - SideEffects sideEffects,
|
| - bool inLoop) {
|
| - caller = getRealCaller(caller);
|
| - switch (selector.kind) {
|
| - case SelectorKind.GETTER:
|
| - return registerDynamicGetterSend(selector, receiverType, caller);
|
| - case SelectorKind.SETTER:
|
| - return registerDynamicSetterSend(
|
| - selector, receiverType, caller, arguments);
|
| - default:
|
| - return registerDynamicSend(selector, receiverType, caller, arguments);
|
| - }
|
| - }
|
| -
|
| - ConcreteType registerDynamicGetterSend(Selector selector,
|
| - ConcreteType receiverType,
|
| - Element caller) {
|
| - caller = getRealCaller(caller);
|
| - ConcreteType result = emptyConcreteType;
|
| -
|
| - void augmentResult(ClassElement baseReceiverType, Element member) {
|
| - if (member.isField) {
|
| - addFieldReader(member, caller);
|
| - result = result.union(getFieldType(selector, member));
|
| - } else if (member.isGetter) {
|
| - addCaller(member, caller);
|
| - ArgumentsTypes noArguments = new ArgumentsTypes([], new Map());
|
| - result = result.union(
|
| - getSendReturnType(selector, member, baseReceiverType, noArguments));
|
| - } else if (member.isFunction) {
|
| - addClosure(member, receiverType, null);
|
| - result = result.union(
|
| - singletonConcreteType(baseTypes.functionBaseType));
|
| - } else {
|
| - throw new ArgumentError("unexpected element type");
|
| - }
|
| - }
|
| -
|
| - if (receiverType.isUnknown()) {
|
| - addDynamicCaller(selector, caller);
|
| - Set<Element> members = getMembersBySelector(selector);
|
| - for (Element member in members) {
|
| - if (!(member.isField || member.isGetter)) continue;
|
| - for (ClassElement cls in
|
| - getReflexiveSubtypesOf(member.enclosingElement)) {
|
| - augmentResult(cls, member);
|
| - }
|
| - }
|
| - } else {
|
| - for (BaseType baseReceiverType in receiverType.baseTypes) {
|
| - if (!baseReceiverType.isNull()) {
|
| - ClassBaseType classBaseType = baseReceiverType;
|
| - ClassElement cls = classBaseType.element;
|
| - Element getterOrField = cls.lookupSelector(selector);
|
| - if (getterOrField != null) {
|
| - augmentResult(cls, getterOrField.implementation);
|
| - }
|
| - }
|
| - }
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - ConcreteType registerDynamicSetterSend(
|
| - Selector selector,
|
| - ConcreteType receiverType,
|
| - Element caller,
|
| - ArgumentsTypes<ConcreteType> arguments) {
|
| - caller = getRealCaller(caller);
|
| - ConcreteType argumentType = arguments.positional.first;
|
| -
|
| - void augmentField(ClassElement receiverType, Element setterOrField) {
|
| - if (setterOrField.isField) {
|
| - augmentFieldType(setterOrField, argumentType);
|
| - } else if (setterOrField.isSetter) {
|
| - // A setter always returns void so there's no need to invalidate its
|
| - // callers even if it is called with new arguments. However, if we
|
| - // start to record more than returned types, like exceptions for
|
| - // instance, we need to do it by uncommenting the following line.
|
| - // inferrer.addCaller(setter, currentMethod);
|
| - getSendReturnType(selector, setterOrField, receiverType,
|
| - new ArgumentsTypes([argumentType], new Map()));
|
| - } else {
|
| - throw new ArgumentError("unexpected element type");
|
| - }
|
| - }
|
| -
|
| - if (receiverType.isUnknown()) {
|
| - // Same remark as above
|
| - // addDynamicCaller(selector, caller);
|
| - for (Element member in getMembersBySelector(selector)) {
|
| - if (!(member.isField || member.isSetter)) continue;
|
| - Element cls = member.enclosingClass;
|
| - augmentField(cls, member);
|
| - }
|
| - } else {
|
| - for (BaseType baseReceiverType in receiverType.baseTypes) {
|
| - if (!baseReceiverType.isNull()) {
|
| - ClassBaseType classBaseType = baseReceiverType;
|
| - ClassElement cls = classBaseType.element;
|
| - Element setterOrField = cls.lookupSelector(selector);
|
| - if (setterOrField != null) {
|
| - augmentField(cls, setterOrField.implementation);
|
| - }
|
| - }
|
| - }
|
| - }
|
| - return argumentType;
|
| - }
|
| -
|
| - ConcreteType registerDynamicSend(Selector selector,
|
| - ConcreteType receiverType,
|
| - Element caller,
|
| - ArgumentsTypes<ConcreteType> arguments) {
|
| - caller = getRealCaller(caller);
|
| - ConcreteType result = emptyConcreteType;
|
| - if (receiverType.isUnknown()) {
|
| - addDynamicCaller(selector, caller);
|
| - Set<Element> elements = getMembersBySelector(selector);
|
| - for (Element element in elements) {
|
| - if (element.isFunction) {
|
| - FunctionElement method = element;
|
| - addCaller(method, caller);
|
| - for (ClassElement cls in
|
| - getReflexiveSubtypesOf(method.enclosingElement)) {
|
| - result = result.union(
|
| - getSendReturnType(selector, method, cls, arguments));
|
| - }
|
| - } else { // closure call
|
| - assert(element.isField);
|
| - for (FunctionElement function in closures.functionElements) {
|
| - addCaller(function, caller);
|
| - result = result.union(
|
| - getSendReturnType(selector, function, null, arguments));
|
| - }
|
| - }
|
| - }
|
| - } else {
|
| - for (BaseType baseReceiverType in receiverType.baseTypes) {
|
| - if (!baseReceiverType.isNull()) {
|
| - ClassBaseType classBaseReceiverType = baseReceiverType;
|
| - ClassElement cls = classBaseReceiverType.element;
|
| - Element method = cls.lookupSelector(selector);
|
| - if (method != null) {
|
| - if (method.isFunction) {
|
| - assert(method is FunctionElement);
|
| - method = method.implementation;
|
| - addCaller(method, caller);
|
| - result = result.union(
|
| - getSendReturnType(selector, method, cls, arguments));
|
| - } else { // closure call
|
| - for (FunctionElement function in closures.functionElements) {
|
| - addCaller(function, caller);
|
| - result = result.union(
|
| - getSendReturnType(selector, function, null, arguments));
|
| - }
|
| - }
|
| - }
|
| - }
|
| - }
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - @override
|
| - void setDefaultTypeOfParameter(ParameterElement parameter,
|
| - ConcreteType type) {
|
| - // We handle default parameters our own way in associateArguments
|
| - }
|
| -
|
| - /**
|
| - * TODO(johnniwinther): Remove once synthetic parameters get their own default
|
| - * values.
|
| - */
|
| - bool hasAlreadyComputedTypeOfParameterDefault(Element parameter) => false;
|
| -
|
| - @override
|
| - ConcreteType registerCalledClosure(Node node,
|
| - Selector selector,
|
| - ConcreteType closure,
|
| - Element caller,
|
| - ArgumentsTypes<ConcreteType> arguments,
|
| - SideEffects sideEffects,
|
| - bool inLoop) {
|
| - caller = getRealCaller(caller);
|
| - ConcreteType result = emptyConcreteType;
|
| - for (FunctionElement function in closures.functionElements) {
|
| - addCaller(function, caller);
|
| - result = result.union(
|
| - getSendReturnType(selector, function, null, arguments));
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - @override
|
| - ConcreteType returnTypeOfElement(Element element) {
|
| - // Never called by SimpleTypeInferrer.
|
| - throw new UnsupportedError("");
|
| - }
|
| -
|
| - @override
|
| - ConcreteType typeOfElement(Element element) {
|
| - if (currentWorkItem != null) {
|
| - final result = currentWorkItem.environment.lookupType(element);
|
| - if (result != null) return result;
|
| - }
|
| - if (element.isParameter || element.isInitializingFormal) {
|
| - return inferredParameterTypes[element];
|
| - } else if (element.isField) {
|
| - return inferredFieldTypes[element];
|
| - }
|
| - throw new ArgumentError("unexpected element type");
|
| - }
|
| -
|
| - @override
|
| - void analyze(Element element, ArgumentsTypes arguments) {
|
| - FunctionElement function = element;
|
| - getSendReturnType(
|
| - null, function, currentWorkItem.environment.classOfThis, arguments);
|
| - }
|
| -}
|
| -
|
| -class TypeInferrerVisitor extends SimpleTypeInferrerVisitor<ConcreteType> {
|
| - final ConcreteType thisType;
|
| - ConcreteTypesInferrer get inferrer => super.inferrer;
|
| -
|
| - TypeInferrerVisitor(Element element,
|
| - ConcreteTypesInferrer inferrer,
|
| - this.thisType,
|
| - Map<Element, ConcreteType> environment,
|
| - [LocalsHandler<ConcreteType> handler])
|
| - : super(element, inferrer.compiler, inferrer, handler);
|
| -
|
| - @override
|
| - ConcreteType visitFunctionExpression(FunctionExpression node) {
|
| - Element element = elements[node];
|
| - // visitFunctionExpression should be only called for closures
|
| - assert(element != analyzedElement);
|
| - inferrer.addClosure(
|
| - element, thisType, new LocalsHandler.deepCopyOf(locals));
|
| - return types.functionType;
|
| - }
|
| -
|
| - @override
|
| - ConcreteType visitLiteralString(LiteralString node) {
|
| - // TODO(polux): get rid of this hack once we have a natural way of inferring
|
| - // the unknown type.
|
| - if (inferrer.testMode
|
| - && (node.dartString.slowToString() == "__dynamic_for_test")) {
|
| - return inferrer.unknownConcreteType;
|
| - }
|
| - return super.visitLiteralString(node);
|
| - }
|
| -
|
| - /**
|
| - * Same as super.visitLiteralList except it doesn't cache anything.
|
| - */
|
| - @override
|
| - ConcreteType visitLiteralList(LiteralList node) {
|
| - ConcreteType elementType;
|
| - int length = 0;
|
| - for (Node element in node.elements.nodes) {
|
| - ConcreteType type = visit(element);
|
| - elementType = elementType == null
|
| - ? types.allocatePhi(null, null, type)
|
| - : types.addPhiInput(null, elementType, type);
|
| - length++;
|
| - }
|
| - elementType = elementType == null
|
| - ? types.nonNullEmpty()
|
| - : types.simplifyPhi(null, null, elementType);
|
| - ConcreteType containerType = node.isConst
|
| - ? types.constListType
|
| - : types.growableListType;
|
| - return types.allocateList(
|
| - containerType,
|
| - node,
|
| - outermostElement,
|
| - elementType,
|
| - length);
|
| - }
|
| -
|
| - /**
|
| - * Same as super.visitGetterSend except it records the type of nodes in test
|
| - * mode.
|
| - */
|
| - @override
|
| - ConcreteType visitGetterSend(Send node) {
|
| - if (inferrer.testMode) {
|
| - var element = elements[node];
|
| - if (element is Local) {
|
| - ConcreteType type = locals.use(element);
|
| - if (type != null) {
|
| - inferrer.augmentInferredType(node, type);
|
| - }
|
| - }
|
| - }
|
| - return super.visitGetterSend(node);
|
| - }
|
| -}
|
|
|