/** * Instances of the class {@code FunctionTypeImpl} defines the behavior common to objects * representing the type of a function, method, constructor, getter, or setter. * * @coverage dart.engine.type */ public class FunctionTypeImpl extends TypeImpl implements FunctionType { /** * Return {@code true} if all of the name/type pairs in the first map are equal to the * corresponding name/type pairs in the second map. The maps are expected to iterate over their * entries in the same order in which those entries were added to the map. * * @param firstTypes the first map of name/type pairs being compared * @param secondTypes the second map of name/type pairs being compared * @return {@code true} if all of the name/type pairs in the first map are equal to the * corresponding name/type pairs in the second map */ private static boolean equals(Map<String, Type> firstTypes, Map<String, Type> secondTypes) { if (secondTypes.size() != firstTypes.size()) { return false; } Iterator<Map.Entry<String, Type>> firstIterator = firstTypes.entrySet().iterator(); Iterator<Map.Entry<String, Type>> secondIterator = firstTypes.entrySet().iterator(); while (firstIterator.hasNext()) { Map.Entry<String, Type> firstEntry = firstIterator.next(); Map.Entry<String, Type> secondEntry = secondIterator.next(); if (!firstEntry.getKey().equals(secondEntry.getKey()) || !firstEntry.getValue().equals(secondEntry.getValue())) { return false; } } return true; } /** * Return a map containing the results of using the given argument types and parameter types to * perform a substitution on all of the values in the given map. The order of the entries will be * preserved. * * @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 */ private static Map<String, Type> substitute( Map<String, Type> types, Type[] argumentTypes, Type[] parameterTypes) { if (types.isEmpty()) { return types; } LinkedHashMap<String, Type> newTypes = new LinkedHashMap<String, Type>(); for (Map.Entry<String, Type> entry : types.entrySet()) { newTypes.put(entry.getKey(), entry.getValue().substitute(argumentTypes, parameterTypes)); } return newTypes; } /** An array containing the actual types of the type arguments. */ private Type[] typeArguments = TypeImpl.EMPTY_ARRAY; /** * An array containing the types of the normal parameters of this type of function. The parameter * types are in the same order as they appear in the declaration of the function. * * @return the types of the normal parameters of this type of function */ private Type[] normalParameterTypes = TypeImpl.EMPTY_ARRAY; /** * A table mapping the names of optional (positional) parameters to the types of the optional * parameters of this type of function. */ private Type[] optionalParameterTypes = TypeImpl.EMPTY_ARRAY; /** * A table mapping the names of named parameters to the types of the named parameters of this type * of function. */ private Map<String, Type> namedParameterTypes = ImmutableMap.of(); /** The type of object returned by this type of function. */ private Type returnType = VoidTypeImpl.getInstance(); /** * Initialize a newly created function type to be declared by the given element and to have the * given name. * * @param element the element representing the declaration of the function type */ public FunctionTypeImpl(ExecutableElement element) { super(element, element == null ? null : element.getName()); } /** * Initialize a newly created function type to be declared by the given element and to have the * given name. * * @param element the element representing the declaration of the function type */ public FunctionTypeImpl(FunctionTypeAliasElement element) { super(element, element == null ? null : element.getName()); } @Override public boolean equals(Object object) { if (!(object instanceof FunctionTypeImpl)) { return false; } FunctionTypeImpl otherType = (FunctionTypeImpl) object; return ObjectUtilities.equals(getElement(), otherType.getElement()) && Arrays.equals(normalParameterTypes, otherType.normalParameterTypes) && Arrays.equals(optionalParameterTypes, otherType.optionalParameterTypes) && equals(namedParameterTypes, otherType.namedParameterTypes) && ObjectUtilities.equals(returnType, otherType.returnType); } @Override public Map<String, Type> getNamedParameterTypes() { return namedParameterTypes; } @Override public Type[] getNormalParameterTypes() { return normalParameterTypes; } @Override public Type[] getOptionalParameterTypes() { return optionalParameterTypes; } @Override public Type getReturnType() { return returnType; } @Override public Type[] getTypeArguments() { return typeArguments; } @Override public int hashCode() { Element element = getElement(); if (element == null) { return 0; } return element.hashCode(); } @Override public boolean isSubtypeOf(Type type) { // trivial base cases if (type == null) { return false; } else if (this == type || type.isDynamic() || type.isDartCoreFunction()) { return true; } else if (!(type instanceof FunctionType)) { return false; } else if (this.equals(type)) { return true; } FunctionType t = this; FunctionType s = (FunctionType) type; // normal parameter types if (t.getNormalParameterTypes().length != s.getNormalParameterTypes().length) { return false; } else if (t.getNormalParameterTypes().length > 0) { Type[] tTypes = t.getNormalParameterTypes(); Type[] sTypes = s.getNormalParameterTypes(); for (int i = 0; i < tTypes.length; i++) { if (!tTypes[i].isAssignableTo(sTypes[i])) { return false; } } } // optional parameter types if (t.getOptionalParameterTypes().length > 0) { Type[] tOpTypes = t.getOptionalParameterTypes(); Type[] sOpTypes = s.getOptionalParameterTypes(); // if k >= m is false, return false: the passed function type has more optional parameter // types than this if (tOpTypes.length < sOpTypes.length) { return false; } for (int i = 0; i < sOpTypes.length; i++) { if (!tOpTypes[i].isAssignableTo(sOpTypes[i])) { return false; } } if (t.getNamedParameterTypes().size() > 0 || s.getNamedParameterTypes().size() > 0) { return false; } } else if (s.getOptionalParameterTypes().length > 0) { return false; } // named parameter types if (t.getNamedParameterTypes().size() > 0) { Map<String, Type> namedTypesT = t.getNamedParameterTypes(); Map<String, Type> namedTypesS = s.getNamedParameterTypes(); // if k >= m is false, return false: the passed function type has more named parameter types // than this if (namedTypesT.size() < namedTypesS.size()) { return false; } // Loop through each element in S verifying that T has a matching parameter name and that the // corresponding type is assignable to the type in S. Iterator<Entry<String, Type>> iteratorS = namedTypesS.entrySet().iterator(); while (iteratorS.hasNext()) { Entry<String, Type> entryS = iteratorS.next(); Type typeT = namedTypesT.get(entryS.getKey()); if (typeT == null) { return false; } if (!entryS.getValue().isAssignableTo(typeT)) { return false; } } } else if (s.getNamedParameterTypes().size() > 0) { return false; } return s.getReturnType().equals(VoidTypeImpl.getInstance()) || t.getReturnType().isAssignableTo(s.getReturnType()); } /** * Set the mapping of the names of named parameters to the types of the named parameters of this * type of function to the given mapping. * * @param namedParameterTypes the mapping of the names of named parameters to the types of the * named parameters of this type of function */ public void setNamedParameterTypes(LinkedHashMap<String, Type> namedParameterTypes) { this.namedParameterTypes = namedParameterTypes; } /** * Set the types of the normal parameters of this type of function to the types in the given * array. * * @param normalParameterTypes the types of the normal parameters of this type of function */ public void setNormalParameterTypes(Type[] normalParameterTypes) { this.normalParameterTypes = normalParameterTypes; } /** * Set the types of the optional parameters of this type of function to the types in the given * array. * * @param optionalParameterTypes the types of the optional parameters of this type of function */ public void setOptionalParameterTypes(Type[] optionalParameterTypes) { this.optionalParameterTypes = optionalParameterTypes; } /** * Set the type of object returned by this type of function to the given type. * * @param returnType the type of object returned by this type of function */ public void setReturnType(Type returnType) { this.returnType = returnType; } /** * Set the actual types of the type arguments to the given types. * * @param typeArguments the actual types of the type arguments */ public void setTypeArguments(Type[] typeArguments) { this.typeArguments = typeArguments; } @Override public FunctionTypeImpl substitute(Type[] argumentTypes) { return substitute(argumentTypes, getTypeArguments()); } @Override public FunctionTypeImpl 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) { return this; } Element element = getElement(); FunctionTypeImpl newType = (element instanceof ExecutableElement) ? new FunctionTypeImpl((ExecutableElement) element) : new FunctionTypeImpl((FunctionTypeAliasElement) element); newType.setReturnType(returnType.substitute(argumentTypes, parameterTypes)); newType.setNormalParameterTypes( substitute(normalParameterTypes, argumentTypes, parameterTypes)); newType.setOptionalParameterTypes( substitute(optionalParameterTypes, argumentTypes, parameterTypes)); newType.namedParameterTypes = substitute(namedParameterTypes, argumentTypes, parameterTypes); return newType; } @Override protected void appendTo(StringBuilder builder) { builder.append("("); boolean needsComma = false; if (normalParameterTypes.length > 0) { for (Type type : normalParameterTypes) { if (needsComma) { builder.append(", "); } else { needsComma = true; } ((TypeImpl) type).appendTo(builder); } } if (optionalParameterTypes.length > 0) { if (needsComma) { builder.append(", "); needsComma = false; } builder.append("["); for (Type type : optionalParameterTypes) { if (needsComma) { builder.append(", "); } else { needsComma = true; } ((TypeImpl) type).appendTo(builder); } builder.append("]"); needsComma = true; } if (namedParameterTypes.size() > 0) { if (needsComma) { builder.append(", "); needsComma = false; } builder.append("{"); for (Map.Entry<String, Type> entry : namedParameterTypes.entrySet()) { if (needsComma) { builder.append(", "); } else { needsComma = true; } builder.append(entry.getKey()); builder.append(": "); ((TypeImpl) entry.getValue()).appendTo(builder); } builder.append("}"); needsComma = true; } builder.append(") -> "); if (returnType == null) { builder.append("null"); } else { ((TypeImpl) returnType).appendTo(builder); } } }
@Override public boolean isSubtypeOf(Type type) { // trivial base cases if (type == null) { return false; } else if (this == type || type.isDynamic() || type.isDartCoreFunction()) { return true; } else if (!(type instanceof FunctionType)) { return false; } else if (this.equals(type)) { return true; } FunctionType t = this; FunctionType s = (FunctionType) type; // normal parameter types if (t.getNormalParameterTypes().length != s.getNormalParameterTypes().length) { return false; } else if (t.getNormalParameterTypes().length > 0) { Type[] tTypes = t.getNormalParameterTypes(); Type[] sTypes = s.getNormalParameterTypes(); for (int i = 0; i < tTypes.length; i++) { if (!tTypes[i].isAssignableTo(sTypes[i])) { return false; } } } // optional parameter types if (t.getOptionalParameterTypes().length > 0) { Type[] tOpTypes = t.getOptionalParameterTypes(); Type[] sOpTypes = s.getOptionalParameterTypes(); // if k >= m is false, return false: the passed function type has more optional parameter // types than this if (tOpTypes.length < sOpTypes.length) { return false; } for (int i = 0; i < sOpTypes.length; i++) { if (!tOpTypes[i].isAssignableTo(sOpTypes[i])) { return false; } } if (t.getNamedParameterTypes().size() > 0 || s.getNamedParameterTypes().size() > 0) { return false; } } else if (s.getOptionalParameterTypes().length > 0) { return false; } // named parameter types if (t.getNamedParameterTypes().size() > 0) { Map<String, Type> namedTypesT = t.getNamedParameterTypes(); Map<String, Type> namedTypesS = s.getNamedParameterTypes(); // if k >= m is false, return false: the passed function type has more named parameter types // than this if (namedTypesT.size() < namedTypesS.size()) { return false; } // Loop through each element in S verifying that T has a matching parameter name and that the // corresponding type is assignable to the type in S. Iterator<Entry<String, Type>> iteratorS = namedTypesS.entrySet().iterator(); while (iteratorS.hasNext()) { Entry<String, Type> entryS = iteratorS.next(); Type typeT = namedTypesT.get(entryS.getKey()); if (typeT == null) { return false; } if (!entryS.getValue().isAssignableTo(typeT)) { return false; } } } else if (s.getNamedParameterTypes().size() > 0) { return false; } return s.getReturnType().equals(VoidTypeImpl.getInstance()) || t.getReturnType().isAssignableTo(s.getReturnType()); }