/******************************************************************************* * Copyright (c) 2000, 2010 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.compiler.lookup; import java.util.List; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ast.Wildcard; /* * Not all fields defined by this type (& its subclasses) are initialized when it is created. * Some are initialized only when needed. * * Accessors have been provided for some public fields so all TypeBindings have the same API... * but access public fields directly whenever possible. * Non-public fields have accessors which should be used everywhere you expect the field to be initialized. * * null is NOT a valid value for a non-public field... it just means the field is not initialized. */ abstract public class TypeBinding extends Binding { public int id= TypeIds.NoId; public long tagBits= 0; // See values in the interface TagBits below /** Base type definitions */ public final static BaseTypeBinding INT= new BaseTypeBinding( TypeIds.T_int, TypeConstants.INT, new char[] { 'I' }); public final static BaseTypeBinding BYTE= new BaseTypeBinding( TypeIds.T_byte, TypeConstants.BYTE, new char[] { 'B' }); public final static BaseTypeBinding SHORT= new BaseTypeBinding( TypeIds.T_short, TypeConstants.SHORT, new char[] { 'S' }); public final static BaseTypeBinding CHAR= new BaseTypeBinding( TypeIds.T_char, TypeConstants.CHAR, new char[] { 'C' }); public final static BaseTypeBinding LONG= new BaseTypeBinding( TypeIds.T_long, TypeConstants.LONG, new char[] { 'J' }); public final static BaseTypeBinding FLOAT= new BaseTypeBinding( TypeIds.T_float, TypeConstants.FLOAT, new char[] { 'F' }); public final static BaseTypeBinding DOUBLE= new BaseTypeBinding( TypeIds.T_double, TypeConstants.DOUBLE, new char[] { 'D' }); public final static BaseTypeBinding BOOLEAN= new BaseTypeBinding( TypeIds.T_boolean, TypeConstants.BOOLEAN, new char[] { 'Z' }); public final static BaseTypeBinding NULL= new BaseTypeBinding( TypeIds.T_null, TypeConstants.NULL, new char[] { 'N' }); //N stands for null even if it is never internally used public final static BaseTypeBinding VOID= new BaseTypeBinding( TypeIds.T_void, TypeConstants.VOID, new char[] { 'V' }); /** * Match a well-known type id to its binding */ public static final TypeBinding wellKnownType(Scope scope, int id) { switch (id) { case TypeIds.T_boolean: return TypeBinding.BOOLEAN; case TypeIds.T_byte: return TypeBinding.BYTE; case TypeIds.T_char: return TypeBinding.CHAR; case TypeIds.T_short: return TypeBinding.SHORT; case TypeIds.T_double: return TypeBinding.DOUBLE; case TypeIds.T_float: return TypeBinding.FLOAT; case TypeIds.T_int: return TypeBinding.INT; case TypeIds.T_long: return TypeBinding.LONG; case TypeIds.T_JavaLangObject: return scope.getJavaLangObject(); case TypeIds.T_JavaLangString: return scope.getJavaLangString(); default: return null; } } /* Answer true if the receiver can be instantiated */ public boolean canBeInstantiated() { return !isBaseType(); } /** * Perform capture conversion on a given type (only effective on parameterized type with * wildcards) */ public TypeBinding capture(Scope scope, int position) { return this; } /** * In case of problems, returns the closest match found. It may not be perfect match, but the * result of a best effort to improve fault-tolerance. */ public TypeBinding closestMatch() { return this; // by default no better type } /** * Iterate through the type components to collect instances of leaf missing types * * @param missingTypes * @return missing types */ public List collectMissingTypes(List missingTypes) { return missingTypes; } /** * Collect the substitutes into a map for certain type variables inside the receiver type e.g. * Collection<T>.findSubstitute(T, Collection<List<X>>): T --> List<X> Constraints: A << F * corresponds to: F.collectSubstitutes(..., A, ..., CONSTRAINT_EXTENDS (1)) A = F corresponds * to: F.collectSubstitutes(..., A, ..., CONSTRAINT_EQUAL (0)) A >> F corresponds to: * F.collectSubstitutes(..., A, ..., CONSTRAINT_SUPER (2)) */ public void collectSubstitutes(Scope scope, TypeBinding actualType, InferenceContext inferenceContext, int constraint) { // no substitute by default } /** * Answer the receiver's constant pool name. NOTE: This method should only be used during/after * code gen. e.g. 'java/lang/Object' */ public abstract char[] constantPoolName(); public String debugName() { return new String(readableName()); } /* * Answer the receiver's dimensions - 0 for non-array types */ public int dimensions() { return 0; } /* Answer the receiver's enclosing type... null if the receiver is a top level type. */ public ReferenceBinding enclosingType() { return null; } public TypeBinding erasure() { return this; } /** * Find supertype which originates from a given well-known type, or null if not found (using id * avoids triggering the load of well-known type: 73740) NOTE: only works for erasures of * well-known types, as random other types may share same id though being distincts. * * @see TypeIds */ public ReferenceBinding findSuperTypeOriginatingFrom(int wellKnownOriginalID, boolean originalIsClass) { if (!(this instanceof ReferenceBinding)) return null; ReferenceBinding reference= (ReferenceBinding)this; // do not allow type variables to match with erasures for free if (reference.id == wellKnownOriginalID || (original().id == wellKnownOriginalID)) return reference; ReferenceBinding currentType= reference; // iterate superclass to avoid recording interfaces if searched supertype is class if (originalIsClass) { while ((currentType= currentType.superclass()) != null) { if (currentType.id == wellKnownOriginalID) return currentType; if (currentType.original().id == wellKnownOriginalID) return currentType; } return null; } ReferenceBinding[] interfacesToVisit= null; int nextPosition= 0; do { ReferenceBinding[] itsInterfaces= currentType.superInterfaces(); if (itsInterfaces != null && itsInterfaces != Binding.NO_SUPERINTERFACES) { if (interfacesToVisit == null) { interfacesToVisit= itsInterfaces; nextPosition= interfacesToVisit.length; } else { int itsLength= itsInterfaces.length; if (nextPosition + itsLength >= interfacesToVisit.length) System.arraycopy(interfacesToVisit, 0, interfacesToVisit= new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition); nextInterface: for (int a= 0; a < itsLength; a++) { ReferenceBinding next= itsInterfaces[a]; for (int b= 0; b < nextPosition; b++) if (next == interfacesToVisit[b]) continue nextInterface; interfacesToVisit[nextPosition++]= next; } } } } while ((currentType= currentType.superclass()) != null); for (int i= 0; i < nextPosition; i++) { currentType= interfacesToVisit[i]; if (currentType.id == wellKnownOriginalID) return currentType; if (currentType.original().id == wellKnownOriginalID) return currentType; ReferenceBinding[] itsInterfaces= currentType.superInterfaces(); if (itsInterfaces != null && itsInterfaces != Binding.NO_SUPERINTERFACES) { int itsLength= itsInterfaces.length; if (nextPosition + itsLength >= interfacesToVisit.length) System.arraycopy(interfacesToVisit, 0, interfacesToVisit= new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition); nextInterface: for (int a= 0; a < itsLength; a++) { ReferenceBinding next= itsInterfaces[a]; for (int b= 0; b < nextPosition; b++) if (next == interfacesToVisit[b]) continue nextInterface; interfacesToVisit[nextPosition++]= next; } } } return null; } /** * Find supertype which originates from a given type, or null if not found */ public TypeBinding findSuperTypeOriginatingFrom(TypeBinding otherType) { if (this == otherType) return this; if (otherType == null) return null; switch (kind()) { case Binding.ARRAY_TYPE: ArrayBinding arrayType= (ArrayBinding)this; int otherDim= otherType.dimensions(); if (arrayType.dimensions != otherDim) { switch (otherType.id) { case TypeIds.T_JavaLangObject: case TypeIds.T_JavaIoSerializable: case TypeIds.T_JavaLangCloneable: return otherType; } if (otherDim < arrayType.dimensions && otherType.leafComponentType().id == TypeIds.T_JavaLangObject) { return otherType; // X[][] has Object[] as an implicit supertype } return null; } if (!(arrayType.leafComponentType instanceof ReferenceBinding)) return null; TypeBinding leafSuperType= arrayType.leafComponentType.findSuperTypeOriginatingFrom(otherType.leafComponentType()); if (leafSuperType == null) return null; return arrayType.environment().createArrayType(leafSuperType, arrayType.dimensions); case Binding.TYPE_PARAMETER: if (isCapture()) { CaptureBinding capture= (CaptureBinding)this; TypeBinding captureBound= capture.firstBound; if (captureBound instanceof ArrayBinding) { TypeBinding match= captureBound.findSuperTypeOriginatingFrom(otherType); if (match != null) return match; } } //$FALL-THROUGH$ case Binding.TYPE: case Binding.PARAMETERIZED_TYPE: case Binding.GENERIC_TYPE: case Binding.RAW_TYPE: case Binding.WILDCARD_TYPE: case Binding.INTERSECTION_TYPE: // do not allow type variables/intersection types to match with erasures for free otherType= otherType.original(); if (this == otherType) return this; if (original() == otherType) return this; ReferenceBinding currentType= (ReferenceBinding)this; if (!otherType.isInterface()) { while ((currentType= currentType.superclass()) != null) { if (currentType == otherType) return currentType; if (currentType.original() == otherType) return currentType; } return null; } ReferenceBinding[] interfacesToVisit= null; int nextPosition= 0; do { ReferenceBinding[] itsInterfaces= currentType.superInterfaces(); if (itsInterfaces != Binding.NO_SUPERINTERFACES) { if (interfacesToVisit == null) { interfacesToVisit= itsInterfaces; nextPosition= interfacesToVisit.length; } else { int itsLength= itsInterfaces.length; if (nextPosition + itsLength >= interfacesToVisit.length) System.arraycopy(interfacesToVisit, 0, interfacesToVisit= new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition); nextInterface: for (int a= 0; a < itsLength; a++) { ReferenceBinding next= itsInterfaces[a]; for (int b= 0; b < nextPosition; b++) if (next == interfacesToVisit[b]) continue nextInterface; interfacesToVisit[nextPosition++]= next; } } } } while ((currentType= currentType.superclass()) != null); for (int i= 0; i < nextPosition; i++) { currentType= interfacesToVisit[i]; if (currentType == otherType) return currentType; if (currentType.original() == otherType) return currentType; ReferenceBinding[] itsInterfaces= currentType.superInterfaces(); if (itsInterfaces != Binding.NO_SUPERINTERFACES) { int itsLength= itsInterfaces.length; if (nextPosition + itsLength >= interfacesToVisit.length) System.arraycopy(interfacesToVisit, 0, interfacesToVisit= new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition); nextInterface: for (int a= 0; a < itsLength; a++) { ReferenceBinding next= itsInterfaces[a]; for (int b= 0; b < nextPosition; b++) if (next == interfacesToVisit[b]) continue nextInterface; interfacesToVisit[nextPosition++]= next; } } } } return null; } /** * Returns the type to use for generic cast, or null if none required */ public TypeBinding genericCast(TypeBinding targetType) { if (this == targetType) return null; TypeBinding targetErasure= targetType.erasure(); // type var get replaced by upper bound if (erasure().findSuperTypeOriginatingFrom(targetErasure) != null) return null; return targetErasure; } /** * Answer the receiver classfile signature. Arrays & base types do not distinguish between * signature() & constantPoolName(). NOTE: This method should only be used during/after code * gen. */ public char[] genericTypeSignature() { return signature(); } /** * Return the supertype which would erase as a subtype of a given declaring class. If the * receiver is already erasure compatible, then it will returned. If not, then will return the * alternate lowest upper bound compatible with declaring class. NOTE: the declaringClass is * already know to be compatible with the receiver * * @param declaringClass to look for * @return the lowest erasure compatible type (considering alternate bounds) */ public TypeBinding getErasureCompatibleType(TypeBinding declaringClass) { switch (kind()) { case Binding.TYPE_PARAMETER: TypeVariableBinding variable= (TypeVariableBinding)this; if (variable.erasure().findSuperTypeOriginatingFrom(declaringClass) != null) { return this; // no need for alternate receiver type } if (variable.superclass != null && variable.superclass.findSuperTypeOriginatingFrom(declaringClass) != null) { return variable.superclass.getErasureCompatibleType(declaringClass); } for (int i= 0, otherLength= variable.superInterfaces.length; i < otherLength; i++) { ReferenceBinding superInterface= variable.superInterfaces[i]; if (superInterface.findSuperTypeOriginatingFrom(declaringClass) != null) { return superInterface.getErasureCompatibleType(declaringClass); } } return this; // only occur if passed null declaringClass for arraylength case Binding.INTERSECTION_TYPE: WildcardBinding intersection= (WildcardBinding)this; if (intersection.erasure().findSuperTypeOriginatingFrom(declaringClass) != null) { return this; // no need for alternate receiver type } if (intersection.superclass != null && intersection.superclass.findSuperTypeOriginatingFrom(declaringClass) != null) { return intersection.superclass.getErasureCompatibleType(declaringClass); } for (int i= 0, otherLength= intersection.superInterfaces.length; i < otherLength; i++) { ReferenceBinding superInterface= intersection.superInterfaces[i]; if (superInterface.findSuperTypeOriginatingFrom(declaringClass) != null) { return superInterface.getErasureCompatibleType(declaringClass); } } return this; // only occur if passed null declaringClass for arraylength default: return this; } } public abstract PackageBinding getPackage(); void initializeForStaticImports() { // only applicable to source types } public boolean isAnnotationType() { return false; } public final boolean isAnonymousType() { return (this.tagBits & TagBits.IsAnonymousType) != 0; } /* Answer true if the receiver is an array */ public final boolean isArrayType() { return (this.tagBits & TagBits.IsArrayType) != 0; } /* Answer true if the receiver is a base type */ public final boolean isBaseType() { return (this.tagBits & TagBits.IsBaseType) != 0; } /** * Returns true if parameterized type AND not of the form List<?> */ public boolean isBoundParameterizedType() { return (this.tagBits & TagBits.IsBoundParameterizedType) != 0; } /** * Returns true if the type is the capture of some wildcard */ public boolean isCapture() { return false; } public boolean isClass() { return false; } /* Answer true if the receiver type can be assigned to the argument type (right) */ public abstract boolean isCompatibleWith(TypeBinding right); public boolean isEnum() { return false; } /** * Returns true if a type is identical to another one, or for generic types, true if compared to * its raw type. */ public boolean isEquivalentTo(TypeBinding otherType) { if (this == otherType) return true; if (otherType == null) return false; switch (otherType.kind()) { case Binding.WILDCARD_TYPE: case Binding.INTERSECTION_TYPE: return ((WildcardBinding)otherType).boundCheck(this); } return false; } public boolean isGenericType() { return false; } /* Answer true if the receiver's hierarchy has problems (always false for arrays & base types) */ public final boolean isHierarchyInconsistent() { return (this.tagBits & TagBits.HierarchyHasProblems) != 0; } public boolean isInterface() { return false; } /** * Returns true if the current type denotes an intersection type: Number & Comparable<?> */ public boolean isIntersectionType() { return false; } public final boolean isLocalType() { return (this.tagBits & TagBits.IsLocalType) != 0; } public final boolean isMemberType() { return (this.tagBits & TagBits.IsMemberType) != 0; } public final boolean isNestedType() { return (this.tagBits & TagBits.IsNestedType) != 0; } public final boolean isNumericType() { switch (this.id) { case TypeIds.T_int: case TypeIds.T_float: case TypeIds.T_double: case TypeIds.T_short: case TypeIds.T_byte: case TypeIds.T_long: case TypeIds.T_char: return true; default: return false; } } /** * Returns true if the type is parameterized, e.g. List<String>. Note that some instances of * ParameterizedTypeBinding have no arguments, like for non-generic members of a parameterized * type. Use {@link #isParameterizedTypeWithActualArguments()} instead to find out. */ public final boolean isParameterizedType() { return kind() == Binding.PARAMETERIZED_TYPE; } /** * Returns true if the type is parameterized, e.g. List<String> Note that some instances of * ParameterizedTypeBinding do answer false to {@link #isParameterizedType()} in case they have * no arguments, like for non-generic members of a parameterized type. i.e. * {@link #isParameterizedType()} is not equivalent to testing * <code>type.kind() == Binding.PARAMETERIZED_TYPE</code> */ public final boolean isParameterizedTypeWithActualArguments() { return (kind() == Binding.PARAMETERIZED_TYPE) && ((ParameterizedTypeBinding)this).arguments != null; } /** * Returns true if the type is parameterized using its own type variables as arguments */ public boolean isParameterizedWithOwnVariables() { if (kind() != Binding.PARAMETERIZED_TYPE) return false; ParameterizedTypeBinding paramType= (ParameterizedTypeBinding)this; if (paramType.arguments == null) return false; TypeVariableBinding[] variables= erasure().typeVariables(); for (int i= 0, length= variables.length; i < length; i++) { if (variables[i] != paramType.arguments[i]) return false; } ReferenceBinding enclosing= paramType.enclosingType(); if (enclosing != null && enclosing.erasure().isGenericType() && !enclosing.isParameterizedWithOwnVariables()) { return false; } return true; } private boolean isProvableDistinctSubType(TypeBinding otherType) { if (otherType.isInterface()) { if (isInterface()) return false; if (isArrayType() || ((this instanceof ReferenceBinding) && ((ReferenceBinding)this).isFinal()) || (isTypeVariable() && ((TypeVariableBinding)this).superclass().isFinal())) { return !isCompatibleWith(otherType); } return false; } else { if (isInterface()) { if (otherType.isArrayType() || ((otherType instanceof ReferenceBinding) && ((ReferenceBinding)otherType).isFinal()) || (otherType.isTypeVariable() && ((TypeVariableBinding)otherType).superclass().isFinal())) { return !isCompatibleWith(otherType); } } else { if (!isTypeVariable() && !otherType.isTypeVariable()) { return !isCompatibleWith(otherType); } } } return false; } /** * Returns true if a type is provably distinct from another one, */ public boolean isProvablyDistinct(TypeBinding otherType) { if (this == otherType) return false; if (otherType == null) return true; switch (kind()) { case Binding.PARAMETERIZED_TYPE: ParameterizedTypeBinding paramType= (ParameterizedTypeBinding)this; switch (otherType.kind()) { case Binding.PARAMETERIZED_TYPE: ParameterizedTypeBinding otherParamType= (ParameterizedTypeBinding)otherType; if (paramType.genericType() != otherParamType.genericType()) return true; if (!paramType.isStatic()) { // static member types do not compare their enclosing ReferenceBinding enclosing= enclosingType(); if (enclosing != null) { ReferenceBinding otherEnclosing= otherParamType.enclosingType(); if (otherEnclosing == null) return true; if ((otherEnclosing.tagBits & TagBits.HasDirectWildcard) == 0) { if (enclosing.isProvablyDistinct(otherEnclosing)) return true; // https://bugs.eclipse.org/bugs/show_bug.cgi?id=302919 } else { if (!enclosing.isEquivalentTo(otherParamType.enclosingType())) return true; } } } int length= paramType.arguments == null ? 0 : paramType.arguments.length; TypeBinding[] otherArguments= otherParamType.arguments; int otherLength= otherArguments == null ? 0 : otherArguments.length; if (otherLength != length) return true; for (int i= 0; i < length; i++) { if (paramType.arguments[i].isProvablyDistinctTypeArgument(otherArguments[i], paramType, i)) return true; } return false; case Binding.GENERIC_TYPE: SourceTypeBinding otherGenericType= (SourceTypeBinding)otherType; if (paramType.genericType() != otherGenericType) return true; if (!paramType.isStatic()) { // static member types do not compare their enclosing ReferenceBinding enclosing= enclosingType(); if (enclosing != null) { ReferenceBinding otherEnclosing= otherGenericType.enclosingType(); if (otherEnclosing == null) return true; if ((otherEnclosing.tagBits & TagBits.HasDirectWildcard) == 0) { if (enclosing != otherEnclosing) return true; } else { if (!enclosing.isEquivalentTo(otherGenericType.enclosingType())) return true; } } } length= paramType.arguments == null ? 0 : paramType.arguments.length; otherArguments= otherGenericType.typeVariables(); otherLength= otherArguments == null ? 0 : otherArguments.length; if (otherLength != length) return true; for (int i= 0; i < length; i++) { if (paramType.arguments[i].isProvablyDistinctTypeArgument(otherArguments[i], paramType, i)) return true; } return false; case Binding.RAW_TYPE: return erasure() != otherType.erasure(); } return true; case Binding.RAW_TYPE: switch (otherType.kind()) { case Binding.GENERIC_TYPE: case Binding.PARAMETERIZED_TYPE: case Binding.RAW_TYPE: return erasure() != otherType.erasure(); } return true; default: break; } return true; } /** * Returns false if two given types could not intersect as argument types: List<Throwable> & * List<Runnable> --> false List<? extends Throwable> & List<? extends Runnable> --> true List<? * extends String> & List<? extends Runnable> --> false */ private boolean isProvablyDistinctTypeArgument(TypeBinding otherArgument, final ParameterizedTypeBinding paramType, final int rank) { if (this == otherArgument) return false; TypeBinding upperBound1= null; TypeBinding lowerBound1= null; switch (kind()) { case Binding.WILDCARD_TYPE: WildcardBinding wildcard= (WildcardBinding)this; switch (wildcard.boundKind) { case Wildcard.EXTENDS: upperBound1= wildcard.bound; break; case Wildcard.SUPER: lowerBound1= wildcard.bound; break; case Wildcard.UNBOUND: return false; } break; case Binding.INTERSECTION_TYPE: break; case Binding.TYPE_PARAMETER: final TypeVariableBinding variable= (TypeVariableBinding)this; if (variable.isCapture()) { CaptureBinding capture= (CaptureBinding)variable; switch (capture.wildcard.boundKind) { case Wildcard.EXTENDS: upperBound1= capture.wildcard.bound; break; case Wildcard.SUPER: lowerBound1= capture.wildcard.bound; break; case Wildcard.UNBOUND: return false; } break; } if (variable.firstBound == null) // unbound variable return false; TypeBinding eliminatedType= Scope.convertEliminatingTypeVariables(variable, paramType.genericType(), rank, null); switch (eliminatedType.kind()) { case Binding.WILDCARD_TYPE: case Binding.INTERSECTION_TYPE: wildcard= (WildcardBinding)eliminatedType; switch (wildcard.boundKind) { case Wildcard.EXTENDS: upperBound1= wildcard.bound; break; case Wildcard.SUPER: lowerBound1= wildcard.bound; break; case Wildcard.UNBOUND: return false; } break; } break; } TypeBinding upperBound2= null; TypeBinding lowerBound2= null; switch (otherArgument.kind()) { case Binding.WILDCARD_TYPE: WildcardBinding otherWildcard= (WildcardBinding)otherArgument; switch (otherWildcard.boundKind) { case Wildcard.EXTENDS: upperBound2= otherWildcard.bound; break; case Wildcard.SUPER: lowerBound2= otherWildcard.bound; break; case Wildcard.UNBOUND: return false; } break; case Binding.INTERSECTION_TYPE: break; case Binding.TYPE_PARAMETER: TypeVariableBinding otherVariable= (TypeVariableBinding)otherArgument; if (otherVariable.isCapture()) { CaptureBinding otherCapture= (CaptureBinding)otherVariable; switch (otherCapture.wildcard.boundKind) { case Wildcard.EXTENDS: upperBound2= otherCapture.wildcard.bound; break; case Wildcard.SUPER: lowerBound2= otherCapture.wildcard.bound; break; case Wildcard.UNBOUND: return false; } break; } if (otherVariable.firstBound == null) // unbound variable return false; TypeBinding otherEliminatedType= Scope.convertEliminatingTypeVariables(otherVariable, paramType.genericType(), rank, null); switch (otherEliminatedType.kind()) { case Binding.WILDCARD_TYPE: case Binding.INTERSECTION_TYPE: otherWildcard= (WildcardBinding)otherEliminatedType; switch (otherWildcard.boundKind) { case Wildcard.EXTENDS: upperBound2= otherWildcard.bound; break; case Wildcard.SUPER: lowerBound2= otherWildcard.bound; break; case Wildcard.UNBOUND: return false; } break; } break; } if (lowerBound1 != null) { if (lowerBound2 != null) { return false; // Object could always be a candidate } else if (upperBound2 != null) { if (lowerBound1.isTypeVariable() || upperBound2.isTypeVariable()) { return false; } return !lowerBound1.isCompatibleWith(upperBound2); } else { if (lowerBound1.isTypeVariable() || otherArgument.isTypeVariable()) { return false; } return !lowerBound1.isCompatibleWith(otherArgument); } } else if (upperBound1 != null) { if (lowerBound2 != null) { return !lowerBound2.isCompatibleWith(upperBound1); } else if (upperBound2 != null) { return upperBound1.isProvableDistinctSubType(upperBound2) && upperBound2.isProvableDistinctSubType(upperBound1); } else { return otherArgument.isProvableDistinctSubType(upperBound1); } } else { if (lowerBound2 != null) { if (lowerBound2.isTypeVariable() || isTypeVariable()) { return false; } return !lowerBound2.isCompatibleWith(this); } else if (upperBound2 != null) { return isProvableDistinctSubType(upperBound2); } else { return true; // ground types should have been the same } } } public final boolean isRawType() { return kind() == Binding.RAW_TYPE; } /** * JLS(3) 4.7. Note: Foo<?>.Bar is also reifiable */ public boolean isReifiable() { TypeBinding leafType= leafComponentType(); if (!(leafType instanceof ReferenceBinding)) return true; ReferenceBinding current= (ReferenceBinding)leafType; do { switch (current.kind()) { case Binding.TYPE_PARAMETER: case Binding.WILDCARD_TYPE: case Binding.INTERSECTION_TYPE: case Binding.GENERIC_TYPE: return false; case Binding.PARAMETERIZED_TYPE: if (current.isBoundParameterizedType()) return false; break; case Binding.RAW_TYPE: return true; } if (current.isStatic()) { return true; } if (current.isLocalType()) { LocalTypeBinding localTypeBinding= (LocalTypeBinding)current.erasure(); MethodBinding enclosingMethod= localTypeBinding.enclosingMethod; if (enclosingMethod != null && enclosingMethod.isStatic()) { return true; } } } while ((current= current.enclosingType()) != null); return true; } /** * Returns true if a given type may be thrown */ public boolean isThrowable() { return false; } // JLS3: 4.5.1.1 public boolean isTypeArgumentContainedBy(TypeBinding otherType) { if (this == otherType) return true; switch (otherType.kind()) { // allow wildcard containment case Binding.WILDCARD_TYPE: case Binding.INTERSECTION_TYPE: TypeBinding lowerBound= this; TypeBinding upperBound= this; switch (kind()) { case Binding.WILDCARD_TYPE: case Binding.INTERSECTION_TYPE: WildcardBinding wildcard= (WildcardBinding)this; switch (wildcard.boundKind) { case Wildcard.EXTENDS: if (wildcard.otherBounds != null) // intersection type break; upperBound= wildcard.bound; lowerBound= null; break; case Wildcard.SUPER: upperBound= wildcard; lowerBound= wildcard.bound; break; case Wildcard.UNBOUND: upperBound= wildcard; lowerBound= null; } break; case Binding.TYPE_PARAMETER: if (isCapture()) { CaptureBinding capture= (CaptureBinding)this; if (capture.lowerBound != null) lowerBound= capture.lowerBound; } } WildcardBinding otherWildcard= (WildcardBinding)otherType; if (otherWildcard.otherBounds != null) return false; // not a true wildcard (intersection type) TypeBinding otherBound= otherWildcard.bound; switch (otherWildcard.boundKind) { case Wildcard.EXTENDS: if (otherBound == this) return true; // ? extends T <= ? extends ? extends T if (upperBound == null) return false; TypeBinding match= upperBound.findSuperTypeOriginatingFrom(otherBound); if (match != null && (match= match.leafComponentType()).isRawType()) { return match == otherBound.leafComponentType(); // forbide: Collection <= ? extends Collection<?> // forbide: Collection[] <= ? extends Collection<?>[] } return upperBound.isCompatibleWith(otherBound); case Wildcard.SUPER: if (otherBound == this) return true; // ? super T <= ? super ? super T if (lowerBound == null) return false; match= otherBound.findSuperTypeOriginatingFrom(lowerBound); if (match != null && (match= match.leafComponentType()).isRawType()) { return match == lowerBound.leafComponentType(); // forbide: Collection <= ? super Collection<?> // forbide: Collection[] <= ? super Collection<?>[] } return otherBound.isCompatibleWith(lowerBound); case Wildcard.UNBOUND: default: return true; } // allow List<?> to match List<? extends Object> (and reciprocally) case Binding.PARAMETERIZED_TYPE: if (!isParameterizedType()) return false; ParameterizedTypeBinding paramType= (ParameterizedTypeBinding)this; ParameterizedTypeBinding otherParamType= (ParameterizedTypeBinding)otherType; if (paramType.actualType() != otherParamType.actualType()) return false; if (!paramType.isStatic()) { // static member types do not compare their enclosing ReferenceBinding enclosing= enclosingType(); if (enclosing != null) { ReferenceBinding otherEnclosing= otherParamType.enclosingType(); if (otherEnclosing == null) return false; if ((otherEnclosing.tagBits & TagBits.HasDirectWildcard) == 0) { if (enclosing != otherEnclosing) return false; } else { if (!enclosing.isEquivalentTo(otherParamType.enclosingType())) return false; } } } int length= paramType.arguments == null ? 0 : paramType.arguments.length; TypeBinding[] otherArguments= otherParamType.arguments; int otherLength= otherArguments == null ? 0 : otherArguments.length; if (otherLength != length) return false; nextArgument: for (int i= 0; i < length; i++) { TypeBinding argument= paramType.arguments[i]; TypeBinding otherArgument= otherArguments[i]; if (argument == otherArgument) continue nextArgument; int kind= argument.kind(); if (otherArgument.kind() != kind) return false; switch (kind) { case Binding.PARAMETERIZED_TYPE: if (argument.isTypeArgumentContainedBy(otherArgument)) // recurse continue nextArgument; break; case Binding.WILDCARD_TYPE: case Binding.INTERSECTION_TYPE: WildcardBinding wildcard= (WildcardBinding)argument; otherWildcard= (WildcardBinding)otherArgument; switch (wildcard.boundKind) { case Wildcard.EXTENDS: // match "? extends <upperBound>" with "?" if (otherWildcard.boundKind == Wildcard.UNBOUND && wildcard.bound == wildcard.typeVariable().upperBound()) continue nextArgument; break; case Wildcard.SUPER: break; case Wildcard.UNBOUND: // match "?" with "? extends <upperBound>" if (otherWildcard.boundKind == Wildcard.EXTENDS && otherWildcard.bound == otherWildcard.typeVariable().upperBound()) continue nextArgument; break; } break; } return false; } return true; } // (? super Object) <= Object if (otherType.id == TypeIds.T_JavaLangObject) { switch (kind()) { case Binding.WILDCARD_TYPE: WildcardBinding wildcard= (WildcardBinding)this; if (wildcard.boundKind == Wildcard.SUPER && wildcard.bound.id == TypeIds.T_JavaLangObject) { return true; } break; } } return false; } /** * Returns true if the type was declared as a type variable */ public boolean isTypeVariable() { return false; } /** * Returns true if wildcard type of the form '?' (no bound) */ public boolean isUnboundWildcard() { return false; } /** * Returns true if the type is a subclass of java.lang.Error or java.lang.RuntimeException */ public boolean isUncheckedException(boolean includeSupertype) { return false; } /** * Returns true if the type is a wildcard */ public boolean isWildcard() { return false; } /* API * Answer the receiver's binding type from Binding.BindingID. */ public int kind() { return Binding.TYPE; } public TypeBinding leafComponentType() { return this; } /** * Meant to be invoked on compatible types, to figure if unchecked conversion is necessary */ public boolean needsUncheckedConversion(TypeBinding targetType) { if (this == targetType) return false; targetType= targetType.leafComponentType(); if (!(targetType instanceof ReferenceBinding)) return false; TypeBinding currentType= leafComponentType(); TypeBinding match= currentType.findSuperTypeOriginatingFrom(targetType); if (!(match instanceof ReferenceBinding)) return false; ReferenceBinding compatible= (ReferenceBinding)match; while (compatible.isRawType()) { if (targetType.isBoundParameterizedType()) return true; if (compatible.isStatic()) break; if ((compatible= compatible.enclosingType()) == null) break; if ((targetType= targetType.enclosingType()) == null) break; } return false; } /** * Returns the orignal generic type instantiated by the receiver type, or itself if not. This is * similar to erasure process, except it doesn't erase type variable, wildcard, intersection * types etc... */ public TypeBinding original() { switch (kind()) { case Binding.PARAMETERIZED_TYPE: case Binding.RAW_TYPE: case Binding.ARRAY_TYPE: return erasure(); default: return this; } } /** * Answer the qualified name of the receiver's package separated by periods or an empty string * if its the default package. * * For example, {java.util}. */ public char[] qualifiedPackageName() { PackageBinding packageBinding= getPackage(); return packageBinding == null || packageBinding.compoundName == CharOperation.NO_CHAR_CHAR ? CharOperation.NO_CHAR : packageBinding.readableName(); } /** * Answer the source name for the type. In the case of member types, as the qualified name from * its top level type. For example, for a member type N defined inside M & A: "A.M.N". */ public abstract char[] qualifiedSourceName(); /** * Answer the receiver classfile signature. Arrays & base types do not distinguish between * signature() & constantPoolName(). NOTE: This method should only be used during/after code * gen. */ public char[] signature() { return constantPoolName(); } public abstract char[] sourceName(); public void swapUnresolved(UnresolvedReferenceBinding unresolvedType, ReferenceBinding resolvedType, LookupEnvironment environment) { // subclasses must override if they wrap another type binding } public TypeVariableBinding[] typeVariables() { return Binding.NO_TYPE_VARIABLES; } }