Package com.google.dart.engine.internal.type

Source Code of com.google.dart.engine.internal.type.InterfaceTypeImpl

/*
* 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).internalUnionTypeIsMoreSpecificThan(
          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;
  }
}
TOP

Related Classes of com.google.dart.engine.internal.type.InterfaceTypeImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.