/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.parser; import gw.internal.gosu.parser.types.FunctionLiteralType; import gw.lang.parser.GosuParserFactory; import gw.lang.parser.GosuParserTypes; import gw.lang.parser.IGosuParser; import gw.lang.parser.IScriptPartId; import gw.lang.parser.ITypeUsesMap; import gw.lang.parser.ScriptabilityModifiers; import gw.lang.parser.StandardSymbolTable; import gw.lang.parser.TypeSystemAwareCache; import gw.lang.parser.TypeVarToTypeMap; import gw.lang.parser.exceptions.ParseResultsException; import gw.lang.parser.expressions.ITypeLiteralExpression; import gw.lang.parser.expressions.ITypeVariableDefinition; import gw.lang.reflect.FunctionType; import gw.lang.reflect.IErrorType; import gw.lang.reflect.IFunctionType; import gw.lang.reflect.IMetaType; import gw.lang.reflect.INonLoadableType; import gw.lang.reflect.IPlaceholder; import gw.lang.reflect.IType; import gw.lang.reflect.ITypeLoader; import gw.lang.reflect.ITypeVariableArrayType; import gw.lang.reflect.ITypeVariableType; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.gs.IGenericTypeVariable; import gw.lang.reflect.gs.IGosuClass; import gw.lang.reflect.gs.IGosuProgram; import gw.lang.reflect.java.IJavaClassInfo; import gw.lang.reflect.java.IJavaType; import gw.lang.reflect.java.JavaTypes; import gw.lang.reflect.java.asm.AsmClass; import gw.lang.reflect.java.asm.IAsmType; import gw.lang.reflect.module.IModule; import gw.util.GosuObjectUtil; import gw.util.Pair; import gw.util.concurrent.Cache; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.StringTokenizer; /** */ public class TypeLord { // LRUish cache of assignability results (recent tests indicate 99% hit rates) public static final TypeSystemAwareCache<Pair<IType, IType>, Boolean> ASSIGNABILITY_CACHE = TypeSystemAwareCache.make( "Assignability Cache", 1000, new Cache.MissHandler<Pair<IType, IType>, Boolean>() { public final Boolean load( Pair<IType, IType> key ) { return areGenericOrParameterizedTypesAssignableInternal( key.getFirst(), key.getSecond() ); } } ); public static Set<IType> getAllClassesInClassHierarchyAsIntrinsicTypes( IJavaClassInfo cls ) { Set<IJavaClassInfo> classSet = new HashSet<IJavaClassInfo>(); addAllClassesInClassHierarchy( cls, classSet ); Set<IType> intrinsicTypeSet = new HashSet<IType>(); intrinsicTypeSet.add(JavaTypes.OBJECT()); for(IJavaClassInfo classInfo : classSet) { intrinsicTypeSet.add( classInfo.getJavaType() ); } return intrinsicTypeSet; } public static Set<IType> getAllClassesInClassHierarchyAsIntrinsicTypes( IType type ) { HashSet<IType> typeSet = new HashSet<IType>(); addAllClassesInClassHierarchy( type, typeSet ); return typeSet; } public static boolean encloses( IType type, IType inner ) { return inner != null && (inner.getEnclosingType() == type || encloses( type, inner.getEnclosingType() )); } public static boolean enclosingTypeInstanceInScope( IType type, IGosuClassInternal inner ) { return inner != null && !inner.isStatic() && ((type != null && type.isAssignableFrom( inner.getEnclosingType() )) || enclosingTypeInstanceInScope( type, (IGosuClassInternal)inner.getEnclosingType() )); } public static Set<IType> getArrayVersionsOfEachType( Set componentTypes ) { Set<IType> allTypes = new HashSet<IType>(); allTypes.add( JavaTypes.OBJECT() ); Iterator it = componentTypes.iterator(); while( it.hasNext() ) { IType type = (IType)it.next(); allTypes.add( type.getArrayType() ); } return allTypes; } public static IType getActualType( Type type, TypeVarToTypeMap actualParamByVarName ) { return getActualType( type, actualParamByVarName, false ); } public static IType getActualType( Type type, TypeVarToTypeMap actualParamByVarName, boolean bKeepTypeVars ) { if( type instanceof Class ) { return TypeSystem.get( (Class)type ); } if( type instanceof TypeVariable ) { return actualParamByVarName.getByString( ((TypeVariable)type).getName() ); } return parseType( normalizeJavaTypeName( type ), actualParamByVarName, bKeepTypeVars, null ); } public static IType getActualType( IAsmType type, TypeVarToTypeMap actualParamByVarName ) { return getActualType( type, actualParamByVarName, false ); } public static IType getActualType( IAsmType type, TypeVarToTypeMap actualParamByVarName, boolean bKeepTypeVars ) { if( type instanceof AsmClass ) { return TypeSystem.getByFullNameIfValid( type.getName() ); } if( !type.isArray() && type.isTypeVariable() ) { return actualParamByVarName.getByString( type.getName() ); } return parseType( type.getFqn(), actualParamByVarName, bKeepTypeVars, null ); } private static String normalizeJavaTypeName( Type type ) { if( type instanceof Class ) { IType itype = TypeSystem.get( (Class<?>)type ); if ( itype == null ) { throw new RuntimeException( "Class " + type + " is not loadable through the TypeSystem!" ); } return itype.getName(); } if( type instanceof TypeVariable ) { return ((TypeVariable)type).getName(); } if( type instanceof ParameterizedType ) { ParameterizedType ptype = (ParameterizedType)type; String strName = normalizeJavaTypeName( ptype.getRawType() ); strName += "<"; boolean bFirst = true; for( Type param : ptype.getActualTypeArguments() ) { if( !bFirst ) { strName += ", "; } bFirst = false; strName += normalizeJavaTypeName( param ); } strName += ">"; return strName; } return fixSunInnerClassBug( type.toString() ); } public static IType getActualType( IType type, TypeVarToTypeMap actualParamByVarName ) { return getActualType( type, actualParamByVarName, false ); } public static IType getActualType( IType type, TypeVarToTypeMap actualParamByVarName, boolean bKeepTypeVars ) { int iArrayDims = 0; if( type != null && type.isArray() ) { for( iArrayDims = 0; type.isArray(); iArrayDims++ ) { type = type.getComponentType(); } } if( type instanceof TypeVariableType ) { TypeVariableType saveType = (TypeVariableType)type; type = actualParamByVarName.get( (ITypeVariableType)type ); if( type == null ) { if( bKeepTypeVars ) { type = saveType; } } else { if( !isParameterizedWith( type, saveType ) ) { type = getActualType( type, actualParamByVarName, bKeepTypeVars ); } else if( !bKeepTypeVars ) { type = getDefaultParameterizedTypeWithTypeVars( type ); } } } else if( type instanceof FunctionType) { if( !(type instanceof ErrorTypeInfo.UniversalFunctionType) ) { type = ((FunctionType)type).parameterize( (FunctionType)type, actualParamByVarName, bKeepTypeVars ); } } else if( type != null && type.isParameterizedType() ) { IType[] typeParams = type.getTypeParameters(); IType[] actualParamTypes = new IType[typeParams.length]; for( int i = 0; i < typeParams.length; i++ ) { IType actualType = getActualType(typeParams[i], actualParamByVarName, bKeepTypeVars); if (actualType == null) { actualType = JavaTypes.OBJECT(); } actualParamTypes[i] = actualType; } type = TypeLord.getPureGenericType( type ).getParameterizedType( actualParamTypes ); } if( iArrayDims > 0 && type != null ) { for( int j = 0; j < iArrayDims; j++ ) { type = type.getArrayType(); } } return type; } private static boolean isParameterizedWith( IType type, TypeVariableType typeVar ) { type = getCoreType( type ); if( type.equals( typeVar ) ) { return true; } if( type instanceof FunctionType ) { IFunctionType funType = (IFunctionType)type; IType[] types = funType.getParameterTypes(); for( IType param : types ) { if( isParameterizedWith( param, typeVar ) ) { return true; } } return isParameterizedWith( funType.getReturnType(), typeVar ); } else if( type.isParameterizedType() ) { for( IType typeParam : type.getTypeParameters() ) { if( isParameterizedWith( typeParam, typeVar ) ) { return true; } } } return false; } public static IType parseType( String strParameterizedTypeName, TypeVarToTypeMap actualParamByVarName ) { return parseType( strParameterizedTypeName, actualParamByVarName, null ); } public static IType parseType( String strParameterizedTypeName, TypeVarToTypeMap actualParamByVarName, ITypeUsesMap typeUsesMap ) { return parseType( strParameterizedTypeName, actualParamByVarName, false, typeUsesMap ); } private static IType parseType( String strParameterizedTypeName, TypeVarToTypeMap actualParamByVarName, boolean bKeepTypeVars, ITypeUsesMap typeUsesMap ) { try { ITypeLiteralExpression expression = parseTypeLiteral( strParameterizedTypeName, actualParamByVarName, bKeepTypeVars, typeUsesMap ); return expression.getType().getType(); } catch( ParseResultsException e ) { throw new RuntimeException( "Unable to parse the type " + strParameterizedTypeName + ". When attempting to parse this " + "as a type literal, a parse error occured.", e ); } } public static ITypeLiteralExpression parseTypeLiteral( String strParameterizedTypeName, TypeVarToTypeMap actualParamByVarName, boolean bKeepTypeVars, ITypeUsesMap typeUsesMap) throws ParseResultsException { StringTokenizer tokenizer = new StringTokenizer( strParameterizedTypeName, " <>[]?:(),", true ); StringBuilder sbType = new StringBuilder(); while( tokenizer.hasMoreTokens() ) { String strToken = tokenizer.nextToken(); IType type = actualParamByVarName.getByString( strToken ); String resolvedTypeName; if( type != null ) { if (type.isParameterizedType()) { type = resolveParameterizedType( type, actualParamByVarName, bKeepTypeVars ); } if( type instanceof TypeVariableType ) { type = bKeepTypeVars ? type : ((TypeVariableType)type).getBoundingType(); } if (type instanceof FunctionLiteralType) { type = type.getSupertype(); } resolvedTypeName = type instanceof TypeVariableType ? type.getRelativeName() : type.getName(); } else { resolvedTypeName = strToken; } boolean bFirstToken = sbType.length() == 0; if( bFirstToken ) { if( "?".equals( resolvedTypeName ) ) { resolvedTypeName = JavaTypes.OBJECT().getName(); } } sbType.append( resolvedTypeName ); } String strNormalizedType = sbType.toString().replace( "$", "." ); GosuParser parser = (GosuParser)GosuParserFactory.createParser( strNormalizedType, new StandardSymbolTable(), ScriptabilityModifiers.SCRIPTABLE ); if (typeUsesMap != null) { parser.setTypeUsesMap(typeUsesMap); } parser.pushIgnoreTypeDeprecation(); try { if( bKeepTypeVars ) { addTypeVars( actualParamByVarName, parser ); } return parser.parseTypeLiteral( null ); } finally { parser.popIgnoreTypeDeprecation(); } } private static IType resolveParameterizedType( IType parameterizedType, TypeVarToTypeMap actualParamByVarName, boolean bKeepTypeVars ) { List<IType> resolvedParams = new ArrayList<IType>(); for( IType paramType : parameterizedType.getTypeParameters() ) { if( paramType instanceof TypeVariableType && actualParamByVarName.containsKey( (ITypeVariableType)paramType ) ) { if( !bKeepTypeVars ) { resolvedParams.add( actualParamByVarName.get( (ITypeVariableType)paramType ) ); } else { resolvedParams.add( paramType ); } } else { if( paramType.isParameterizedType() ) { resolvedParams.add( resolveParameterizedType( paramType, actualParamByVarName, bKeepTypeVars ) ); } else { resolvedParams.add( paramType ); } } } return parameterizedType.getGenericType().getParameterizedType( resolvedParams.toArray( new IType[resolvedParams.size()] ) ); } private static void addTypeVars( TypeVarToTypeMap types, IGosuParser parser ) { for( Object passedInTvKey : types.keySet() ) { IType type = types.getRaw( passedInTvKey ); if( type instanceof TypeVariableType ) { ITypeVariableDefinition existingTv = parser.getTypeVariables().get( type.getName() ); if( existingTv == null || TypeVarToTypeMap.looseEquals( passedInTvKey, existingTv.getType() ) ) { parser.getTypeVariables().put( type.getRelativeName(), ((TypeVariableType)type).getTypeVarDef() ); } } else if( type.isParameterizedType() ) { TypeVarToTypeMap map = new TypeVarToTypeMap(); for( int i = 0; i < type.getTypeParameters().length; i++ ) { IType t = type.getTypeParameters()[i]; map.putRaw( String.valueOf( i ), t ); } addTypeVars( map, parser ); } } } public static TypeVarToTypeMap mapTypeByVarName( IType ownersType, IType declaringType ) { return mapTypeByVarName( ownersType, declaringType, false ); } public static TypeVarToTypeMap mapTypeByVarName( IType ownersType, IType declaringType, boolean bKeepTypeVars ) { TypeVarToTypeMap actualParamByVarName; ownersType = findActualDeclaringType( ownersType, declaringType ); if( ownersType != null && ownersType.isParameterizedType() ) { actualParamByVarName = mapActualTypeByVarName( ownersType ); } else { actualParamByVarName = mapGenericTypeByVarName( ownersType, bKeepTypeVars ); if( ownersType != null ) { while( ownersType.getEnclosingType() != null ) { ownersType = ownersType.getEnclosingType(); TypeVarToTypeMap vars = mapGenericTypeByVarName( ownersType, bKeepTypeVars ); if( actualParamByVarName.isEmpty() ) { actualParamByVarName = vars; } else { actualParamByVarName.putAll( vars ); } } } } return actualParamByVarName; } // If the declaring type is generic and the owning type is parameterized, we need to // find the corresponding parameterized type of the declaring type e.g. // // class Base<T> { // function blah() : Bar<T> {} // } // class Foo<T> extends Base<T> {} // // new Foo<String>().blah() // infer return type as Bar<String> // // The declaring class of blah() is generic class Base<T> (not a parameterized one), // while the owner's type is Foo<String>, thus in order to resolve the actual return // type for blah() we must walk the ancestry of Foo<String> until find the corresponding // parameterized type for Base<T>. private static IType findActualDeclaringType( IType ownersType, IType declaringType ) { if( ownersType == null ) { return null; } if( declaringType.isParameterizedType() && !declaringType.isGenericType() ) { return declaringType; } if( ownersType == declaringType ) { return declaringType; } if( ownersType.getGenericType() == declaringType ) { return ownersType; } IType actualDeclaringType = findActualDeclaringType( ownersType.getSupertype(), declaringType ); if( actualDeclaringType != null && actualDeclaringType != declaringType ) { return actualDeclaringType; } for( IType iface : ownersType.getInterfaces() ) { actualDeclaringType = findActualDeclaringType( iface, declaringType ); if( actualDeclaringType != null && actualDeclaringType != declaringType ) { return actualDeclaringType; } } return declaringType; } private static TypeVarToTypeMap mapActualTypeByVarName( IType ownersType ) { TypeVarToTypeMap actualParamByVarName = new TypeVarToTypeMap(); IGenericTypeVariable[] vars = ownersType.getGenericType().getGenericTypeVariables(); if (vars != null) { IType[] paramArgs = ownersType.getTypeParameters(); for( int i = 0; i < vars.length; i++ ) { IGenericTypeVariable typeVar = vars[i]; if( paramArgs.length > i ) { actualParamByVarName.put( typeVar.getTypeVariableDefinition().getType(), paramArgs[i] ); } } } return actualParamByVarName; } private static TypeVarToTypeMap mapGenericTypeByVarName( IType ownersType, boolean bKeepTypeVars ) { TypeVarToTypeMap genericParamByVarName = TypeVarToTypeMap.EMPTY_MAP; IType genType = null; if( ownersType != null ) { genType = ownersType.getGenericType(); } if( genType != null ) { genericParamByVarName = new TypeVarToTypeMap(); IGenericTypeVariable[] vars = genType.getGenericTypeVariables(); if( vars != null ) { for( int i = 0; i < vars.length; i++ ) { IGenericTypeVariable typeVar = vars[i]; Object key = typeVar.getTypeVariableDefinition() == null ? typeVar.getName() : typeVar.getTypeVariableDefinition().getType(); if( !genericParamByVarName.containsKeyRaw( key ) ) { genericParamByVarName.putRaw( key, bKeepTypeVars ? new TypeVariableType( ownersType, typeVar ) : typeVar.getBoundingType() ); } } } } return genericParamByVarName; } public static String getNameWithQualifiedTypeVariables( IType type, boolean includeModules ) { if( type.isArray() ) { return getNameWithQualifiedTypeVariables( type.getComponentType(), includeModules ) + "[]"; } else if( type.isParameterizedType() ) { String strParams = getNameOfParams( type.getTypeParameters(), false, true, includeModules ); return getPureGenericType( type ).getName() + strParams; } else if( type instanceof TypeVariableType ) { if( type.getEnclosingType() != null ) { return ((TypeVariableType)type).getNameWithEnclosingType(); } } return type.getName(); } public static String getNameOfParams( IType[] paramTypes, boolean bRelative, boolean bWithEnclosingType ) { return getNameOfParams(paramTypes, bRelative, bWithEnclosingType, false); } public static String getNameOfParams( IType[] paramTypes, boolean bRelative, boolean bWithEnclosingType, boolean bIncludeModule ) { StringBuilder sb = new StringBuilder( "<" ); for( int i = 0; i < paramTypes.length; i++ ) { IType paramType = paramTypes[i]; if( bRelative ) { sb.append( paramType.getRelativeName() ); } else { if( bWithEnclosingType && paramType instanceof TypeVariableType ) { TypeVariableType type = (TypeVariableType)paramType; if( type.getEnclosingType() != null ) { if( bIncludeModule && !(type.getEnclosingType() instanceof INonLoadableType) ) { sb.append( type.getEnclosingType().getTypeLoader().getModule().getName() + "." ); } sb.append( type.getNameWithEnclosingType() ); } else { sb.append( type.getName() ); } } else if( bWithEnclosingType && paramType.isParameterizedType() ) { sb.append( paramType.getGenericType().getName() ); sb.append( getNameOfParams( paramType.getTypeParameters(), bRelative, bWithEnclosingType, bIncludeModule ) ); } else { if( bIncludeModule && !(paramType instanceof INonLoadableType) ) { ITypeLoader typeLoader = paramType.getTypeLoader(); if (typeLoader != null) { IModule oldModule = typeLoader.getModule(); if (oldModule != null) { sb.append( oldModule.getName() + "." ); } } } sb.append( paramType.getName() ); } } if( i < paramTypes.length - 1 ) { sb.append( ", " ); } } sb.append( '>' ); return sb.toString(); } /** * Finds a parameterized type in the ancestry of a given type. For instance, * given the type for ArrayList<Person> as the sourceType and List as * the rawGenericType, returns List<Person>. * * @param sourceType The type to search in. * @param rawGenericType The raw generic type of the parameterized type to * search for e.g., List is the raw generic type of List<String>. * * @return A parameterization of rawGenericType corresponding with the type * params of sourceType. */ public static IType findParameterizedType( IType sourceType, IType rawGenericType ) { if( sourceType == null ) { return null; } rawGenericType = getPureGenericType( rawGenericType ); if( //sourceType.isParameterizedType() && sourceType.getGenericType() == rawGenericType ) { return sourceType; } IType parameterizedType = findParameterizedType( sourceType.getSupertype(), rawGenericType ); if( parameterizedType != null ) { return parameterizedType; } IType[] interfaces = sourceType.getInterfaces(); for (int i = 0; i < interfaces.length; i++) { IType iface = interfaces[i]; parameterizedType = findParameterizedType( iface, rawGenericType ); if( parameterizedType != null ) { return parameterizedType; } } return null; } // Todo: the above method is nearly identical to this one. lets see about combining them public static IType findParameterizedTypeInHierarchy( IType sourceType, IType rawGenericType ) { if( sourceType == null ) { return null; } if( sourceType.isParameterizedType() && sourceType.getGenericType().equals( rawGenericType ) ) { return sourceType; } IType[] list = sourceType.getInterfaces(); for( int i = 0; i < list.length; i++ ) { IType returnType = findParameterizedTypeInHierarchy( list[i], rawGenericType ); if( returnType != null ) { return returnType; } } return findParameterizedTypeInHierarchy( sourceType.getSupertype(), rawGenericType ); } public static void addAllClassesInClassHierarchy( Class entityClass, Set<Class> set ) { if( !set.add( entityClass ) ) { return; } Class[] interfaces = entityClass.getInterfaces(); for( int i = 0; i < interfaces.length; i++ ) { addAllClassesInClassHierarchy( interfaces[i], set ); } if( entityClass.getSuperclass() != null ) { addAllClassesInClassHierarchy( entityClass.getSuperclass(), set ); } } private static void addAllClassesInClassHierarchy( IJavaClassInfo entityClass, Set<IJavaClassInfo> set ) { if( !set.add( entityClass ) ) { return; } IJavaClassInfo[] interfaces = entityClass.getInterfaces(); for( int i = 0; i < interfaces.length; i++ ) { addAllClassesInClassHierarchy( interfaces[i], set ); } if( entityClass.getSuperclass() != null ) { addAllClassesInClassHierarchy( entityClass.getSuperclass(), set ); } } public static void addAllClassesInClassHierarchy( IType type, Set<IType> set ) { addAllClassesInClassHierarchy( type, set, false ); } public static void addAllClassesInClassHierarchy( IType type, Set<IType> set, boolean bForce ) { if( !set.add( type ) && !bForce ) { return; } boolean bFromStructure = type instanceof IGosuClass && ((IGosuClass)type).isStructure(); for( IType iface : type.getInterfaces() ) { if( !bFromStructure || !(iface instanceof IGosuClass) || ((IGosuClass)iface).isStructure() ) { addAllClassesInClassHierarchy( iface, set ); } } if( type.getSupertype() != null ) { addAllClassesInClassHierarchy( type.getSupertype(), set ); } if( type.isParameterizedType() ) { addAllClassesInClassHierarchy( type.getGenericType(), set ); } } public static <E extends IType> E getPureGenericType( E type ) { if ( type == null || TypeSystem.isDeleted(type)) { return null; } if( type.isArray() ) { return (E)getPureGenericType( type.getComponentType() ).getArrayType(); } while( type.isParameterizedType() ) { //noinspection unchecked type = (E)type.getGenericType(); } return type; } public static IType makeDefaultParameterizedType( IType type ) { return makeDefaultParameterizedType( type, false ); } public static IType makeDefaultParameterizedType( IType type, boolean bHandleRecursiveParameterizedType ) { if( type != null && !(type instanceof IGosuEnhancementInternal) && type.isGenericType() && (!type.isParameterizedType() || (bHandleRecursiveParameterizedType && isRecursiveType( type, type.getTypeParameters() ))) ) { if( type instanceof MetaType ) { return MetaType.DEFAULT_TYPE_TYPE.get(); } IType[] boundingTypes = new IType[type.getGenericTypeVariables().length]; for( int i = 0; i < boundingTypes.length; i++ ) { boundingTypes[i] = type.getGenericTypeVariables()[i].getBoundingType(); if( bHandleRecursiveParameterizedType && boundingTypes[i].isParameterizedType() && isRecursiveType( boundingTypes[i], boundingTypes[i].getTypeParameters() ) ) { boundingTypes[i] = getDefaultParameterizedType( boundingTypes[i] ); } } if( boundingTypes.length == 0 || TypeLord.isRecursiveType( type, boundingTypes ) ) { return type; } type = type.getParameterizedType( boundingTypes ); } return type; } public static IType replaceTypeVariableTypeParametersWithBoundingTypes( IType type ) { return replaceTypeVariableTypeParametersWithBoundingTypes( type, null ); } public static IType replaceTypeVariableTypeParametersWithBoundingTypes( IType type, IType enclType ) { if( type instanceof ITypeVariableType ) { if( enclType != null && enclType.isParameterizedType() ) { TypeVarToTypeMap map = mapTypeByVarName( enclType, enclType ); return replaceTypeVariableTypeParametersWithBoundingTypes( getActualType( ((ITypeVariableType)type).getBoundingType(), map, true ) ); } return replaceTypeVariableTypeParametersWithBoundingTypes( ((ITypeVariableType)type).getBoundingType(), enclType ); } if( type.isArray() ) { return replaceTypeVariableTypeParametersWithBoundingTypes( type.getComponentType(), enclType ).getArrayType(); } if( type.isParameterizedType() ) { IType[] typeParams = type.getTypeParameters(); if( TypeLord.isRecursiveType( type, typeParams ) ) { return type; } IType[] concreteParams = new IType[typeParams.length]; for( int i = 0; i < typeParams.length; i++ ) { concreteParams[i] = replaceTypeVariableTypeParametersWithBoundingTypes( typeParams[i], enclType ); } type = type.getParameterizedType( concreteParams ); } else { if( type.isGenericType() ) { IType[] boundingTypes = new IType[type.getGenericTypeVariables().length]; for( int i = 0; i < boundingTypes.length; i++ ) { boundingTypes[i] = type.getGenericTypeVariables()[i].getBoundingType(); } if( TypeLord.isRecursiveType( type, boundingTypes ) ) { return type; } for( int i = 0; i < boundingTypes.length; i++ ) { boundingTypes[i] = replaceTypeVariableTypeParametersWithBoundingTypes( boundingTypes[i], enclType ); } type = type.getParameterizedType( boundingTypes ); } } return type; } public static IType getDefaultParameterizedType( IType type ) { if( type.isArray() ) { return getDefaultParameterizedType( type.getComponentType() ).getArrayType(); } if( type instanceof CompoundType ) { return makeDefaultParameterizedTypeForCompoundType( type ); } if( !type.isGenericType() && !type.isParameterizedType() ) { return type; } type = getPureGenericType( type ); return makeDefaultParameterizedType( type ); } private static IType makeDefaultParameterizedTypeForCompoundType( IType type ) { IType[] defCompTypes = new IType[type.getCompoundTypeComponents().size()]; int i = 0; boolean bDifferent = false; for( IType compType: type.getCompoundTypeComponents() ) { defCompTypes[i++] = getDefaultParameterizedType( compType ); bDifferent = bDifferent || defCompTypes[i] != compType; } if( bDifferent ) { type = CompoundType.get( defCompTypes ); } return type; } public static IType getDefaultParameterizedTypeWithTypeVars( IType type ) { if( type.isArray() ) { return getDefaultParameterizedTypeWithTypeVars( type.getComponentType() ).getArrayType(); } if( type instanceof ITypeVariableType ) { return getDefaultParameterizedTypeWithTypeVars( ((ITypeVariableType)type).getBoundingType() ); } if( type instanceof ITypeVariableArrayType ) { return getDefaultParameterizedTypeWithTypeVars( ((ITypeVariableType)type.getComponentType()).getBoundingType() ).getArrayType(); } if( !type.isGenericType() && !type.isParameterizedType() ) { return type; } type = getPureGenericType( type ); return makeDefaultParameterizedType( type ); } public static boolean isSubtype( IType subtype, IType supertype ) { if( subtype == null ) { return false; } // Make sure we're dealing with pure types before doing any checks subtype = getPureGenericType( subtype ); supertype = getPureGenericType( supertype ); if( supertype instanceof IGosuClassInternal ) { if( ((IGosuClassInternal)supertype).isSubClass( subtype ) ) { return true; } } IType st = getPureGenericType( subtype.getSupertype() ); return st == supertype || isSubtype( st, supertype ); } static String fixSunInnerClassBug( String type ) { int i = type.indexOf( "$", 0 ); if( i < 0 ) { return type; } else { int n = 0; i = 0; StringBuilder sb = new StringBuilder( type ); while( i >= 0 ) { i = findNthPositionOfString( n, sb, "$" ); removeDuplicateClassName( sb, i ); n++; } return sb.toString(); } } private static int findNthPositionOfString( int n, StringBuilder sb, String str ) { int count = 0; int i = 0; while( count <= n ) { i = sb.indexOf( str, i + 1 ); count++; } return i; } static void removeDuplicateClassName( StringBuilder sb, int dollarSignPosition ) { StringBuilder foundBuffer = new StringBuilder(); boolean chopped = false; int start = dollarSignPosition - 1; while( start >= 0 ) { foundBuffer.append( sb.charAt( start ) ); if( repeatsWithDot( foundBuffer ) && !chopped ) { foundBuffer.setLength( foundBuffer.length() / 2 ); chopped = true; break; } start--; } if( chopped ) { sb.replace( start, dollarSignPosition, foundBuffer.reverse().toString() ); } } static boolean repeatsWithDot( StringBuilder sb ) { if( sb == null || sb.length() % 2 == 0 ) { return false; } int halfPoint = sb.length() / 2; if( sb.charAt( halfPoint ) != '.' ) { return false; } for( int i = 0; i < halfPoint; i++ ) { if( sb.charAt( i ) != sb.charAt( i + halfPoint + 1 ) ) { return false; } } return true; } public static boolean areGenericOrParameterizedTypesAssignable( IType to, IType from ) { return ASSIGNABILITY_CACHE.get(Pair.make(to, from)); } private static boolean areGenericOrParameterizedTypesAssignableInternal( IType to, IType from ) { if( from instanceof CompoundType ) { Set<IType> types = ((CompoundType)from).getTypes(); for( IType type : types ) { if( areGenericOrParameterizedTypesAssignable( to, type ) ) { return true; } } return false; } else if( to.isParameterizedType() || to.isGenericType() ) { IType relatedParameterizedType = findParameterizedType( from, to.getGenericType() ); if( relatedParameterizedType == null ) { if( from == to.getGenericType() ) { // Handle case List<Object> assignable from List relatedParameterizedType = from; } else { return false; } } if( from.isGenericType() && to.isParameterizedType() && ((!from.isParameterizedType() && (to == makeDefaultParameterizedType( from ) || // Handle recursive type Foo where we want Foo<Foo> assignable from Foo isRecursiveType( getPureGenericType( to ), to.getTypeParameters() ) && getPureGenericType( to ) == from)) || sameAsDefaultProxiedType( to, from )) ) { //## todo: this is a hack so that we can assign a generic type to its default parameterized type e.g., // var listOfObj = List<Object>; var list = List; listOfObj = list; // // Why support this? Primarily because type information coming from Java and other types // may have generic types e.g., a java class with method, List getMyList(), is exposed // with a return type of List, not List<Object>. Since an unparamaterized Gosu type // literal is parsed as its default parameterized type (List parses as List<Object>) we // would otherwise have a lot of potential unintentional errors. return true; } IType[] paramTypesFrom = relatedParameterizedType.getTypeParameters(); IType[] typeParams = to.getTypeParameters(); if( typeParams != null ) { for( int i = 0; i < typeParams.length; i++ ) { IType paramType = typeParams[i]; //## Leaving this commented out instead of deleting it to prevent it from coming back. // if( paramType instanceof TypeVariableType ) // { // paramType = ((TypeVariableType)paramType).getTypeVarDef().getTypeVar().getBoundingTypes()[0]; // } if( paramType != JavaTypes.OBJECT() && (paramTypesFrom == null || paramTypesFrom.length <= i || !paramType.isAssignableFrom( paramTypesFrom[i] )) ) { return false; } } } return true; } return false; } private static boolean sameAsDefaultProxiedType( IType to, IType from ) { if( to instanceof IJavaTypeInternal && from instanceof IGosuClassInternal && ((IGosuClassInternal)from).isProxy() ) { IJavaType proxiedType = ((IGosuClassInternal)from).getJavaType(); return proxiedType != null && (TypeLord.makeDefaultParameterizedType( proxiedType ) == to || TypeLord.replaceTypeVariableTypeParametersWithBoundingTypes( proxiedType ) == to); } return false; } public static Set<String> getNamespacesFromTypeNames( Set<? extends CharSequence> typeNames, Set<String> namespaces ) { for( CharSequence typeName : typeNames ) { String strName = typeName.toString(); int iIndex = strName.lastIndexOf( '.' ); if( iIndex > 0 ) { String strPossibleEnclosingTypeName = strName.substring( 0, iIndex ); if( typeNames.contains( strPossibleEnclosingTypeName ) ) { // Don't add the enclosing type of an inner class as a namespace continue; } } addNamespace( namespaces, strName ); } return namespaces; } public static void addNamespace( Set<String> namespaces, String strType ) { int iIndex = strType.lastIndexOf( '.' ); if( iIndex > 0 ) { String strPackage = strType.substring( 0, iIndex ); if( namespaces.add( strPackage ) ) { // Add parent namespaces i.e. a namespace may not have a class // e.g. the java namespace has no direct classes, so we have to // add it here. addNamespace( namespaces, strPackage ); } } } public static IType getRootType(IType type) { IType result = type; while (result.getSupertype() != null || result.getSupertype() != JavaTypes.OBJECT()) { result = result.getSupertype(); } return result; } public static IType findLeastUpperBound( List<? extends IType> types ) { return findLeastUpperBoundImpl( types, new HashSet<IType>() ); } private static IType findLeastUpperBoundImpl( List<? extends IType> types, Set<IType> resolvingTypes ) { // Optimization 1: if there are no types, return void if( types.size() == 0 ) { return JavaTypes.pVOID(); } // Optimization 2: if there is one type, return that type else if( types.size() == 1 ) { return types.get( 0 ); } // Optimization 3: if there is only one type in the list, return that type IType type = null; boolean disJointTypes = false; boolean foundOnlyNullTypes = true; for( Iterator<? extends IType> it = types.iterator(); it.hasNext(); ) { IType iIntrinsicType = it.next(); //nuke null types, which don't contribute to the type if( iIntrinsicType.equals( GosuParserTypes.NULL_TYPE() ) ) { continue; } foundOnlyNullTypes = false; if( type == null ) { type = iIntrinsicType; } if( !type.equals( iIntrinsicType ) && !BeanAccess.isBoxedTypeFor( type, iIntrinsicType ) && !BeanAccess.isBoxedTypeFor( iIntrinsicType, type ) ) { disJointTypes = true; break; } } if( foundOnlyNullTypes ) { return GosuParserTypes.NULL_TYPE(); } if( !disJointTypes ) { return type; } // Short circuit recursive LUBs for( IType iType : types ) { if( resolvingTypes.contains( iType ) ) { return JavaTypes.OBJECT(); } } resolvingTypes.addAll( types ); // OK, we have disjoint types, so we need to do the full-monty LUB analysis IType seedType = types.get( 0 ); Set<IType> lubSet = new HashSet<IType>( seedType.getAllTypesInHierarchy() ); for( int i = 1; i < types.size(); i++ ) { IType iIntrinsicType = types.get( i ); lubSet.retainAll( iIntrinsicType.getAllTypesInHierarchy() ); } pruneNonLUBs( lubSet ); if( lubSet.size() == 0 ) { /* If there is no common types, return Object */ return JavaTypes.OBJECT(); } lubSet = findParameterizationLUBS( types, lubSet, resolvingTypes ); if( lubSet.size() == 1 ) { /* If there is a single, unabiguous LUB type, return that */ return lubSet.iterator().next(); } else { return CompoundType.get( lubSet ); } } private static Set<IType> findParameterizationLUBS( List<? extends IType> currentTypes, Set<IType> lubSet, Set<IType> resolvingTypes ) { Set<IType> returnSet = new HashSet<IType>(); for( IType lub : lubSet ) { if( lub.isGenericType() && !lub.isParameterizedType() && currentTypes.size() > 1 ) { ArrayList<List<IType>> typeParametersByPosition = new ArrayList<List<IType>>( lub.getGenericTypeVariables().length ); for( int i = 0; i < lub.getGenericTypeVariables().length; i++ ) { typeParametersByPosition.add( new ArrayList<IType>() ); } for( IType initialType : currentTypes ) { IType parameterizedType = findParameterizedType( initialType, lub ); if (parameterizedType == null) { System.out.println("*** ERROR: parameteredType is null"); System.out.println("*** initialType is " + initialType); System.out.println("*** lub is " + lub); for (IType t : currentTypes) { System.out.println("*** currentTypes contains " + t); } for (IType t : lubSet) { System.out.println("*** lubSet contains " + t); } } if( !parameterizedType.isParameterizedType() ) { parameterizedType = getDefaultParameterizedType( initialType ); } if( parameterizedType.isParameterizedType() ) { for( int i = 0; i < parameterizedType.getTypeParameters().length; i++ ) { IType parameter = parameterizedType.getTypeParameters()[i]; typeParametersByPosition.get( i ).add( parameter ); } } } ArrayList<IType> paramLubs = new ArrayList<IType>(); for( List<IType> paramterTypesAtPositionI : typeParametersByPosition ) { IType leastUpperBound = findLeastUpperBoundImpl( paramterTypesAtPositionI, resolvingTypes ); paramLubs.add( leastUpperBound ); } IType finalType = lub.getParameterizedType( paramLubs.toArray( new IType[paramLubs.size()] ) ); returnSet.add( finalType ); } else { returnSet.add( lub ); } } return returnSet; } private static void pruneNonLUBs( Set<IType> typeSet ) { for( Iterator<IType> outerIterator = typeSet.iterator(); outerIterator.hasNext(); ) { IType typeToPossiblyRemove = outerIterator.next(); for( IType otherType : typeSet ) { //noinspection SuspiciousMethodCalls if( otherType.getAllTypesInHierarchy().contains( typeToPossiblyRemove ) && !typeToPossiblyRemove.getAllTypesInHierarchy().contains( otherType ) ) { outerIterator.remove(); break; } } } } public static boolean isRecursiveType( IJavaType javaType ) { return getDefaultParameterizedType( javaType ).isGenericType(); } public static boolean isRecursiveType( IType subject, IType[] types ) { if( subject instanceof CompoundType ) { for( IType compType : subject.getCompoundTypeComponents() ) { if( isParameterizedType( compType ) && isRecursiveType( compType, compType.getTypeParameters() ) ) { return true; } } } for( IType csr : types ) { if( getPureGenericType( csr ).equals( getPureGenericType( subject ) ) ) { // Short-circuit recursive type parameterization e.g., class Foo<T extends Foo<T>> return true; } else if( csr instanceof CompoundType ) { Set<IType> compoundTypeComponents = csr.getCompoundTypeComponents(); IType[] typesArr = compoundTypeComponents.toArray( new IType[compoundTypeComponents.size()] ); if( isRecursiveType( subject, typesArr ) ) { return true; } } else if( csr.isParameterizedType() ) { if( isRecursiveType( subject, csr.getTypeParameters() ) ) { return true; } } else if( csr instanceof TypeVariableType ) { if( isRecursiveType( csr, new IType[] {((TypeVariableType)csr).getBoundingType()} ) ) { return true; } if( isRecursiveType( subject, new IType[] {((TypeVariableType)csr).getBoundingType()} ) ) { return true; } } else if( csr.isArray() ) { if( isRecursiveType( subject, new IType[] {csr.getComponentType()} ) ) { return true; } } } return false; } public static IJavaClassInfo getOuterMostEnclosingClass(IJavaClassInfo innerClass) { IJavaClassInfo outerMost = innerClass; while( outerMost.getEnclosingType() != null ) { outerMost = outerMost.getEnclosingClass(); } return outerMost; } public static IType getOuterMostEnclosingClass( IType innerClass ) { IType outerMost = innerClass; while( outerMost.getEnclosingType() != null && !isEvalProgram( outerMost ) ) { outerMost = outerMost.getEnclosingType(); if (TypeSystem.isDeleted(outerMost)) { return null; } } return outerMost; } public static boolean isParameterizedType( IType type ) { if( type.isParameterizedType() ) { return true; } if( type instanceof CompoundType ) { for( IType compType : type.getCompoundTypeComponents() ) { if( isParameterizedType( compType ) ) { return true; } } } return false; } public static boolean isEvalProgram( IType type ) { return (type instanceof IGosuProgram) && ((IGosuProgram)type).isAnonymous(); } public static void addReferencedTypeVarsThatAreNotInMap( IType type, TypeVarToTypeMap map, boolean bKeepTypeVars ) { if( type instanceof TypeVariableType ) { IType existingType = map.get( (TypeVariableType)type ); if( existingType == null ) { if( bKeepTypeVars ) { map.put( (ITypeVariableType)type, type ); } else { map.put( (ITypeVariableType)type, ((TypeVariableType)type).getBoundingType() ); } } } else if( type.isParameterizedType() ) { for( IType typeParam : type.getTypeParameters() ) { addReferencedTypeVarsThatAreNotInMap( typeParam, map, bKeepTypeVars ); } } else if( type.isArray() ) { addReferencedTypeVarsThatAreNotInMap( type.getComponentType(), map, bKeepTypeVars ); } else if( type instanceof IFunctionType ) { IFunctionType funType = (IFunctionType)type; IType[] types = funType.getParameterTypes(); for( IType iType : types ) { addReferencedTypeVarsThatAreNotInMap( iType, map, bKeepTypeVars ); } addReferencedTypeVarsThatAreNotInMap( funType.getReturnType(), map, bKeepTypeVars ); } } public static boolean hasTypeVariable( IType type ) { if( type == null ) { return false; } else if( type instanceof TypeVariableType ) { return true; } else if( type instanceof TypeVariableArrayType ) { return true; } else if( type.isParameterizedType() ) { IType[] compileTimeTypeParams = type.getTypeParameters(); for( int i = 0; i < compileTimeTypeParams.length; i++ ) { IType ctTypeParam = compileTimeTypeParams[i]; if( hasTypeVariable( ctTypeParam ) ) { return true; } } } else if( type instanceof FunctionType ) { IFunctionType funType = (IFunctionType)type; IType[] types = funType.getParameterTypes(); for( IType param : types ) { if( hasTypeVariable( param ) ) { return true; } } if( hasTypeVariable( funType.getReturnType() ) ) { return true; } } return false; } public static boolean isExpandable( IType type ) { return getExpandableComponentType( type ) != null; } public static IType getExpandableComponentType( IType type ) { IType retType = null; if( type.isArray() ) { retType = type.getComponentType(); } else if( JavaTypes.INTEGER_INTERVAL().isAssignableFrom( type ) ) { retType = JavaTypes.pINT(); } else if( JavaTypes.LONG_INTERVAL().isAssignableFrom( type ) ) { retType = JavaTypes.pLONG(); } else if( type instanceof IPlaceholder && ((IPlaceholder)type).isPlaceholder() ) { retType = type.getComponentType(); } else { IType parameterized = TypeLord.findParameterizedType( type, JavaTypes.ITERABLE() ); if( parameterized != null && parameterized.isParameterizedType() ) { retType = parameterized.getTypeParameters()[0]; } else { parameterized = TypeLord.findParameterizedType( type, JavaTypes.ITERATOR() ); if( parameterized != null && parameterized.isParameterizedType() ) { retType = parameterized.getTypeParameters()[0]; } } } return retType; } /** */ public static void inferTypeVariableTypesFromGenParamTypeAndConcreteType( IType genParamType, IType argType, TypeVarToTypeMap inferenceMap ) { if( argType == GosuParserTypes.NULL_TYPE() || argType instanceof IErrorType || (argType instanceof IMetaType && ((IMetaType)argType).getType() instanceof IErrorType) ) { return; } if( argType.isPrimitive() ) { argType = getBoxedTypeFromPrimitiveType( argType ); } if( genParamType.isArray() ) { if ( !argType.isArray()) { return; } //## todo: DON'T allow a null component type here; we do it now as a hack that enables gosu arrays to be compatible with java arrays //## todo: same as JavaMethodInfo.inferTypeVariableTypesFromGenParamTypeAndConcreteType() if( argType.getComponentType() == null || !argType.getComponentType().isPrimitive() ) { inferTypeVariableTypesFromGenParamTypeAndConcreteType( genParamType.getComponentType(), argType.getComponentType(), inferenceMap ); } } else if( genParamType.isParameterizedType() ) { IType argTypeInTermsOfParamType = findParameterizedType( argType, genParamType.getGenericType() ); if( argTypeInTermsOfParamType == null ) { return; } IType[] concreteTypeParams = argTypeInTermsOfParamType.getTypeParameters(); if( concreteTypeParams != null && concreteTypeParams.length > 0 ) { int i = 0; for( IType typeArg : genParamType.getTypeParameters() ) { inferTypeVariableTypesFromGenParamTypeAndConcreteType( typeArg, concreteTypeParams[i++], inferenceMap ); } } } else if( genParamType instanceof ITypeVariableType && argType != GosuParserTypes.NULL_TYPE() ) { ITypeVariableType tvType = ((ITypeVariableType)genParamType).getTypeVarDef().getType(); IType type = inferenceMap.get( tvType ); if( type == null || type instanceof ITypeVariableType ) { // Infer the type inferenceMap.put( tvType, argType ); } IType boundingType = ((ITypeVariableType)genParamType).getBoundingType(); if( !isRecursiveType( genParamType, new IType[]{boundingType} ) ) { inferTypeVariableTypesFromGenParamTypeAndConcreteType( boundingType, argType, inferenceMap ); } } else if( genParamType instanceof FunctionType ) { FunctionType genBlockType = (FunctionType)genParamType; if( !(argType instanceof FunctionType) ) { // argType may not be symetric with getParamType if the enclosing expr is errant return; } inferTypeVariableTypesFromGenParamTypeAndConcreteType( genBlockType.getReturnType(), ((FunctionType)argType).getReturnType(), inferenceMap ); IType[] genBlockParamTypes = genBlockType.getParameterTypes(); if( genBlockParamTypes != null ) { IType[] argTypeParamTypes = ((FunctionType)argType).getParameterTypes(); if( argTypeParamTypes.length == genBlockParamTypes.length ) { for( int i = 0; i < genBlockParamTypes.length; i++ ) { inferTypeVariableTypesFromGenParamTypeAndConcreteType( genBlockParamTypes[i], ((FunctionType)argType).getParameterTypes()[i], inferenceMap ); } } } } } public static IType getConcreteType( IType type ) { if( type.isGenericType() && !type.isParameterizedType() ) { IGenericTypeVariable[] genTypeVars = type.getGenericTypeVariables(); IType[] typeVarTypes = new IType[genTypeVars.length]; for( int i = 0; i < typeVarTypes.length; i++ ) { typeVarTypes[i] = genTypeVars[i].getTypeVariableDefinition().getType(); } type = type.getParameterizedType( typeVarTypes ); } return type; } public static IType getCoreType( IType type ) { if (TypeSystem.isDeleted(type)) { return null; } if( type.isArray() ) { return getCoreType( type.getComponentType() ); } return type; } public static IType getBoxedTypeFromPrimitiveType( IType primitiveType ) { IType boxedType; if( primitiveType == JavaTypes.pBOOLEAN() ) { boxedType = JavaTypes.BOOLEAN(); } else if( primitiveType == JavaTypes.pBYTE() ) { boxedType = JavaTypes.BYTE(); } else if( primitiveType == JavaTypes.pCHAR() ) { boxedType = JavaTypes.CHARACTER(); } else if( primitiveType == JavaTypes.pSHORT() ) { boxedType = JavaTypes.SHORT(); } else if( primitiveType == JavaTypes.pINT() ) { boxedType = JavaTypes.INTEGER(); } else if( primitiveType == JavaTypes.pLONG() ) { boxedType = JavaTypes.LONG(); } else if( primitiveType == JavaTypes.pFLOAT() ) { boxedType = JavaTypes.FLOAT(); } else if( primitiveType == JavaTypes.pDOUBLE() ) { boxedType = JavaTypes.DOUBLE(); } else if( primitiveType == JavaTypes.pVOID() ) { boxedType = JavaTypes.VOID(); } else { throw new IllegalArgumentException( "Unhandled type " + primitiveType ); } return boxedType; } public static IType boundTypes( IType type, List<IType> typesToBound ) { IType inferringType; if( type == null ) { return null; } else if( type instanceof ITypeVariableType && (inferringType = inferringType(type, typesToBound)) != null ) { // inferringType removes type from the list, to prevent stack overflow. IType boundType = boundTypes( ((ITypeVariableType)type).getBoundingType(), typesToBound ); typesToBound.add(inferringType); // add it back // FIXME-idubrov: Should we replace all type variables equal to type inside boundType with boundType? return boundType; } else if( type instanceof ITypeVariableArrayType ) { IType componentType = type.getComponentType(); return boundTypes( componentType, typesToBound ).getArrayType(); } else if( type.isParameterizedType() ) { IType[] parameters = type.getTypeParameters().clone(); for( int i = 0; i < parameters.length; i++ ) { parameters[i] = boundTypes( parameters[i], typesToBound ); } return type.getGenericType().getParameterizedType( parameters ); } else if( type instanceof IFunctionType ) { IFunctionType funType = (IFunctionType)type; IType[] paramTypes = funType.getParameterTypes().clone(); for( int i = 0; i < paramTypes.length; i++ ) { paramTypes[i] = boundTypes( paramTypes[i], typesToBound ); } IType returnType = boundTypes( funType.getReturnType(), typesToBound ); return funType.newInstance( paramTypes, returnType ); } else { return type; } } private static IType inferringType( IType type, List<IType> currentlyInferringTypes ) { if( type instanceof TypeVariableType ) { TypeVariableType typeVarType = (TypeVariableType)type; for( IType currentlyInferringType : currentlyInferringTypes ) { TypeVariableType inferringTypeVarType = (TypeVariableType)currentlyInferringType; if( areTypeVariablesEquivalent( typeVarType, inferringTypeVarType ) ) { currentlyInferringTypes.remove(inferringTypeVarType); return inferringTypeVarType; } } } return null; } private static boolean areTypeVariablesEquivalent( TypeVariableType possible, TypeVariableType inferred ) { boolean match = false; if( GosuObjectUtil.equals( possible.getName(), inferred.getName() ) ) { IType enclosingType1 = possible.getEnclosingType(); IType enclosingType2 = inferred.getEnclosingType(); if( enclosingType1 instanceof IFunctionType && enclosingType2 instanceof IFunctionType ) { IFunctionType funType1 = (IFunctionType)enclosingType1; IFunctionType funType2 = (IFunctionType)enclosingType2; IScriptPartId id1 = funType1.getScriptPart(); IScriptPartId id2 = funType2.getScriptPart(); String typeName1 = id1 == null ? null : id1.getContainingTypeName(); String typeName2 = id2 == null ? null : id2.getContainingTypeName(); if( GosuObjectUtil.equals( typeName1, typeName2 ) && GosuObjectUtil.equals( funType1.getParamSignature(), funType2.getParamSignature() ) ) { match = true; } } } return match; } public static boolean isInstanceOfPossible( IType lhsType, IType rhsType ) { if( (!(rhsType instanceof IGosuClass) && !(rhsType instanceof IJavaType)) || (!(lhsType instanceof IGosuClass) && !(lhsType instanceof IJavaType)) ) { return true; } if( rhsType.isFinal() ) { return lhsType.isAssignableFrom( rhsType ); } else if( !rhsType.isInterface() && !lhsType.isInterface() ) { return rhsType.isAssignableFrom( lhsType ) || lhsType.isAssignableFrom( rhsType ); } return true; } public static IType getTopLevelType(IType type) { IType topType = getCoreType(type); topType = getPureGenericType(topType); topType = getOuterMostEnclosingClass(topType); return topType; } }