| /* |
| * 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.dart.engine.AnalysisEngine; |
| import com.google.dart.engine.element.Element; |
| import com.google.dart.engine.internal.element.ElementPair; |
| import com.google.dart.engine.type.Type; |
| import com.google.dart.engine.type.UnionType; |
| |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| /** |
| * The abstract class {@code TypeImpl} implements the behavior common to objects representing the |
| * declared type of elements in the element model. |
| * |
| * @coverage dart.engine.type |
| */ |
| public abstract class TypeImpl implements Type { |
| // TODO (jwren) Move this class to "com.google.dart.engine.utilities.collection" |
| public class TypePair { |
| private Type firstType; |
| private Type secondType; |
| private int cachedHashCode; |
| |
| TypePair(Type firstType, Type secondType) { |
| this.firstType = firstType; |
| this.secondType = secondType; |
| } |
| |
| @Override |
| public boolean equals(Object object) { |
| if (object == this) { |
| return true; |
| } |
| if (object instanceof TypePair) { |
| TypePair typePair = (TypePair) object; |
| return firstType.equals(typePair.firstType) && secondType != null |
| && secondType.equals(typePair.secondType); |
| } |
| return false; |
| } |
| |
| // TODO(jwren) Revisit the hashCode distribution. |
| // TODO(jwren) For equals() & hashCode() could we use the implementations in ObjectUtilities or |
| // Guava's Objects class? |
| @Override |
| public int hashCode() { |
| if (cachedHashCode == 0) { |
| int firstHashCode = 0; |
| if (firstType != null) { |
| Element firstElement = firstType.getElement(); |
| firstHashCode = firstElement == null ? 0 : firstElement.hashCode(); |
| } |
| int secondHashCode = 0; |
| if (secondType != null) { |
| Element secondElement = secondType.getElement(); |
| secondHashCode = secondElement == null ? 0 : secondElement.hashCode(); |
| } |
| cachedHashCode = firstHashCode + secondHashCode; |
| } |
| return cachedHashCode; |
| } |
| } |
| |
| protected static boolean equalArrays(Type[] typeArgs1, Type[] typeArgs2, |
| Set<ElementPair> visitedElementPairs) { |
| if (typeArgs1.length != typeArgs2.length) { |
| return false; |
| } |
| for (int i = 0; i < typeArgs1.length; i++) { |
| if (!((TypeImpl) typeArgs1[i]).internalEquals(typeArgs2[i], visitedElementPairs)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Return an array containing the results of using the given argument types and parameter types to |
| * perform a substitution on all of the given types. |
| * |
| * @param types the types on which a substitution is to be performed |
| * @param argumentTypes the argument types for the substitution |
| * @param parameterTypes the parameter types for the substitution |
| * @return the result of performing the substitution on each of the types |
| */ |
| protected static Type[] substitute(Type[] types, Type[] argumentTypes, Type[] parameterTypes) { |
| int length = types.length; |
| if (length == 0) { |
| return types; |
| } |
| Type[] newTypes = new Type[length]; |
| for (int i = 0; i < length; i++) { |
| newTypes[i] = types[i].substitute(argumentTypes, parameterTypes); |
| } |
| return newTypes; |
| } |
| |
| /** |
| * The element representing the declaration of this type, or {@code null} if the type has not, or |
| * cannot, be associated with an element. |
| */ |
| private Element element; |
| |
| /** |
| * The name of this type, or {@code null} if the type does not have a name. |
| */ |
| private String name; |
| |
| /** |
| * An empty array of types. |
| */ |
| public static final Type[] EMPTY_ARRAY = new Type[0]; |
| |
| /** |
| * Initialize a newly created type to be declared by the given element and to have the given name. |
| * |
| * @param element the element representing the declaration of the type |
| * @param name the name of the type |
| */ |
| public TypeImpl(Element element, String name) { |
| this.element = element; |
| this.name = name; |
| } |
| |
| @Override |
| public String getDisplayName() { |
| return getName(); |
| } |
| |
| @Override |
| public Element getElement() { |
| return element; |
| } |
| |
| @Override |
| public Type getLeastUpperBound(Type type) { |
| return null; |
| } |
| |
| @Override |
| public String getName() { |
| return name; |
| } |
| |
| @Override |
| public boolean isAssignableTo(Type type) { |
| return isAssignableTo(type, new HashSet<TypePair>()); |
| } |
| |
| /** |
| * Return {@code true} if this type is assignable to the given type. A type <i>T</i> may be |
| * assigned to a type <i>S</i>, written <i>T</i> ⇔ <i>S</i>, iff either <i>T</i> <: <i>S</i> |
| * or <i>S</i> <: <i>T</i> (Interface Types section of spec). |
| * <p> |
| * The given set of pairs of types (T1, T2), where each pair indicates that we invoked this method |
| * because we are in the process of answering the question of whether T1 is a subtype of T2, is |
| * used to prevent infinite loops. |
| * |
| * @param type the type being compared with this type |
| * @param visitedTypePairs the set of pairs of types used to prevent infinite loops |
| * @return {@code true} if this type is assignable to the given type |
| */ |
| public final boolean isAssignableTo(Type type, Set<TypePair> visitedTypePairs) { |
| // Strictness matters for union types on the LHS, but not for union types |
| // on the RHS. |
| if (this instanceof UnionType) { |
| if (AnalysisEngine.getInstance().getStrictUnionTypes()) { |
| // *Every* element on the LHS must be assignable to the RHS. We recursively fall into |
| // the next case when the RHS is also a union: the order here is important! |
| for (Type left : ((UnionType) this).getElements()) { |
| // Would have to cast to [TypeImpl] to call the [visitedTypePairs] version here. |
| if (!left.isAssignableTo(type)) { |
| return false; |
| } |
| } |
| return true; |
| } else { |
| // *Some* element on the LHS must be assignable to the RHS. |
| for (Type left : ((UnionType) this).getElements()) { |
| // Would have to cast to [TypeImpl] to call the [visitedTypePairs] version here. |
| if (left.isAssignableTo(type)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| } else if (type instanceof UnionType) { |
| // The LHS, which is not a union, must be assignable to *some* element on the RHS. |
| for (Type right : ((UnionType) type).getElements()) { |
| if (this.isAssignableTo(right, visitedTypePairs)) { |
| return true; |
| } |
| } |
| return false; |
| } else { |
| // For non union types we use the language spec definition of [<=>]. |
| return isSubtypeOf(type, visitedTypePairs) |
| || ((TypeImpl) type).isSubtypeOf(this, visitedTypePairs); |
| } |
| } |
| |
| @Override |
| public boolean isBottom() { |
| return false; |
| } |
| |
| @Override |
| public boolean isDartCoreFunction() { |
| return false; |
| } |
| |
| @Override |
| public boolean isDynamic() { |
| return false; |
| } |
| |
| @Override |
| public final boolean isMoreSpecificThan(Type type) { |
| return isMoreSpecificThan(type, false, new HashSet<TypePair>()); |
| } |
| |
| /** |
| * Return {@code true} if this type is more specific than the given type. |
| * <p> |
| * The given set of pairs of types (T1, T2), where each pair indicates that we invoked this method |
| * because we are in the process of answering the question of whether T1 is a subtype of T2, is |
| * used to prevent infinite loops. |
| * |
| * @param type the type being compared with this type |
| * @param withDynamic {@code true} if "dynamic" should be considered as a subtype of any type |
| * @param visitedTypePairs the set of pairs of types used to prevent infinite loops |
| * @return {@code true} if this type is more specific than the given type |
| */ |
| public final boolean isMoreSpecificThan(Type type, boolean withDynamic, |
| Set<TypePair> visitedTypePairs) { |
| // If the visitedTypePairs already has the pair (this, type), return false |
| TypePair typePair = new TypePair(this, type); |
| if (!visitedTypePairs.add(typePair)) { |
| return false; |
| } |
| boolean result = internalIsMoreSpecificThan(type, withDynamic, visitedTypePairs); |
| visitedTypePairs.remove(typePair); |
| return result; |
| } |
| |
| @Override |
| public boolean isObject() { |
| return false; |
| } |
| |
| @Override |
| public final boolean isSubtypeOf(Type type) { |
| return isSubtypeOf(type, new HashSet<TypePair>()); |
| } |
| |
| /** |
| * Return {@code true} if this type is a subtype of the given type. |
| * <p> |
| * The given set of pairs of types (T1, T2), where each pair indicates that we invoked this method |
| * because we are in the process of answering the question of whether T1 is a subtype of T2, is |
| * used to prevent infinite loops. |
| * |
| * @param type the type being compared with this type |
| * @param visitedTypePairs the set of pairs of types used to prevent infinite loops |
| * @return {@code true} if this type is a subtype of the given type |
| */ |
| public final boolean isSubtypeOf(Type type, Set<TypePair> visitedTypePairs) { |
| // If the visitedTypePairs already has the pair (this, type), return false |
| TypePair typePair = new TypePair(this, type); |
| if (!visitedTypePairs.add(typePair)) { |
| return false; |
| } |
| boolean result = internalIsSubtypeOf(type, visitedTypePairs); |
| visitedTypePairs.remove(typePair); |
| return result; |
| } |
| |
| @Override |
| public boolean isSupertypeOf(Type type) { |
| return type.isSubtypeOf(this); |
| } |
| |
| @Override |
| public boolean isVoid() { |
| return false; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder builder = new StringBuilder(); |
| appendTo(builder); |
| return builder.toString(); |
| } |
| |
| /** |
| * Append a textual representation of this type to the given builder. |
| * |
| * @param builder the builder to which the text is to be appended |
| */ |
| protected void appendTo(StringBuilder builder) { |
| if (name == null) { |
| builder.append("<unnamed type>"); |
| } else { |
| builder.append(name); |
| } |
| } |
| |
| protected abstract boolean internalEquals(Object object, Set<ElementPair> visitedElementPairs); |
| |
| protected abstract boolean internalIsMoreSpecificThan(Type type, boolean withDynamic, |
| Set<TypePair> visitedTypePairs); |
| |
| protected abstract boolean internalIsSubtypeOf(Type type, Set<TypePair> visitedTypePairs); |
| } |