| /******************************************************************************* |
| * Copyright (c) 2000, 2006 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.Map; |
| 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; |
| } |
| |
| /** |
| * 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, ..., 1) |
| * A = F corresponds to: F.collectSubstitutes(..., A, ..., 0) |
| * A >> F corresponds to: F.collectSubstitutes(..., A, ..., 2) |
| */ |
| public void collectSubstitutes(Scope scope, TypeBinding actualType, Map substitutes, 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 erases to a given type, or null if not found |
| */ |
| public TypeBinding findSuperTypeWithSameErasure(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.findSuperTypeWithSameErasure(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.findSuperTypeWithSameErasure(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 : |
| // do not allow type variables/intersection types to match with erasures for free |
| if (!otherType.isTypeVariable() && !otherType.isIntersectionType()) otherType = otherType.erasure(); |
| if (this == otherType || (!isTypeVariable() && !isIntersectionType() && erasure() == otherType)) return this; |
| |
| ReferenceBinding currentType = (ReferenceBinding)this; |
| if (!otherType.isInterface()) { |
| while ((currentType = currentType.superclass()) != null) { |
| if (currentType == otherType || (!currentType.isTypeVariable() && !currentType.isIntersectionType() && currentType.erasure() == 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 || (!currentType.isTypeVariable() && !currentType.isIntersectionType() && currentType.erasure() == 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 otherType) { |
| if (this == otherType) |
| return null; |
| TypeBinding otherErasure = otherType.erasure(); |
| if (otherErasure == this.erasure()) |
| return null; |
| return otherErasure; |
| } |
| |
| /** |
| * 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(); |
| } |
| |
| public abstract PackageBinding getPackage(); |
| |
| 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; |
| if (otherType.isWildcard()) // wildcard |
| 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 a type is intersecting with another one, |
| */ |
| public boolean isIntersectingWith(TypeBinding otherType) { |
| return this == otherType; |
| } |
| |
| /** |
| * 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 (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> |
| */ |
| public boolean isParameterizedType() { |
| return false; |
| } |
| |
| /** |
| * Returns true if the type is parameterized using its own type variables as arguments |
| */ |
| public boolean isParameterizedWithOwnVariables() { |
| if (this.kind() != Binding.PARAMETERIZED_TYPE) |
| return false; |
| ParameterizedTypeBinding paramType = (ParameterizedTypeBinding) this; |
| if (paramType.arguments == null) |
| return false; |
| TypeVariableBinding[] variables = this.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; |
| } |
| |
| /** |
| * Returns true if the two types are statically known to be different at compile-time, |
| * e.g. a type variable is not provably known to be distinct from another type |
| */ |
| public boolean isProvablyDistinctFrom(TypeBinding otherType, int depth) { |
| if (this == otherType) |
| return false; |
| if (depth > 1) |
| return true; |
| switch (otherType.kind()) { |
| case Binding.TYPE_PARAMETER: |
| case Binding.WILDCARD_TYPE: |
| return false; |
| } |
| switch (kind()) { |
| case Binding.TYPE_PARAMETER: |
| case Binding.WILDCARD_TYPE: |
| return false; |
| |
| case Binding.PARAMETERIZED_TYPE: |
| ParameterizedTypeBinding parameterizedType = (ParameterizedTypeBinding) this; |
| if (parameterizedType.type.isProvablyDistinctFrom(otherType.erasure(), depth)) |
| return true; |
| switch (otherType.kind()) { |
| case Binding.GENERIC_TYPE: |
| case Binding.RAW_TYPE: |
| return false; |
| case Binding.PARAMETERIZED_TYPE: |
| TypeBinding[] arguments = parameterizedType.arguments; |
| if (arguments == null) |
| return false; |
| ParameterizedTypeBinding otherParameterizedType = (ParameterizedTypeBinding) otherType; |
| TypeBinding[] otherArguments = otherParameterizedType.arguments; |
| if (otherArguments == null) |
| return false; |
| for (int i = 0, length = arguments.length; i < length; i++) { |
| if (arguments[i].isProvablyDistinctFrom(otherArguments[i], depth + 1)) |
| return true; |
| } |
| return false; |
| |
| } |
| break; |
| |
| case Binding.RAW_TYPE: |
| if (depth > 0) return true; |
| return this.erasure().isProvablyDistinctFrom(otherType.erasure(), 0); |
| |
| case Binding.GENERIC_TYPE: |
| if (depth > 0) return true; |
| return this != otherType.erasure(); |
| } |
| return this != otherType; |
| } |
| |
| public boolean isRawType() { |
| return false; |
| } |
| |
| /** |
| * 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.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()) { |
| NestedTypeBinding nestedType = (NestedTypeBinding) current.erasure(); |
| if (nestedType.scope.methodScope().isStatic) return true; |
| } |
| } while ((current = current.enclosingType()) != null); |
| return true; |
| } |
| |
| // 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: |
| TypeBinding lowerBound = this; |
| TypeBinding upperBound = this; |
| switch (this.kind()) { |
| case Binding.WILDCARD_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 (this.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.findSuperTypeWithSameErasure(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.findSuperTypeWithSameErasure(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 (!this.isParameterizedType()) |
| return false; |
| ParameterizedTypeBinding paramType = (ParameterizedTypeBinding) this; |
| ParameterizedTypeBinding otherParamType = (ParameterizedTypeBinding) otherType; |
| if (paramType.type != otherParamType.type) |
| 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: |
| 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; |
| } |
| return false; |
| } |
| |
| /** |
| * 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 |
| */ |
| public boolean isTypeArgumentIntersecting(TypeBinding otherArgument) { |
| if (this == otherArgument) |
| return true; |
| switch (kind()) { |
| |
| // TYPE_PARAM & ANY TYPE |
| case Binding.TYPE_PARAMETER: |
| return true; |
| |
| case Binding.WILDCARD_TYPE: |
| switch (otherArgument.kind()) { |
| |
| // WILDCARD & TYPE_PARAM |
| case Binding.TYPE_PARAMETER: |
| return true; |
| |
| // WILDCARD & WILDCARD |
| case Binding.WILDCARD_TYPE: |
| TypeBinding lowerBound1 = null; |
| TypeBinding upperBound1 = null; |
| WildcardBinding wildcard = (WildcardBinding) this; |
| switch (wildcard.boundKind) { |
| case Wildcard.EXTENDS: |
| upperBound1 = wildcard.bound; |
| break; |
| case Wildcard.SUPER: |
| lowerBound1 = wildcard.bound; |
| break; |
| case Wildcard.UNBOUND: |
| } |
| |
| TypeBinding lowerBound2 = null; |
| TypeBinding upperBound2 = null; |
| WildcardBinding otherWildcard = (WildcardBinding) otherArgument; |
| switch (otherWildcard.boundKind) { |
| case Wildcard.EXTENDS: |
| upperBound2 = otherWildcard.bound; |
| break; |
| case Wildcard.SUPER: |
| lowerBound2 = otherWildcard.bound; |
| break; |
| case Wildcard.UNBOUND: |
| } |
| if (lowerBound1 != null) { |
| if (lowerBound2 != null) { |
| return true; // Object could always be a candidate |
| |
| } else if (upperBound2 != null) { |
| return lowerBound1.isCompatibleWith(upperBound2); |
| } else { |
| return true; |
| } |
| } else if (upperBound1 != null) { |
| if (upperBound1.isTypeVariable()) |
| return true; |
| if (lowerBound2 != null) { |
| return lowerBound2.isCompatibleWith(upperBound1); |
| |
| } else if (upperBound2 != null) { |
| if (upperBound1.isInterface()) { |
| if (upperBound2.isInterface()) |
| return true; |
| if (upperBound2.isArrayType() |
| || ((upperBound2 instanceof ReferenceBinding) && ((ReferenceBinding) upperBound2) |
| .isFinal())) { |
| return upperBound2 |
| .isCompatibleWith(upperBound1); |
| } |
| return true; |
| } else { |
| if (upperBound2.isInterface()) { |
| if (upperBound1.isArrayType() |
| || ((upperBound1 instanceof ReferenceBinding) && ((ReferenceBinding) upperBound1) |
| .isFinal())) { |
| return upperBound1 |
| .isCompatibleWith(upperBound2); |
| } |
| } else { |
| return upperBound1 |
| .isCompatibleWith(upperBound2); |
| } |
| } |
| return true; |
| } else { |
| return true; |
| } |
| } else { |
| return true; |
| } |
| |
| // WILDCARD & OTHER TYPE |
| default: |
| wildcard = (WildcardBinding) this; |
| switch (wildcard.boundKind) { |
| case Wildcard.EXTENDS: |
| return otherArgument.isCompatibleWith(wildcard.bound); |
| case Wildcard.SUPER: |
| return wildcard.bound.isCompatibleWith(otherArgument); |
| case Wildcard.UNBOUND: |
| default: |
| return true; |
| } |
| } |
| |
| default: |
| switch (otherArgument.kind()) { |
| |
| // OTHER TYPE & TYPE_PARAM |
| case Binding.TYPE_PARAMETER: |
| return true; |
| |
| // OTHER TYPE & WILDCARD |
| case Binding.WILDCARD_TYPE: |
| WildcardBinding otherWildcard = (WildcardBinding) otherArgument; |
| switch (otherWildcard.boundKind) { |
| case Wildcard.EXTENDS: |
| return this.isCompatibleWith(otherWildcard.bound); |
| case Wildcard.SUPER: |
| return otherWildcard.bound.isCompatibleWith(this); |
| case Wildcard.UNBOUND: |
| default: |
| return true; |
| } |
| |
| // OTHER TYPE & OTHER TYPE |
| default: |
| 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 = this.leafComponentType(); |
| TypeBinding match = currentType.findSuperTypeWithSameErasure(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; |
| } |
| |
| /** |
| * 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.Hashtable}. |
| */ |
| |
| 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; |
| } |
| } |