/* * Copyright (c) 2012, the Dart project authors. * * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package com.google.dart.engine.internal.type; import com.google.common.annotations.VisibleForTesting; import com.google.dart.engine.element.ClassElement; import com.google.dart.engine.element.ConstructorElement; import com.google.dart.engine.element.Element; import com.google.dart.engine.element.LibraryElement; import com.google.dart.engine.element.MethodElement; import com.google.dart.engine.element.PropertyAccessorElement; import com.google.dart.engine.element.TypeParameterElement; import com.google.dart.engine.internal.element.ClassElementImpl; import com.google.dart.engine.internal.element.ElementPair; import com.google.dart.engine.internal.element.member.ConstructorMember; import com.google.dart.engine.internal.element.member.MethodMember; import com.google.dart.engine.internal.element.member.PropertyAccessorMember; import com.google.dart.engine.internal.resolver.InheritanceManager; import com.google.dart.engine.type.FunctionType; import com.google.dart.engine.type.InterfaceType; import com.google.dart.engine.type.Type; import com.google.dart.engine.type.TypeParameterType; import com.google.dart.engine.type.UnionType; import com.google.dart.engine.utilities.general.ObjectUtilities; import java.util.Arrays; import java.util.HashSet; import java.util.Set; /** * Instances of the class {@code InterfaceTypeImpl} defines the behavior common to objects * representing the type introduced by either a class or an interface, or a reference to such a * type. * * @coverage dart.engine.type */ public class InterfaceTypeImpl extends TypeImpl implements InterfaceType { /** * This method computes the longest inheritance path from some passed {@link Type} to Object. * * @param type the {@link Type} to compute the longest inheritance path of from the passed * {@link Type} to Object * @return the computed longest inheritance path to Object * @see InterfaceType#getLeastUpperBound(Type) */ @VisibleForTesting public static int computeLongestInheritancePathToObject(InterfaceType type) { return computeLongestInheritancePathToObject(type, 0, new HashSet<ClassElement>()); } /** * Returns the set of all superinterfaces of the passed {@link Type}. * * @param type the {@link Type} to compute the set of superinterfaces of * @return the {@link Set} of superinterfaces of the passed {@link Type} * @see #getLeastUpperBound(Type) */ @VisibleForTesting public static Set<InterfaceType> computeSuperinterfaceSet(InterfaceType type) { return computeSuperinterfaceSet(type, new HashSet<InterfaceType>()); } /** * This method computes the longest inheritance path from some passed {@link Type} to Object. This * method calls itself recursively, callers should use the public method * {@link #computeLongestInheritancePathToObject(Type)}. * * @param type the {@link Type} to compute the longest inheritance path of from the passed * {@link Type} to Object * @param depth a field used recursively * @param visitedClasses the classes that have already been visited * @return the computed longest inheritance path to Object * @see #computeLongestInheritancePathToObject(Type) * @see #getLeastUpperBound(Type) */ private static int computeLongestInheritancePathToObject(InterfaceType type, int depth, HashSet<ClassElement> visitedClasses) { ClassElement classElement = type.getElement(); // Object case if (classElement.getSupertype() == null || visitedClasses.contains(classElement)) { return depth; } int longestPath = 1; try { visitedClasses.add(classElement); InterfaceType[] superinterfaces = classElement.getInterfaces(); int pathLength; if (superinterfaces.length > 0) { // loop through each of the superinterfaces recursively calling this method and keeping track // of the longest path to return for (InterfaceType superinterface : superinterfaces) { pathLength = computeLongestInheritancePathToObject( superinterface, depth + 1, visitedClasses); if (pathLength > longestPath) { longestPath = pathLength; } } } // finally, perform this same check on the super type // TODO(brianwilkerson) Does this also need to add in the number of mixin classes? InterfaceType supertype = classElement.getSupertype(); pathLength = computeLongestInheritancePathToObject(supertype, depth + 1, visitedClasses); if (pathLength > longestPath) { longestPath = pathLength; } } finally { visitedClasses.remove(classElement); } return longestPath; } /** * Returns the set of all superinterfaces of the passed {@link Type}. This is a recursive method, * callers should call the public {@link #computeSuperinterfaceSet(Type)}. * * @param type the {@link Type} to compute the set of superinterfaces of * @param set a {@link HashSet} used recursively by this method * @return the {@link Set} of superinterfaces of the passed {@link Type} * @see #computeSuperinterfaceSet(Type) * @see #getLeastUpperBound(Type) */ private static Set<InterfaceType> computeSuperinterfaceSet(InterfaceType type, HashSet<InterfaceType> set) { Element element = type.getElement(); if (element != null) { InterfaceType[] superinterfaces = type.getInterfaces(); for (InterfaceType superinterface : superinterfaces) { if (set.add(superinterface)) { computeSuperinterfaceSet(superinterface, set); } } InterfaceType supertype = type.getSuperclass(); if (supertype != null) { if (set.add(supertype)) { computeSuperinterfaceSet(supertype, set); } } } return set; } /** * Return the intersection of the given sets of types, where intersection is based on the equality * of the types themselves. * * @param first the first set of types to be intersected * @param second the second set of types to be intersected * @return the intersection of the given sets of types */ private static InterfaceType[] intersection(Set<InterfaceType> first, Set<InterfaceType> second) { Set<InterfaceType> result = new HashSet<InterfaceType>(first); result.retainAll(second); return result.toArray(new InterfaceType[result.size()]); } /** * Return the "least upper bound" of the given types under the assumption that the types have the * same element and differ only in terms of the type arguments. The resulting type is composed by * comparing the corresponding type arguments, keeping those that are the same, and using * 'dynamic' for those that are different. * * @param firstType the first type * @param secondType the second type * @return the "least upper bound" of the given types */ private static InterfaceType leastUpperBound(InterfaceType firstType, InterfaceType secondType) { if (firstType.equals(secondType)) { return firstType; } Type[] firstArguments = firstType.getTypeArguments(); Type[] secondArguments = secondType.getTypeArguments(); int argumentCount = firstArguments.length; if (argumentCount == 0) { return firstType; } Type[] lubArguments = new Type[argumentCount]; for (int i = 0; i < argumentCount; i++) { // // Ideally we would take the least upper bound of the two argument types, but this can cause // an infinite recursion (such as when finding the least upper bound of String and num). // if (firstArguments[i].equals(secondArguments[i])) { lubArguments[i] = firstArguments[i]; } if (lubArguments[i] == null) { lubArguments[i] = DynamicTypeImpl.getInstance(); } } InterfaceTypeImpl lub = new InterfaceTypeImpl(firstType.getElement()); lub.setTypeArguments(lubArguments); return lub; } /** * An array containing the actual types of the type arguments. */ private Type[] typeArguments = TypeImpl.EMPTY_ARRAY; /** * Initialize a newly created type to be declared by the given element. * * @param element the element representing the declaration of the type */ public InterfaceTypeImpl(ClassElement element) { super(element, element.getDisplayName()); } /** * Initialize a newly created type to have the given name. This constructor should only be used in * cases where there is no declaration of the type. * * @param name the name of the type */ private InterfaceTypeImpl(String name) { super(null, name); } @Override public boolean equals(Object object) { return internalEquals(object, new HashSet<ElementPair>()); } @Override public PropertyAccessorElement[] getAccessors() { PropertyAccessorElement[] accessors = getElement().getAccessors(); PropertyAccessorElement[] members = new PropertyAccessorElement[accessors.length]; for (int i = 0; i < accessors.length; i++) { members[i] = PropertyAccessorMember.from(accessors[i], this); } return members; } @Override public String getDisplayName() { String name = getName(); Type[] typeArguments = getTypeArguments(); boolean allDynamic = true; for (Type type : typeArguments) { if (type != null && !type.isDynamic()) { allDynamic = false; break; } } // If there is at least one non-dynamic type, then list them out if (!allDynamic) { StringBuilder builder = new StringBuilder(); builder.append(name); builder.append("<"); for (int i = 0; i < typeArguments.length; i++) { if (i != 0) { builder.append(", "); } Type typeArg = typeArguments[i]; builder.append(typeArg.getDisplayName()); } builder.append(">"); name = builder.toString(); } return name; } @Override public ClassElement getElement() { return (ClassElement) super.getElement(); } @Override public PropertyAccessorElement getGetter(String getterName) { return PropertyAccessorMember.from( ((ClassElementImpl) getElement()).getGetter(getterName), this); } @Override public InterfaceType[] getInterfaces() { ClassElement classElement = getElement(); InterfaceType[] interfaces = classElement.getInterfaces(); TypeParameterElement[] typeParameters = classElement.getTypeParameters(); Type[] parameterTypes = classElement.getType().getTypeArguments(); if (typeParameters.length == 0) { return interfaces; } int count = interfaces.length; InterfaceType[] typedInterfaces = new InterfaceType[count]; for (int i = 0; i < count; i++) { typedInterfaces[i] = interfaces[i].substitute(typeArguments, parameterTypes); } return typedInterfaces; } @Override public Type getLeastUpperBound(Type type) { // quick check for self if (type == this) { return this; } // dynamic Type dynamicType = DynamicTypeImpl.getInstance(); if (this == dynamicType || type == dynamicType) { return dynamicType; } // TODO (jwren) opportunity here for a better, faster algorithm if this turns out to be a bottle-neck if (!(type instanceof InterfaceType)) { return null; } // new names to match up with the spec InterfaceType i = this; InterfaceType j = (InterfaceType) type; // compute set of supertypes Set<InterfaceType> si = computeSuperinterfaceSet(i); Set<InterfaceType> sj = computeSuperinterfaceSet(j); // union si with i and sj with j si.add(i); sj.add(j); // compute intersection, reference as set 's' InterfaceType[] s = intersection(si, sj); // for each element in Set s, compute the largest inheritance path to Object int[] depths = new int[s.length]; int maxDepth = 0; for (int n = 0; n < s.length; n++) { depths[n] = computeLongestInheritancePathToObject(s[n]); if (depths[n] > maxDepth) { maxDepth = depths[n]; } } // ensure that the currently computed maxDepth is unique, // otherwise, decrement and test for uniqueness again for (; maxDepth >= 0; maxDepth--) { int indexOfLeastUpperBound = -1; int numberOfTypesAtMaxDepth = 0; for (int m = 0; m < depths.length; m++) { if (depths[m] == maxDepth) { numberOfTypesAtMaxDepth++; indexOfLeastUpperBound = m; } } if (numberOfTypesAtMaxDepth == 1) { return s[indexOfLeastUpperBound]; } } // illegal state, log and return null- Object at maxDepth == 0 should always return itself as // the least upper bound. // TODO (jwren) log the error state return null; } @Override public MethodElement getMethod(String methodName) { return MethodMember.from(((ClassElementImpl) getElement()).getMethod(methodName), this); } @Override public MethodElement[] getMethods() { MethodElement[] methods = getElement().getMethods(); MethodElement[] members = new MethodElement[methods.length]; for (int i = 0; i < methods.length; i++) { members[i] = MethodMember.from(methods[i], this); } return members; } @Override public InterfaceType[] getMixins() { ClassElement classElement = getElement(); InterfaceType[] mixins = classElement.getMixins(); TypeParameterElement[] typeParameters = classElement.getTypeParameters(); Type[] parameterTypes = classElement.getType().getTypeArguments(); if (typeParameters.length == 0) { return mixins; } int count = mixins.length; InterfaceType[] typedMixins = new InterfaceType[count]; for (int i = 0; i < count; i++) { typedMixins[i] = mixins[i].substitute(typeArguments, parameterTypes); } return typedMixins; } @Override public PropertyAccessorElement getSetter(String setterName) { return PropertyAccessorMember.from( ((ClassElementImpl) getElement()).getSetter(setterName), this); } @Override public InterfaceType getSuperclass() { ClassElement classElement = getElement(); InterfaceType supertype = classElement.getSupertype(); if (supertype == null) { return null; } Type[] typeParameters = classElement.getType().getTypeArguments(); if (typeArguments.length == 0 || typeArguments.length != typeParameters.length) { return supertype; } return supertype.substitute(typeArguments, typeParameters); } @Override public Type[] getTypeArguments() { return typeArguments; } @Override public TypeParameterElement[] getTypeParameters() { return getElement().getTypeParameters(); } @Override public int hashCode() { ClassElement element = getElement(); if (element == null) { return 0; } return element.hashCode(); } @Override public boolean isDartCoreFunction() { ClassElement element = getElement(); if (element == null) { return false; } return element.getName().equals("Function") && element.getLibrary().isDartCore(); } @Override public boolean isDirectSupertypeOf(InterfaceType type) { InterfaceType i = this; InterfaceType j = type; ClassElement jElement = j.getElement(); InterfaceType supertype = jElement.getSupertype(); // // If J has no direct supertype then it is Object, and Object has no direct supertypes. // if (supertype == null) { return false; } // // I is listed in the extends clause of J. // Type[] jArgs = j.getTypeArguments(); Type[] jVars = jElement.getType().getTypeArguments(); supertype = supertype.substitute(jArgs, jVars); if (supertype.equals(i)) { return true; } // // I is listed in the implements clause of J. // for (InterfaceType interfaceType : jElement.getInterfaces()) { interfaceType = interfaceType.substitute(jArgs, jVars); if (interfaceType.equals(i)) { return true; } } // // I is listed in the with clause of J. // for (InterfaceType mixinType : jElement.getMixins()) { mixinType = mixinType.substitute(jArgs, jVars); if (mixinType.equals(i)) { return true; } } // // J is a mixin application of the mixin of I. // // TODO(brianwilkerson) Determine whether this needs to be implemented or whether it is covered // by the case above. return false; } @Override public boolean isObject() { return getElement().getSupertype() == null; } @Override public ConstructorElement lookUpConstructor(String constructorName, LibraryElement library) { // prepare base ConstructorElement ConstructorElement constructorElement; if (constructorName == null) { constructorElement = getElement().getUnnamedConstructor(); } else { constructorElement = getElement().getNamedConstructor(constructorName); } // not found or not accessible if (constructorElement == null || !constructorElement.isAccessibleIn(library)) { return null; } // return member return ConstructorMember.from(constructorElement, this); } @Override public PropertyAccessorElement lookUpGetter(String getterName, LibraryElement library) { PropertyAccessorElement element = getGetter(getterName); if (element != null && element.isAccessibleIn(library)) { return element; } return lookUpGetterInSuperclass(getterName, library); } @Override public PropertyAccessorElement lookUpGetterInSuperclass(String getterName, LibraryElement library) { for (InterfaceType mixin : getMixins()) { PropertyAccessorElement element = mixin.getGetter(getterName); if (element != null && element.isAccessibleIn(library)) { return element; } } HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>(); InterfaceType supertype = getSuperclass(); ClassElement supertypeElement = supertype == null ? null : supertype.getElement(); while (supertype != null && !visitedClasses.contains(supertypeElement)) { visitedClasses.add(supertypeElement); PropertyAccessorElement element = supertype.getGetter(getterName); if (element != null && element.isAccessibleIn(library)) { return element; } for (InterfaceType mixin : supertype.getMixins()) { element = mixin.getGetter(getterName); if (element != null && element.isAccessibleIn(library)) { return element; } } supertype = supertype.getSuperclass(); supertypeElement = supertype == null ? null : supertype.getElement(); } return null; } @Override public MethodElement lookUpMethod(String methodName, LibraryElement library) { MethodElement element = getMethod(methodName); if (element != null && element.isAccessibleIn(library)) { return element; } return lookUpMethodInSuperclass(methodName, library); } @Override public MethodElement lookUpMethodInSuperclass(String methodName, LibraryElement library) { for (InterfaceType mixin : getMixins()) { MethodElement element = mixin.getMethod(methodName); if (element != null && element.isAccessibleIn(library)) { return element; } } HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>(); InterfaceType supertype = getSuperclass(); ClassElement supertypeElement = supertype == null ? null : supertype.getElement(); while (supertype != null && !visitedClasses.contains(supertypeElement)) { visitedClasses.add(supertypeElement); MethodElement element = supertype.getMethod(methodName); if (element != null && element.isAccessibleIn(library)) { return element; } for (InterfaceType mixin : supertype.getMixins()) { element = mixin.getMethod(methodName); if (element != null && element.isAccessibleIn(library)) { return element; } } supertype = supertype.getSuperclass(); supertypeElement = supertype == null ? null : supertype.getElement(); } return null; } @Override public PropertyAccessorElement lookUpSetter(String setterName, LibraryElement library) { PropertyAccessorElement element = getSetter(setterName); if (element != null && element.isAccessibleIn(library)) { return element; } return lookUpSetterInSuperclass(setterName, library); } @Override public PropertyAccessorElement lookUpSetterInSuperclass(String setterName, LibraryElement library) { for (InterfaceType mixin : getMixins()) { PropertyAccessorElement element = mixin.getSetter(setterName); if (element != null && element.isAccessibleIn(library)) { return element; } } HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>(); InterfaceType supertype = getSuperclass(); ClassElement supertypeElement = supertype == null ? null : supertype.getElement(); while (supertype != null && !visitedClasses.contains(supertypeElement)) { visitedClasses.add(supertypeElement); PropertyAccessorElement element = supertype.getSetter(setterName); if (element != null && element.isAccessibleIn(library)) { return element; } for (InterfaceType mixin : supertype.getMixins()) { element = mixin.getSetter(setterName); if (element != null && element.isAccessibleIn(library)) { return element; } } supertype = supertype.getSuperclass(); supertypeElement = supertype == null ? null : supertype.getElement(); } return null; } /** * Set the actual types of the type arguments to those in the given array. * * @param typeArguments the actual types of the type arguments */ public void setTypeArguments(Type[] typeArguments) { this.typeArguments = typeArguments; } @Override public InterfaceTypeImpl substitute(Type[] argumentTypes) { return substitute(argumentTypes, getTypeArguments()); } @Override public InterfaceTypeImpl substitute(Type[] argumentTypes, Type[] parameterTypes) { if (argumentTypes.length != parameterTypes.length) { throw new IllegalArgumentException("argumentTypes.length (" + argumentTypes.length + ") != parameterTypes.length (" + parameterTypes.length + ")"); } if (argumentTypes.length == 0 || typeArguments.length == 0) { return this; } Type[] newTypeArguments = substitute(typeArguments, argumentTypes, parameterTypes); if (Arrays.equals(newTypeArguments, typeArguments)) { return this; } InterfaceTypeImpl newType = new InterfaceTypeImpl(getElement()); newType.setTypeArguments(newTypeArguments); return newType; } @Override protected void appendTo(StringBuilder builder) { builder.append(getName()); int argumentCount = typeArguments.length; if (argumentCount > 0) { builder.append("<"); for (int i = 0; i < argumentCount; i++) { if (i > 0) { builder.append(", "); } ((TypeImpl) typeArguments[i]).appendTo(builder); } builder.append(">"); } } @Override protected boolean internalEquals(Object object, Set<ElementPair> visitedElementPairs) { if (!(object instanceof InterfaceTypeImpl)) { return false; } InterfaceTypeImpl otherType = (InterfaceTypeImpl) object; return ObjectUtilities.equals(getElement(), otherType.getElement()) && TypeImpl.equalArrays(typeArguments, otherType.typeArguments, visitedElementPairs); } @Override protected boolean internalIsMoreSpecificThan(Type type, boolean withDynamic, Set<TypePair> visitedTypePairs) { // // S is dynamic. // The test to determine whether S is dynamic is done here because dynamic is not an instance of // InterfaceType. // if (type == DynamicTypeImpl.getInstance()) { return true; } else if ((type instanceof UnionType)) { return ((UnionTypeImpl) type).internalUnionTypeIsLessSpecificThan( this, withDynamic, visitedTypePairs); } else if (!(type instanceof InterfaceType)) { return false; } return isMoreSpecificThan( (InterfaceType) type, new HashSet<ClassElement>(), withDynamic, visitedTypePairs); } @Override protected boolean internalIsSubtypeOf(Type type, Set<TypePair> visitedTypePairs) { // // T is a subtype of S, written T <: S, iff [bottom/dynamic]T << S // if (type.isDynamic()) { return true; } else if (type instanceof TypeParameterType) { return false; } else if (type instanceof UnionType) { return ((UnionTypeImpl) type).internalUnionTypeIsSuperTypeOf(this, visitedTypePairs); } else if (type instanceof FunctionType) { // This implementation assumes transitivity // for function type subtyping on the RHS, but a literal reading // of the spec does not specify this. More precisely: if T <: F1 and F1 <: F2 and // F1 and F2 are function types, then we assume T <: F2. // // From the Function Types section of the spec: // // If a type I includes an instance method named call(), and the type of call() // is the function type F, then I is considered to be a subtype of F. // // However, the section on Interface Types says // // T is a subtype of S, written T <: S, iff [bottom/dynamic]T << S. // // after giving rules for << (pronounced "more specific than"). However, the "only if" // direction of the "iff" // in the definition of <: seems to be contradicted by the special case <: rule // quoted from the Function Types section: I see no rule for << which tells us that // I << F if I has call() at type F. // // After defining <: , the spec then // emphasizes that unlike the relation <<, the relation <: is not transitive in general: // // Note that <: is not a partial order on types, it is only binary relation on types. // This is because <: is not transitive. If it was, the subtype rule would have a cycle. // For example: List <: List<String> and List<int> <: List, but List<int> is not a subtype // of List<String>. Although <: is not a partial order on types, it does contain a partial // order, namely <<. This means that, barring raw types, intuition about classical subtype // rules does apply. // // There is no other occurrence of the word "raw" in relation to types in the spec that I can // find, but presumably it's a reference to // // http://docs.oracle.com/javase/tutorial/java/generics/rawTypes.html // // so e.g. non-generic types are never raw. As pointed out by paulberry, it's not clear // whether a type like T<int, dynamic> should be considered raw or not. On the one hand, it // doesn't correspond to a "raw"-in-the-Java-sense occurrence of T, which would instead // be T<dynamic, dynamic>; on the other hand, it's treated differently by <: and << when // occurring on the left hand side. ClassElement element = getElement(); InheritanceManager manager = new InheritanceManager(element.getLibrary()); FunctionType callType = manager.lookupMemberType(this, "call"); if (callType != null) { // A more literal reading of the spec would give something like // // return callType.equals(type) // // here, but that causes 101 errors in the external tests // (tools/test.py --mode release --compiler dartanalyzer --runtime none). return callType.isSubtypeOf(type); } return false; } else if (!(type instanceof InterfaceType)) { return false; } else if (this.equals(type)) { return true; } return isSubtypeOf((InterfaceType) type, new HashSet<ClassElement>(), visitedTypePairs); } // TODO(jwren) Remove "visitedClasses" parameter, as the logic for "visitedTypePairs" should // prevent a larger set of infinite loops private boolean isMoreSpecificThan(InterfaceType s, HashSet<ClassElement> visitedClasses, boolean withDynamic, Set<TypePair> visitedTypePairs) { // // A type T is more specific than a type S, written T << S, if one of the following conditions // is met: // // Reflexivity: T is S. // if (this.equals(s)) { return true; } // // T is bottom. (This case is handled by the class BottomTypeImpl.) // // Direct supertype: S is a direct supertype of T. // if (s.isDirectSupertypeOf(this)) { return true; } // // Covariance: T is of the form I<T1, ..., Tn> and S is of the form I<S1, ..., Sn> and Ti << Si, 1 <= i <= n. // ClassElement tElement = getElement(); ClassElement sElement = s.getElement(); if (tElement.equals(sElement)) { Type[] tArguments = getTypeArguments(); Type[] sArguments = s.getTypeArguments(); if (tArguments.length != sArguments.length) { return false; } for (int i = 0; i < tArguments.length; i++) { if (!((TypeImpl) tArguments[i]).isMoreSpecificThan( sArguments[i], withDynamic, visitedTypePairs)) { return false; } } return true; } // // Transitivity: T << U and U << S. // // First check for infinite loops ClassElement element = getElement(); if (element == null || visitedClasses.contains(element)) { return false; } visitedClasses.add(element); // Iterate over all of the types U that are more specific than T because they are direct // supertypes of T and return true if any of them are more specific than S. InterfaceType supertype = getSuperclass(); if (supertype != null && ((InterfaceTypeImpl) supertype).isMoreSpecificThan( s, visitedClasses, withDynamic, visitedTypePairs)) { return true; } for (InterfaceType interfaceType : getInterfaces()) { if (((InterfaceTypeImpl) interfaceType).isMoreSpecificThan( s, visitedClasses, withDynamic, visitedTypePairs)) { return true; } } for (InterfaceType mixinType : getMixins()) { if (((InterfaceTypeImpl) mixinType).isMoreSpecificThan( s, visitedClasses, withDynamic, visitedTypePairs)) { return true; } } return false; } private boolean isSubtypeOf(InterfaceType type, HashSet<ClassElement> visitedClasses, Set<TypePair> visitedTypePairs) { InterfaceType typeT = this; InterfaceType typeS = type; ClassElement elementT = getElement(); if (elementT == null || visitedClasses.contains(elementT)) { return false; } visitedClasses.add(elementT); if (typeT.equals(typeS)) { return true; } else if (ObjectUtilities.equals(elementT, typeS.getElement())) { // For each of the type arguments return true if all type args from T is a subtype of all // types from S. Type[] typeTArgs = typeT.getTypeArguments(); Type[] typeSArgs = typeS.getTypeArguments(); if (typeTArgs.length != typeSArgs.length) { // This case covers the case where two objects are being compared that have a different // number of parameterized types. return false; } for (int i = 0; i < typeTArgs.length; i++) { // Recursively call isSubtypeOf the type arguments and return false if the T argument is not // a subtype of the S argument. if (!((TypeImpl) typeTArgs[i]).isSubtypeOf(typeSArgs[i], visitedTypePairs)) { return false; } } return true; } else if (typeS.isDartCoreFunction() && elementT.getMethod("call") != null) { return true; } InterfaceType supertype = getSuperclass(); // The type is Object, return false. if (supertype != null && ((InterfaceTypeImpl) supertype).isSubtypeOf(typeS, visitedClasses, visitedTypePairs)) { return true; } InterfaceType[] interfaceTypes = getInterfaces(); for (InterfaceType interfaceType : interfaceTypes) { if (((InterfaceTypeImpl) interfaceType).isSubtypeOf(typeS, visitedClasses, visitedTypePairs)) { return true; } } InterfaceType[] mixinTypes = getMixins(); for (InterfaceType mixinType : mixinTypes) { if (((InterfaceTypeImpl) mixinType).isSubtypeOf(typeS, visitedClasses, visitedTypePairs)) { return true; } } return false; } }