/******************************************************************************* * 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 * Stephen Herrmann <stephan@cs.tu-berlin.de> - Contribution for bug 317046 *******************************************************************************/ 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 != 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 == otherType) return currentType; if (currentType.original() == otherType) 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; } /** * 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, boolean isClassLiteral) { if (otherType.isInterface()) { if (isInterface()) return false; if (isArrayType() // || isClassLiteral // https://bugs.eclipse.org/bugs/show_bug.cgi?id=322531 || ((this instanceof ReferenceBinding) && ((ReferenceBinding) this).isFinal()) || (isTypeVariable() && ((TypeVariableBinding)this).superclass().isFinal())) { return !isCompatibleWith(otherType); } return false; } else { if (isInterface()) { if (otherType.isArrayType() // || isClassLiteral // https://bugs.eclipse.org/bugs/show_bug.cgi?id=322531 || ((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) { /* With the hybrid 1.4/1.5+ projects modes, while establishing type equivalence, we need to be prepared for a type such as Map appearing in one of three forms: As (a) a ParameterizedTypeBinding e.g Map<String, String>, (b) as RawTypeBinding Map#RAW and finally (c) as a BinaryTypeBinding When the usage of a type lacks type parameters, whether we land up with the raw form or not depends on whether the underlying type was "seen to be" a generic type in the particular build environment or not. See: https://bugs.eclipse.org/bugs/show_bug.cgi?id=186565 https://bugs.eclipse.org/bugs/show_bug.cgi?id=328827 https://bugs.eclipse.org/bugs/show_bug.cgi?id=329588 */ 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 : if (paramType.genericType() != otherType) return true; if (!paramType.isStatic()) { // static member types do not compare their enclosing ReferenceBinding enclosing = enclosingType(); if (enclosing != null) { ReferenceBinding otherEnclosing = otherType.enclosingType(); if (otherEnclosing == null) return true; if ((otherEnclosing.tagBits & TagBits.HasDirectWildcard) == 0) { if (enclosing != otherEnclosing) return true; } else { if (!enclosing.isEquivalentTo(otherType.enclosingType())) return true; } } } length = paramType.arguments == null ? 0 : paramType.arguments.length; otherArguments = otherType.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(); case Binding.TYPE: // https://bugs.eclipse.org/bugs/show_bug.cgi?id=329588 return erasure() != otherType; } return true; case Binding.RAW_TYPE : // dead code ?? switch(otherType.kind()) { case Binding.GENERIC_TYPE : case Binding.PARAMETERIZED_TYPE : case Binding.RAW_TYPE : case Binding.TYPE: // https://bugs.eclipse.org/bugs/show_bug.cgi?id=329588 return erasure() != otherType.erasure(); } return true; case Binding.TYPE: // https://bugs.eclipse.org/bugs/show_bug.cgi?id=329588 switch(otherType.kind()) { case Binding.PARAMETERIZED_TYPE : case Binding.RAW_TYPE : return this != otherType.erasure(); } break; 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; ReferenceBinding genericType = paramType.genericType(); 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, 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, 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, false) && upperBound2.isProvableDistinctSubType(upperBound1, false); } else { return otherArgument.isProvableDistinctSubType(upperBound1, genericType.id == TypeIds.T_JavaLangClass); } } else { if (lowerBound2 != null) { if (lowerBound2.isTypeVariable() || isTypeVariable()) { return false; } return !lowerBound2.isCompatibleWith(this); } else if (upperBound2 != null) { return isProvableDistinctSubType(upperBound2, genericType.id == TypeIds.T_JavaLangClass); } 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; } }