| /******************************************************************************* |
| * Copyright (c) 2000, 2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.compiler.lookup; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.internal.compiler.ast.*; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.eclipse.jdt.internal.compiler.impl.ReferenceContext; |
| import org.eclipse.jdt.internal.compiler.problem.AbortCompilation; |
| import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; |
| import org.eclipse.jdt.internal.compiler.util.HashtableOfObject; |
| import org.eclipse.jdt.internal.compiler.util.ObjectVector; |
| |
| public abstract class Scope |
| implements BaseTypes, BindingIds, CompilerModifiers, ProblemReasons, TagBits, TypeConstants, TypeIds { |
| |
| public final static int BLOCK_SCOPE = 1; |
| public final static int CLASS_SCOPE = 3; |
| public final static int COMPILATION_UNIT_SCOPE = 4; |
| public final static int METHOD_SCOPE = 2; |
| |
| /* Answer an int describing the relationship between the given types. |
| * |
| * NotRelated |
| * EqualOrMoreSpecific : left is compatible with right |
| * MoreGeneric : right is compatible with left |
| */ |
| public static int compareTypes(TypeBinding left, TypeBinding right) { |
| if (left.isCompatibleWith(right)) |
| return EqualOrMoreSpecific; |
| if (right.isCompatibleWith(left)) |
| return MoreGeneric; |
| return NotRelated; |
| } |
| |
| /** |
| * Returns an array of types, where original types got substituted given a substitution. |
| * Only allocate an array if anything is different. |
| */ |
| public static ReferenceBinding[] substitute(Substitution substitution, ReferenceBinding[] originalTypes) { |
| ReferenceBinding[] substitutedTypes = originalTypes; |
| for (int i = 0, length = originalTypes.length; i < length; i++) { |
| ReferenceBinding originalType = originalTypes[i]; |
| ReferenceBinding substitutedParameter = (ReferenceBinding)substitution.substitute(originalType); |
| if (substitutedParameter != originalType) { |
| if (substitutedTypes == originalTypes) { |
| System.arraycopy(originalTypes, 0, substitutedTypes = new ReferenceBinding[length], 0, i); |
| } |
| substitutedTypes[i] = substitutedParameter; |
| } else if (substitutedTypes != originalTypes) { |
| substitutedTypes[i] = originalType; |
| } |
| } |
| return substitutedTypes; |
| } |
| |
| /** |
| * Returns an array of types, where original types got substituted given a substitution. |
| * Only allocate an array if anything is different. |
| */ |
| public static TypeBinding[] substitute(Substitution substitution, TypeBinding[] originalTypes) { |
| TypeBinding[] substitutedTypes = originalTypes; |
| for (int i = 0, length = originalTypes.length; i < length; i++) { |
| TypeBinding originalType = originalTypes[i]; |
| TypeBinding substitutedParameter = substitution.substitute(originalType); |
| if (substitutedParameter != originalType) { |
| if (substitutedTypes == originalTypes) { |
| System.arraycopy(originalTypes, 0, substitutedTypes = new TypeBinding[length], 0, i); |
| } |
| substitutedTypes[i] = substitutedParameter; |
| } else if (substitutedTypes != originalTypes) { |
| substitutedTypes[i] = originalType; |
| } |
| } |
| return substitutedTypes; |
| } |
| |
| public int kind; |
| public Scope parent; |
| |
| protected Scope(int kind, Scope parent) { |
| this.kind = kind; |
| this.parent = parent; |
| } |
| |
| /* |
| * Boxing primitive |
| */ |
| public int boxing(int id) { |
| switch (id) { |
| case T_int : |
| return T_JavaLangInteger; |
| case T_byte : |
| return T_JavaLangByte; |
| case T_short : |
| return T_JavaLangShort; |
| case T_char : |
| return T_JavaLangCharacter; |
| case T_long : |
| return T_JavaLangLong; |
| case T_float : |
| return T_JavaLangFloat; |
| case T_double : |
| return T_JavaLangDouble; |
| case T_boolean : |
| return T_JavaLangBoolean; |
| case T_void : |
| return T_JavaLangVoid; |
| } |
| return id; |
| } |
| /* |
| * Boxing primitive |
| */ |
| public TypeBinding boxing(TypeBinding type) { |
| TypeBinding boxedType; |
| switch (type.id) { |
| case T_int : |
| boxedType = environment().getType(JAVA_LANG_INTEGER); |
| if (boxedType != null) return boxedType; |
| return new ProblemReferenceBinding( JAVA_LANG_INTEGER, NotFound); |
| case T_byte : |
| boxedType = environment().getType(JAVA_LANG_BYTE); |
| if (boxedType != null) return boxedType; |
| return new ProblemReferenceBinding( JAVA_LANG_BYTE, NotFound); |
| case T_short : |
| boxedType = environment().getType(JAVA_LANG_SHORT); |
| if (boxedType != null) return boxedType; |
| return new ProblemReferenceBinding( JAVA_LANG_SHORT, NotFound); |
| case T_char : |
| boxedType = environment().getType(JAVA_LANG_CHARACTER); |
| if (boxedType != null) return boxedType; |
| return new ProblemReferenceBinding( JAVA_LANG_CHARACTER, NotFound); |
| case T_long : |
| boxedType = environment().getType(JAVA_LANG_LONG); |
| if (boxedType != null) return boxedType; |
| return new ProblemReferenceBinding( JAVA_LANG_LONG, NotFound); |
| case T_float : |
| boxedType = environment().getType(JAVA_LANG_FLOAT); |
| if (boxedType != null) return boxedType; |
| return new ProblemReferenceBinding( JAVA_LANG_FLOAT, NotFound); |
| case T_double : |
| boxedType = environment().getType(JAVA_LANG_DOUBLE); |
| if (boxedType != null) return boxedType; |
| return new ProblemReferenceBinding( JAVA_LANG_DOUBLE, NotFound); |
| case T_boolean : |
| boxedType = environment().getType(JAVA_LANG_BOOLEAN); |
| if (boxedType != null) return boxedType; |
| return new ProblemReferenceBinding( JAVA_LANG_BOOLEAN, NotFound); |
| case T_void : |
| boxedType = environment().getType(JAVA_LANG_VOID); |
| if (boxedType != null) return boxedType; |
| return new ProblemReferenceBinding( JAVA_LANG_VOID, NotFound); |
| } |
| return type; |
| } |
| |
| public final ClassScope classScope() { |
| Scope scope = this; |
| do { |
| if (scope instanceof ClassScope) |
| return (ClassScope) scope; |
| scope = scope.parent; |
| } while (scope != null); |
| return null; |
| } |
| |
| /* Answer an int describing the relationship between the given type and unchecked exceptions. |
| * |
| * NotRelated |
| * EqualOrMoreSpecific : type is known for sure to be an unchecked exception type |
| * MoreGeneric : type is a supertype of an actual unchecked exception type |
| */ |
| public int compareUncheckedException(ReferenceBinding type) { |
| int comparison = compareTypes(type, getJavaLangRuntimeException()); |
| if (comparison != 0) return comparison; |
| return compareTypes(type, getJavaLangError()); |
| } |
| |
| public final CompilationUnitScope compilationUnitScope() { |
| Scope lastScope = null; |
| Scope scope = this; |
| do { |
| lastScope = scope; |
| scope = scope.parent; |
| } while (scope != null); |
| return (CompilationUnitScope) lastScope; |
| } |
| |
| /** |
| * Internal use only |
| * Given a method, returns null if arguments cannot be converted to parameters. |
| * Will answer a subsituted method in case the method was generic and type inference got triggered; |
| * in case the method was originally compatible, then simply answer it back. |
| */ |
| protected final MethodBinding computeCompatibleMethod(MethodBinding method, TypeBinding[] arguments, InvocationSite invocationSite) { |
| |
| TypeBinding[] genericTypeArguments = invocationSite.genericTypeArguments(); |
| TypeBinding[] parameters = method.parameters; |
| if (parameters == arguments |
| && (method.returnType.tagBits & HasTypeVariable) == 0 |
| && genericTypeArguments == null) |
| return method; |
| |
| int argLength = arguments.length; |
| if (argLength != parameters.length) |
| return null; // incompatible |
| |
| TypeVariableBinding[] typeVariables = method.typeVariables; |
| if (typeVariables != NoTypeVariables) { // generic method |
| method = ParameterizedGenericMethodBinding.computeCompatibleMethod(method, arguments, this, invocationSite); |
| if (method == null) return null; // incompatible |
| if (!method.isValidBinding()) return method; // bound check issue is taking precedence |
| parameters = method.parameters; // reacquire them after type inference has performed |
| } else if (genericTypeArguments != null) { |
| if (method instanceof ParameterizedGenericMethodBinding) { |
| if (method.declaringClass.isRawType()) |
| return new ProblemMethodBinding(method, method.selector, genericTypeArguments, TypeArgumentsForRawGenericMethod); // attempt to invoke generic method of raw type with type hints <String>foo() |
| } else { |
| return new ProblemMethodBinding(method, method.selector, genericTypeArguments, TypeParameterArityMismatch); |
| } |
| } |
| |
| argumentCompatibility: { |
| for (int i = 0; i < argLength; i++) |
| if (parameters[i] != arguments[i] && !arguments[i].isCompatibleWith(parameters[i])) |
| break argumentCompatibility; |
| return method; // compatible |
| } |
| if (genericTypeArguments != null) { |
| return new ProblemMethodBinding(method, method.selector, arguments, ParameterizedMethodTypeMismatch); |
| } |
| return null; // incompatible |
| } |
| |
| protected boolean connectTypeVariables(TypeParameter[] typeParameters) { |
| boolean noProblems = true; |
| if (typeParameters == null || environment().options.sourceLevel < ClassFileConstants.JDK1_5) return true; |
| |
| nextVariable : for (int i = 0, paramLength = typeParameters.length; i < paramLength; i++) { |
| TypeParameter typeParameter = typeParameters[i]; |
| TypeVariableBinding typeVariable = typeParameter.binding; |
| if (typeVariable == null) return false; |
| |
| typeVariable.superclass = getJavaLangObject(); |
| typeVariable.superInterfaces = NoSuperInterfaces; |
| // set firstBound to the binding of the first explicit bound in parameter declaration |
| typeVariable.firstBound = null; // first bound used to compute erasure |
| |
| TypeReference typeRef = typeParameter.type; |
| if (typeRef == null) |
| continue nextVariable; |
| ReferenceBinding superType = this.kind == METHOD_SCOPE |
| ? (ReferenceBinding) typeRef.resolveType((BlockScope)this) |
| : (ReferenceBinding) typeRef.resolveType((ClassScope)this); |
| if (superType == null) { |
| typeVariable.tagBits |= HierarchyHasProblems; |
| noProblems = false; |
| continue nextVariable; |
| } |
| if (superType.isTypeVariable()) { |
| TypeVariableBinding varSuperType = (TypeVariableBinding) superType; |
| if (varSuperType.rank >= typeVariable.rank && varSuperType.declaringElement == typeVariable.declaringElement) { |
| problemReporter().forwardTypeVariableReference(typeParameter, varSuperType); |
| typeVariable.tagBits |= HierarchyHasProblems; |
| noProblems = false; |
| continue nextVariable; |
| } |
| } |
| if (superType.isFinal()) |
| problemReporter().finalVariableBound(typeVariable, typeRef); |
| typeRef.resolvedType = superType; // hold onto the problem type |
| if (superType.isClass()) { |
| typeVariable.superclass = superType; |
| } else { |
| typeVariable.superInterfaces = new ReferenceBinding[] {superType}; |
| typeVariable.modifiers |= AccInterface; |
| } |
| typeVariable.firstBound = superType; // first bound used to compute erasure |
| |
| TypeReference[] boundRefs = typeParameter.bounds; |
| if (boundRefs != null) { |
| for (int j = 0, k = boundRefs.length; j < k; j++) { |
| typeRef = boundRefs[j]; |
| superType = this.kind == METHOD_SCOPE |
| ? (ReferenceBinding) typeRef.resolveType((BlockScope)this) |
| : (ReferenceBinding) typeRef.resolveType((ClassScope)this); |
| if (superType == null) { |
| typeVariable.tagBits |= HierarchyHasProblems; |
| noProblems = false; |
| continue nextVariable; |
| } |
| typeRef.resolvedType = superType; // hold onto the problem type |
| if (superType.isClass()) { |
| problemReporter().boundsMustBeAnInterface(typeRef, superType); |
| typeVariable.tagBits |= HierarchyHasProblems; |
| noProblems = false; |
| continue nextVariable; |
| } |
| int size = typeVariable.superInterfaces.length; |
| System.arraycopy(typeVariable.superInterfaces, 0, typeVariable.superInterfaces = new ReferenceBinding[size + 1], 0, size); |
| typeVariable.superInterfaces[size] = superType; |
| } |
| } |
| } |
| return noProblems; |
| } |
| |
| public TypeBinding convertToRawType(TypeBinding type) { |
| if (type.isArrayType()) { |
| TypeBinding leafComponentType = type.leafComponentType(); |
| if (leafComponentType.isGenericType()) |
| return createArrayType(environment().createRawType((ReferenceBinding) leafComponentType, null), type.dimensions()); |
| } else if (type.isGenericType()) { |
| return environment().createRawType((ReferenceBinding) type, null); |
| } |
| return type; |
| } |
| |
| public ArrayBinding createArrayType(TypeBinding type, int dimension) { |
| if (type.isValidBinding()) |
| return environment().createArrayType(type, dimension); |
| // do not cache obvious invalid types |
| return new ArrayBinding(type, dimension, environment()); |
| } |
| |
| public ParameterizedTypeBinding createParameterizedType(ReferenceBinding genericType, TypeBinding[] arguments, ReferenceBinding enclosingType) { |
| valid: { |
| if (!genericType.isValidBinding()) break valid; |
| for (int i = 0, max = arguments == null ? 0 : arguments.length; i < max; i++){ |
| if (!arguments[i].isValidBinding()) break valid; |
| } |
| return environment().createParameterizedType(genericType, arguments, enclosingType); |
| } |
| return new ParameterizedTypeBinding(genericType, arguments, enclosingType, environment()); |
| } |
| |
| public TypeVariableBinding[] createTypeVariables(TypeParameter[] typeParameters, Binding declaringElement) { |
| |
| PackageBinding unitPackage = compilationUnitScope().fPackage; |
| |
| // do not construct type variables if source < 1.5 |
| if (typeParameters == null || environment().options.sourceLevel < ClassFileConstants.JDK1_5) { |
| return NoTypeVariables; |
| } |
| TypeVariableBinding[] typeVariableBindings = NoTypeVariables; |
| |
| int length = typeParameters.length; |
| typeVariableBindings = new TypeVariableBinding[length]; |
| HashtableOfObject knownTypeParameterNames = new HashtableOfObject(length); |
| int count = 0; |
| nextParameter : for (int i = 0; i < length; i++) { |
| TypeParameter typeParameter = typeParameters[i]; |
| TypeVariableBinding parameterBinding = new TypeVariableBinding(typeParameter.name, declaringElement, i); |
| parameterBinding.fPackage = unitPackage; |
| typeParameter.binding = parameterBinding; |
| |
| if (knownTypeParameterNames.containsKey(typeParameter.name)) { |
| TypeVariableBinding previousBinding = (TypeVariableBinding) knownTypeParameterNames.get(typeParameter.name); |
| if (previousBinding != null) { |
| for (int j = 0; j < i; j++) { |
| TypeParameter previousParameter = typeParameters[j]; |
| if (previousParameter.binding == previousBinding) { |
| problemReporter().duplicateTypeParameterInType(previousParameter); |
| previousParameter.binding = null; |
| break; |
| } |
| } |
| } |
| knownTypeParameterNames.put(typeParameter.name, null); // ensure that the duplicate parameter is found & removed |
| problemReporter().duplicateTypeParameterInType(typeParameter); |
| typeParameter.binding = null; |
| } else { |
| knownTypeParameterNames.put(typeParameter.name, parameterBinding); |
| // remember that we have seen a field with this name |
| if (parameterBinding != null) |
| typeVariableBindings[count++] = parameterBinding; |
| } |
| // TODO should offer warnings to inform about hiding declaring, enclosing or member types |
| // ReferenceBinding type = sourceType; |
| // // check that the member does not conflict with an enclosing type |
| // do { |
| // if (CharOperation.equals(type.sourceName, memberContext.name)) { |
| // problemReporter().hidingEnclosingType(memberContext); |
| // continue nextParameter; |
| // } |
| // type = type.enclosingType(); |
| // } while (type != null); |
| // // check that the member type does not conflict with another sibling member type |
| // for (int j = 0; j < i; j++) { |
| // if (CharOperation.equals(referenceContext.memberTypes[j].name, memberContext.name)) { |
| // problemReporter().duplicateNestedType(memberContext); |
| // continue nextParameter; |
| // } |
| // } |
| } |
| if (count != length) { |
| System.arraycopy(typeVariableBindings, 0, typeVariableBindings = new TypeVariableBinding[count], 0, count); |
| } |
| return typeVariableBindings; |
| } |
| |
| public boolean detectCycle(ReferenceBinding superType) { |
| return false; |
| } |
| |
| public final ClassScope enclosingClassScope() { |
| Scope scope = this; |
| while ((scope = scope.parent) != null) { |
| if (scope instanceof ClassScope) return (ClassScope)scope; |
| } |
| return null; // may answer null if no type around |
| } |
| |
| public final MethodScope enclosingMethodScope() { |
| Scope scope = this; |
| while ((scope = scope.parent) != null) { |
| if (scope instanceof MethodScope) return (MethodScope)scope; |
| } |
| return null; // may answer null if no method around |
| } |
| |
| /* Answer the receiver's enclosing source type. |
| */ |
| public final SourceTypeBinding enclosingSourceType() { |
| Scope scope = this; |
| do { |
| if (scope instanceof ClassScope) |
| return ((ClassScope) scope).referenceContext.binding; |
| scope = scope.parent; |
| } while (scope != null); |
| return null; |
| } |
| public final LookupEnvironment environment() { |
| Scope scope, unitScope = this; |
| while ((scope = unitScope.parent) != null) |
| unitScope = scope; |
| return ((CompilationUnitScope) unitScope).environment; |
| } |
| |
| // abstract method lookup lookup (since maybe missing default abstract methods) |
| public MethodBinding findDefaultAbstractMethod( |
| ReferenceBinding receiverType, |
| char[] selector, |
| TypeBinding[] argumentTypes, |
| InvocationSite invocationSite, |
| ReferenceBinding classHierarchyStart, |
| MethodBinding matchingMethod, |
| ObjectVector found) { |
| |
| int startFoundSize = found.size; |
| ReferenceBinding currentType = classHierarchyStart; |
| while (currentType != null) { |
| matchingMethod = findMethodInSuperInterfaces(currentType, selector, found, matchingMethod); |
| currentType = currentType.superclass(); |
| } |
| int foundSize = found.size; |
| if (foundSize == startFoundSize) { |
| if (matchingMethod != null) compilationUnitScope().recordTypeReferences(matchingMethod.thrownExceptions); |
| return matchingMethod; // maybe null |
| } |
| MethodBinding[] candidates = new MethodBinding[foundSize - startFoundSize]; |
| int candidatesCount = 0; |
| MethodBinding problemMethod = null; |
| // argument type compatibility check |
| for (int i = startFoundSize; i < foundSize; i++) { |
| MethodBinding methodBinding = (MethodBinding) found.elementAt(i); |
| MethodBinding compatibleMethod = computeCompatibleMethod(methodBinding, argumentTypes, invocationSite); |
| if (compatibleMethod != null) { |
| if (compatibleMethod.isValidBinding()) |
| candidates[candidatesCount++] = compatibleMethod; |
| else if (problemMethod == null) |
| problemMethod = compatibleMethod; |
| } |
| } |
| |
| if (candidatesCount == 1) { |
| compilationUnitScope().recordTypeReferences(candidates[0].thrownExceptions); |
| return candidates[0]; |
| } |
| if (candidatesCount == 0) { // try to find a close match when the parameter order is wrong or missing some parameters |
| if (problemMethod != null) return problemMethod; |
| int argLength = argumentTypes.length; |
| nextMethod : for (int i = 0; i < foundSize; i++) { |
| MethodBinding methodBinding = (MethodBinding) found.elementAt(i); |
| TypeBinding[] params = methodBinding.parameters; |
| int paramLength = params.length; |
| nextArg: for (int a = 0; a < argLength; a++) { |
| TypeBinding arg = argumentTypes[a]; |
| for (int p = 0; p < paramLength; p++) |
| if (params[p] == arg) |
| continue nextArg; |
| continue nextMethod; |
| } |
| return methodBinding; |
| } |
| return (MethodBinding) found.elementAt(0); // no good match so just use the first one found |
| } |
| // no need to check for visibility - interface methods are public |
| return mostSpecificInterfaceMethodBinding(candidates, candidatesCount, invocationSite); |
| } |
| |
| // Internal use only |
| public ReferenceBinding findDirectMemberType(char[] typeName, ReferenceBinding enclosingType) { |
| if ((enclosingType.tagBits & HasNoMemberTypes) != 0) |
| return null; // know it has no member types (nor inherited member types) |
| |
| SourceTypeBinding enclosingSourceType = enclosingSourceType(); |
| compilationUnitScope().recordReference(enclosingType, typeName); |
| ReferenceBinding memberType = enclosingType.getMemberType(typeName); |
| if (memberType != null) { |
| compilationUnitScope().recordTypeReference(memberType); |
| if (enclosingSourceType == null |
| ? memberType.canBeSeenBy(getCurrentPackage()) |
| : memberType.canBeSeenBy(enclosingType, enclosingSourceType)) |
| return memberType; |
| return new ProblemReferenceBinding(typeName, memberType, NotVisible); |
| } |
| return null; |
| } |
| |
| // Internal use only |
| public MethodBinding findExactMethod( |
| ReferenceBinding receiverType, |
| char[] selector, |
| TypeBinding[] argumentTypes, |
| InvocationSite invocationSite) { |
| |
| compilationUnitScope().recordTypeReferences(argumentTypes); |
| MethodBinding exactMethod = receiverType.getExactMethod(selector, argumentTypes, compilationUnitScope()); |
| if (exactMethod != null) { |
| compilationUnitScope().recordTypeReferences(exactMethod.thrownExceptions); |
| // special treatment for Object.getClass() in 1.5 mode (substitute parameterized return type) |
| if (receiverType.isInterface() || exactMethod.canBeSeenBy(receiverType, invocationSite, this)) { |
| if (receiverType.id != T_Object |
| && argumentTypes == NoParameters |
| && CharOperation.equals(selector, GETCLASS) |
| && exactMethod.returnType.isParameterizedType()/*1.5*/) { |
| return ParameterizedMethodBinding.instantiateGetClass(receiverType, exactMethod, this); |
| } |
| // targeting a generic method could find an exact match with variable return type |
| if (exactMethod.typeVariables != NoTypeVariables || invocationSite.genericTypeArguments() != null) |
| exactMethod = computeCompatibleMethod(exactMethod, argumentTypes, invocationSite); |
| return exactMethod; |
| } |
| } |
| return null; |
| } |
| |
| // Internal use only |
| /* Answer the field binding that corresponds to fieldName. |
| Start the lookup at the receiverType. |
| InvocationSite implements |
| isSuperAccess(); this is used to determine if the discovered field is visible. |
| Only fields defined by the receiverType or its supertypes are answered; |
| a field of an enclosing type will not be found using this API. |
| |
| If no visible field is discovered, null is answered. |
| */ |
| public FieldBinding findField(TypeBinding receiverType, char[] fieldName, InvocationSite invocationSite, boolean needResolve) { |
| if (receiverType.isBaseType()) return null; |
| if (receiverType.isArrayType()) { |
| TypeBinding leafType = receiverType.leafComponentType(); |
| if (leafType instanceof ReferenceBinding) { |
| if (!((ReferenceBinding) leafType).canBeSeenBy(this)) |
| return new ProblemFieldBinding((ReferenceBinding)leafType, fieldName, ReceiverTypeNotVisible); |
| } |
| if (CharOperation.equals(fieldName, LENGTH)) |
| return ArrayBinding.ArrayLength; |
| return null; |
| } |
| |
| compilationUnitScope().recordTypeReference(receiverType); |
| |
| ReferenceBinding currentType = (ReferenceBinding) receiverType; |
| if (!currentType.canBeSeenBy(this)) |
| return new ProblemFieldBinding(currentType, fieldName, ReceiverTypeNotVisible); |
| |
| FieldBinding field = currentType.getField(fieldName, true /*resolve*/); |
| if (field != null) { |
| if (field.canBeSeenBy(currentType, invocationSite, this)) |
| return field; |
| return new ProblemFieldBinding(field /* closest match*/, field.declaringClass, fieldName, NotVisible); |
| } |
| // collect all superinterfaces of receiverType until the field is found in a supertype |
| ReferenceBinding[][] interfacesToVisit = null; |
| int lastPosition = -1; |
| FieldBinding visibleField = null; |
| boolean keepLooking = true; |
| boolean notVisible = false; |
| // we could hold onto the not visible field for extra error reporting |
| while (keepLooking) { |
| ReferenceBinding[] itsInterfaces = currentType.superInterfaces(); |
| if (itsInterfaces != NoSuperInterfaces) { |
| if (interfacesToVisit == null) |
| interfacesToVisit = new ReferenceBinding[5][]; |
| if (++lastPosition == interfacesToVisit.length) |
| System.arraycopy( |
| interfacesToVisit, |
| 0, |
| interfacesToVisit = new ReferenceBinding[lastPosition * 2][], |
| 0, |
| lastPosition); |
| interfacesToVisit[lastPosition] = itsInterfaces; |
| } |
| if ((currentType = currentType.superclass()) == null) |
| break; |
| |
| compilationUnitScope().recordTypeReference(currentType); |
| if ((field = currentType.getField(fieldName, needResolve)) != null) { |
| keepLooking = false; |
| if (field.canBeSeenBy(receiverType, invocationSite, this)) { |
| if (visibleField == null) |
| visibleField = field; |
| else |
| return new ProblemFieldBinding(visibleField /* closest match*/, visibleField.declaringClass, fieldName, Ambiguous); |
| } else { |
| notVisible = true; |
| } |
| } |
| } |
| |
| // walk all visible interfaces to find ambiguous references |
| if (interfacesToVisit != null) { |
| ProblemFieldBinding ambiguous = null; |
| done : for (int i = 0; i <= lastPosition; i++) { |
| ReferenceBinding[] interfaces = interfacesToVisit[i]; |
| for (int j = 0, length = interfaces.length; j < length; j++) { |
| ReferenceBinding anInterface = interfaces[j]; |
| if ((anInterface.tagBits & InterfaceVisited) == 0) { |
| // if interface as not already been visited |
| anInterface.tagBits |= InterfaceVisited; |
| compilationUnitScope().recordTypeReference(anInterface); |
| if ((field = anInterface.getField(fieldName, true /*resolve*/)) != null) { |
| if (visibleField == null) { |
| visibleField = field; |
| } else { |
| ambiguous = new ProblemFieldBinding(visibleField /* closest match*/, visibleField.declaringClass, fieldName, Ambiguous); |
| break done; |
| } |
| } else { |
| ReferenceBinding[] itsInterfaces = anInterface.superInterfaces(); |
| if (itsInterfaces != NoSuperInterfaces) { |
| if (++lastPosition == interfacesToVisit.length) |
| System.arraycopy( |
| interfacesToVisit, |
| 0, |
| interfacesToVisit = new ReferenceBinding[lastPosition * 2][], |
| 0, |
| lastPosition); |
| interfacesToVisit[lastPosition] = itsInterfaces; |
| } |
| } |
| } |
| } |
| } |
| |
| // bit reinitialization |
| for (int i = 0; i <= lastPosition; i++) { |
| ReferenceBinding[] interfaces = interfacesToVisit[i]; |
| for (int j = 0, length = interfaces.length; j < length; j++) |
| interfaces[j].tagBits &= ~InterfaceVisited; |
| } |
| if (ambiguous != null) |
| return ambiguous; |
| } |
| |
| if (visibleField != null) |
| return visibleField; |
| if (notVisible) |
| return new ProblemFieldBinding(currentType, fieldName, NotVisible); |
| return null; |
| } |
| |
| // Internal use only |
| public ReferenceBinding findMemberType(char[] typeName, ReferenceBinding enclosingType) { |
| if ((enclosingType.tagBits & HasNoMemberTypes) != 0) |
| return null; // know it has no member types (nor inherited member types) |
| |
| SourceTypeBinding enclosingSourceType = enclosingSourceType(); |
| PackageBinding currentPackage = getCurrentPackage(); |
| compilationUnitScope().recordReference(enclosingType, typeName); |
| ReferenceBinding memberType = enclosingType.getMemberType(typeName); |
| if (memberType != null) { |
| compilationUnitScope().recordTypeReference(memberType); |
| if (enclosingSourceType == null |
| ? memberType.canBeSeenBy(currentPackage) |
| : memberType.canBeSeenBy(enclosingType, enclosingSourceType)) |
| return memberType; |
| return new ProblemReferenceBinding(typeName, memberType, NotVisible); |
| } |
| |
| // collect all superinterfaces of receiverType until the memberType is found in a supertype |
| ReferenceBinding currentType = enclosingType; |
| ReferenceBinding[][] interfacesToVisit = null; |
| int lastPosition = -1; |
| ReferenceBinding visibleMemberType = null; |
| boolean keepLooking = true; |
| ReferenceBinding notVisible = null; |
| // we could hold onto the not visible field for extra error reporting |
| while (keepLooking) { |
| ReferenceBinding[] itsInterfaces = currentType.superInterfaces(); |
| if (itsInterfaces != NoSuperInterfaces) { |
| if (interfacesToVisit == null) |
| interfacesToVisit = new ReferenceBinding[5][]; |
| if (++lastPosition == interfacesToVisit.length) |
| System.arraycopy( |
| interfacesToVisit, |
| 0, |
| interfacesToVisit = new ReferenceBinding[lastPosition * 2][], |
| 0, |
| lastPosition); |
| interfacesToVisit[lastPosition] = itsInterfaces; |
| } |
| if ((currentType = currentType.superclass()) == null) |
| break; |
| |
| compilationUnitScope().recordReference(currentType, typeName); |
| if ((memberType = currentType.getMemberType(typeName)) != null) { |
| compilationUnitScope().recordTypeReference(memberType); |
| keepLooking = false; |
| if (enclosingSourceType == null |
| ? memberType.canBeSeenBy(currentPackage) |
| : memberType.canBeSeenBy(enclosingType, enclosingSourceType)) { |
| if (visibleMemberType == null) |
| visibleMemberType = memberType; |
| else |
| return new ProblemReferenceBinding(typeName, Ambiguous); |
| } else { |
| notVisible = memberType; |
| } |
| } |
| } |
| // walk all visible interfaces to find ambiguous references |
| if (interfacesToVisit != null) { |
| ProblemReferenceBinding ambiguous = null; |
| done : for (int i = 0; i <= lastPosition; i++) { |
| ReferenceBinding[] interfaces = interfacesToVisit[i]; |
| for (int j = 0, length = interfaces.length; j < length; j++) { |
| ReferenceBinding anInterface = interfaces[j]; |
| if ((anInterface.tagBits & InterfaceVisited) == 0) { |
| // if interface as not already been visited |
| anInterface.tagBits |= InterfaceVisited; |
| compilationUnitScope().recordReference(anInterface, typeName); |
| if ((memberType = anInterface.getMemberType(typeName)) != null) { |
| compilationUnitScope().recordTypeReference(memberType); |
| if (visibleMemberType == null) { |
| visibleMemberType = memberType; |
| } else { |
| ambiguous = new ProblemReferenceBinding(typeName, Ambiguous); |
| break done; |
| } |
| } else { |
| ReferenceBinding[] itsInterfaces = anInterface.superInterfaces(); |
| if (itsInterfaces != NoSuperInterfaces) { |
| if (++lastPosition == interfacesToVisit.length) |
| System.arraycopy( |
| interfacesToVisit, |
| 0, |
| interfacesToVisit = new ReferenceBinding[lastPosition * 2][], |
| 0, |
| lastPosition); |
| interfacesToVisit[lastPosition] = itsInterfaces; |
| } |
| } |
| } |
| } |
| } |
| |
| // bit reinitialization |
| for (int i = 0; i <= lastPosition; i++) { |
| ReferenceBinding[] interfaces = interfacesToVisit[i]; |
| for (int j = 0, length = interfaces.length; j < length; j++) |
| interfaces[j].tagBits &= ~InterfaceVisited; |
| } |
| if (ambiguous != null) |
| return ambiguous; |
| } |
| if (visibleMemberType != null) |
| return visibleMemberType; |
| if (notVisible != null) |
| return new ProblemReferenceBinding(typeName, notVisible, NotVisible); |
| return null; |
| } |
| |
| // Internal use only |
| public MethodBinding findMethod( |
| ReferenceBinding receiverType, |
| char[] selector, |
| TypeBinding[] argumentTypes, |
| InvocationSite invocationSite) { |
| |
| ReferenceBinding currentType = receiverType; |
| MethodBinding matchingMethod = null; |
| ObjectVector found = new ObjectVector(); //TODO (kent) should rewrite to remove #matchingMethod since found is allocated anyway |
| |
| compilationUnitScope().recordTypeReferences(argumentTypes); |
| |
| if (currentType.isInterface()) { |
| compilationUnitScope().recordTypeReference(currentType); |
| MethodBinding[] currentMethods = currentType.getMethods(selector); |
| int currentLength = currentMethods.length; |
| if (currentLength == 1) { |
| matchingMethod = currentMethods[0]; |
| } else if (currentLength > 1) { |
| found.addAll(currentMethods); |
| } |
| matchingMethod = findMethodInSuperInterfaces(currentType, selector, found, matchingMethod); |
| currentType = getJavaLangObject(); |
| } |
| |
| boolean isCompliant14 = compilationUnitScope().environment.options.complianceLevel >= ClassFileConstants.JDK1_4; |
| // superclass lookup |
| ReferenceBinding classHierarchyStart = currentType; |
| while (currentType != null) { |
| compilationUnitScope().recordTypeReference(currentType); |
| MethodBinding[] currentMethods = currentType.getMethods(selector); |
| int currentLength = currentMethods.length; |
| |
| /* |
| * if 1.4 compliant, must filter out redundant protected methods from superclasses |
| */ |
| if (isCompliant14){ |
| nextMethod: for (int i = 0; i < currentLength; i++){ |
| MethodBinding currentMethod = currentMethods[i]; |
| // protected method need to be checked only - default access is already dealt with in #canBeSeen implementation |
| // when checking that p.C -> q.B -> p.A cannot see default access members from A through B. |
| if ((currentMethod.modifiers & AccProtected) == 0) continue nextMethod; |
| if (matchingMethod != null){ |
| if (currentMethod.areParametersEqual(matchingMethod)){ |
| currentLength--; |
| currentMethods[i] = null; // discard this match |
| continue nextMethod; |
| } |
| } else { |
| for (int j = 0, max = found.size; j < max; j++) { |
| if (((MethodBinding)found.elementAt(j)).areParametersEqual(currentMethod)){ |
| currentLength--; |
| currentMethods[i] = null; |
| continue nextMethod; |
| } |
| } |
| } |
| } |
| } |
| |
| if (currentLength == 1 && matchingMethod == null && found.size == 0) { |
| matchingMethod = currentMethods[0]; |
| } else if (currentLength > 0) { |
| if (matchingMethod != null) { |
| found.add(matchingMethod); |
| matchingMethod = null; |
| } |
| // append currentMethods, filtering out null entries |
| int maxMethod = currentMethods.length; |
| if (maxMethod == currentLength) { // no method was eliminated for 1.4 compliance (see above) |
| found.addAll(currentMethods); |
| } else { |
| for (int i = 0, max = currentMethods.length; i < max; i++) { |
| MethodBinding currentMethod = currentMethods[i]; |
| if (currentMethod != null) found.add(currentMethod); |
| } |
| } |
| } |
| currentType = currentType.superclass(); |
| } |
| |
| // if found several candidates, then eliminate those not matching argument types |
| int foundSize = found.size; |
| MethodBinding[] candidates = null; |
| int candidatesCount = 0; |
| boolean checkedMatchingMethod = false; // is matchingMethod meeting argument expectation ? |
| MethodBinding problemMethod = null; |
| if (foundSize > 0) { |
| // argument type compatibility check |
| for (int i = 0; i < foundSize; i++) { |
| MethodBinding methodBinding = (MethodBinding) found.elementAt(i); |
| MethodBinding compatibleMethod = computeCompatibleMethod(methodBinding, argumentTypes, invocationSite); |
| if (compatibleMethod != null) { |
| if (compatibleMethod.isValidBinding()) { |
| switch (candidatesCount) { |
| case 0: |
| matchingMethod = compatibleMethod; // if only one match, reuse matchingMethod |
| checkedMatchingMethod = true; // matchingMethod is known to exist and match params here |
| break; |
| case 1: |
| candidates = new MethodBinding[foundSize]; // only lazily created if more than one match |
| candidates[0] = matchingMethod; // copy back |
| matchingMethod = null; |
| // fall through |
| default: |
| candidates[candidatesCount] = compatibleMethod; |
| } |
| candidatesCount++; |
| } else if (problemMethod == null) { |
| problemMethod = compatibleMethod; |
| } |
| } |
| } |
| } |
| if (candidatesCount > 0) |
| problemMethod = null; // forget the problem method if candidates were found |
| |
| // if only one matching method left (either from start or due to elimination of rivals), then match is in matchingMethod |
| if (matchingMethod != null) { |
| if (!checkedMatchingMethod) { |
| MethodBinding compatibleMethod = computeCompatibleMethod(matchingMethod, argumentTypes, invocationSite); |
| if (compatibleMethod != null) { |
| if (compatibleMethod.isValidBinding()) { |
| matchingMethod = compatibleMethod; |
| checkedMatchingMethod = true; |
| } else { |
| problemMethod = compatibleMethod; |
| } |
| } |
| } |
| if (checkedMatchingMethod) { |
| // (if no default abstract) must explicitly look for one instead, which could be a better match |
| if (!matchingMethod.canBeSeenBy(receiverType, invocationSite, this)) { |
| // ignore matching method (to be consistent with multiple matches, none visible (matching method is then null) |
| MethodBinding interfaceMethod = |
| findDefaultAbstractMethod(receiverType, selector, argumentTypes, invocationSite, classHierarchyStart, null, found); |
| if (interfaceMethod != null) return interfaceMethod; |
| compilationUnitScope().recordTypeReferences(matchingMethod.thrownExceptions); |
| return matchingMethod; |
| } |
| } |
| matchingMethod = |
| findDefaultAbstractMethod(receiverType, selector, argumentTypes, invocationSite, classHierarchyStart, matchingMethod, found); |
| if (matchingMethod != null) return matchingMethod; |
| return problemMethod; |
| } |
| |
| // no match was found, try to find a close match when the parameter order is wrong or missing some parameters |
| if (candidatesCount == 0) { |
| MethodBinding interfaceMethod = |
| findDefaultAbstractMethod(receiverType, selector, argumentTypes, invocationSite, classHierarchyStart, matchingMethod, found); |
| if (interfaceMethod != null) return interfaceMethod; |
| if (problemMethod != null) return problemMethod; |
| |
| int argLength = argumentTypes.length; |
| foundSize = found.size; |
| nextMethod : for (int i = 0; i < foundSize; i++) { |
| MethodBinding methodBinding = (MethodBinding) found.elementAt(i); |
| TypeBinding[] params = methodBinding.parameters; |
| int paramLength = params.length; |
| nextArg: for (int a = 0; a < argLength; a++) { |
| TypeBinding arg = argumentTypes[a]; |
| for (int p = 0; p < paramLength; p++) |
| if (params[p] == arg) |
| continue nextArg; |
| continue nextMethod; |
| } |
| return methodBinding; |
| } |
| return (MethodBinding) found.elementAt(0); // no good match so just use the first one found |
| } |
| |
| // check for duplicate parameterized methods |
| if (compilationUnitScope().environment.options.sourceLevel >= ClassFileConstants.JDK1_5) { |
| for (int i = 0; i < candidatesCount; i++) { |
| MethodBinding current = candidates[i]; |
| if (current instanceof ParameterizedMethodBinding) |
| for (int j = i + 1; j < candidatesCount; j++) |
| if (current.declaringClass == candidates[j].declaringClass && current.areParametersEqual(candidates[j])) |
| return new ProblemMethodBinding(candidates[i].selector, candidates[i].parameters, Ambiguous); |
| } |
| } |
| |
| // tiebreak using visibility check |
| int visiblesCount = 0; |
| for (int i = 0; i < candidatesCount; i++) { |
| MethodBinding methodBinding = candidates[i]; |
| if (methodBinding.canBeSeenBy(receiverType, invocationSite, this)) { |
| if (visiblesCount != i) { |
| candidates[i] = null; |
| candidates[visiblesCount] = methodBinding; |
| } |
| visiblesCount++; |
| } |
| } |
| if (visiblesCount == 1) { |
| compilationUnitScope().recordTypeReferences(candidates[0].thrownExceptions); |
| return candidates[0]; |
| } |
| if (visiblesCount == 0) { |
| MethodBinding interfaceMethod = |
| findDefaultAbstractMethod(receiverType, selector, argumentTypes, invocationSite, classHierarchyStart, matchingMethod, found); |
| if (interfaceMethod != null) return interfaceMethod; |
| return new ProblemMethodBinding(candidates[0], candidates[0].selector, candidates[0].parameters, NotVisible); |
| } |
| if (isCompliant14) |
| return mostSpecificMethodBinding(candidates, visiblesCount, invocationSite); |
| return candidates[0].declaringClass.isClass() |
| ? mostSpecificClassMethodBinding(candidates, visiblesCount, invocationSite) |
| : mostSpecificInterfaceMethodBinding(candidates, visiblesCount, invocationSite); |
| } |
| |
| // Internal use only |
| public MethodBinding findMethodForArray( |
| ArrayBinding receiverType, |
| char[] selector, |
| TypeBinding[] argumentTypes, |
| InvocationSite invocationSite) { |
| |
| TypeBinding leafType = receiverType.leafComponentType(); |
| if (leafType instanceof ReferenceBinding) { |
| if (!((ReferenceBinding) leafType).canBeSeenBy(this)) |
| return new ProblemMethodBinding(selector, TypeConstants.NoParameters, (ReferenceBinding)leafType, ReceiverTypeNotVisible); |
| } |
| |
| ReferenceBinding object = getJavaLangObject(); |
| MethodBinding methodBinding = object.getExactMethod(selector, argumentTypes); |
| if (methodBinding != null) { |
| // handle the method clone() specially... cannot be protected or throw exceptions |
| if (argumentTypes == NoParameters) { |
| switch (selector[0]) { |
| case 'c': |
| if (CharOperation.equals(selector, CLONE)) |
| return new UpdatedMethodBinding( |
| environment().options.targetJDK >= ClassFileConstants.JDK1_4 ? (TypeBinding)receiverType : (TypeBinding)object, // remember its array type for codegen purpose on target>=1.4.0 |
| (methodBinding.modifiers ^ AccProtected) | AccPublic, |
| CLONE, |
| methodBinding.returnType, |
| argumentTypes, |
| null, |
| object); |
| break; |
| case 'g': |
| if (CharOperation.equals(selector, GETCLASS) && methodBinding.returnType.isParameterizedType()/*1.5*/) { |
| return ParameterizedMethodBinding.instantiateGetClass(receiverType, methodBinding, this); |
| } |
| break; |
| } |
| } |
| if (methodBinding.canBeSeenBy(receiverType, invocationSite, this)) |
| return methodBinding; |
| } |
| // answers closest approximation, may not check argumentTypes or visibility |
| methodBinding = findMethod(object, selector, argumentTypes, invocationSite); |
| if (methodBinding == null) |
| return new ProblemMethodBinding(selector, argumentTypes, NotFound); |
| if (methodBinding.isValidBinding()) { |
| MethodBinding compatibleMethod = computeCompatibleMethod(methodBinding, argumentTypes, invocationSite); |
| if (compatibleMethod == null) |
| return new ProblemMethodBinding(methodBinding, selector, argumentTypes, NotFound); |
| if (!compatibleMethod.isValidBinding()) |
| return compatibleMethod; |
| |
| methodBinding = compatibleMethod; |
| if (!methodBinding.canBeSeenBy(receiverType, invocationSite, this)) |
| return new ProblemMethodBinding(methodBinding, selector, methodBinding.parameters, NotVisible); |
| } |
| return methodBinding; |
| } |
| |
| public MethodBinding findMethodInSuperInterfaces( |
| ReferenceBinding currentType, |
| char[] selector, |
| ObjectVector found, |
| MethodBinding matchingMethod) { |
| |
| ReferenceBinding[] itsInterfaces = currentType.superInterfaces(); |
| if (itsInterfaces != NoSuperInterfaces) { |
| ReferenceBinding[][] interfacesToVisit = new ReferenceBinding[5][]; |
| int lastPosition = -1; |
| if (++lastPosition == interfacesToVisit.length) |
| System.arraycopy( |
| interfacesToVisit, 0, |
| interfacesToVisit = new ReferenceBinding[lastPosition * 2][], 0, |
| lastPosition); |
| interfacesToVisit[lastPosition] = itsInterfaces; |
| |
| for (int i = 0; i <= lastPosition; i++) { |
| ReferenceBinding[] interfaces = interfacesToVisit[i]; |
| for (int j = 0, length = interfaces.length; j < length; j++) { |
| currentType = interfaces[j]; |
| if ((currentType.tagBits & InterfaceVisited) == 0) { |
| // if interface as not already been visited |
| currentType.tagBits |= InterfaceVisited; |
| |
| compilationUnitScope().recordTypeReference(currentType); |
| MethodBinding[] currentMethods = currentType.getMethods(selector); |
| int currentLength = currentMethods.length; |
| if (currentLength == 1 && matchingMethod == null && found.size == 0) { |
| matchingMethod = currentMethods[0]; |
| } else if (currentLength > 0) { |
| if (matchingMethod != null) { |
| found.add(matchingMethod); |
| matchingMethod = null; |
| } |
| found.addAll(currentMethods); |
| } |
| itsInterfaces = currentType.superInterfaces(); |
| if (itsInterfaces != NoSuperInterfaces) { |
| if (++lastPosition == interfacesToVisit.length) |
| System.arraycopy( |
| interfacesToVisit, 0, |
| interfacesToVisit = new ReferenceBinding[lastPosition * 2][], 0, |
| lastPosition); |
| interfacesToVisit[lastPosition] = itsInterfaces; |
| } |
| } |
| } |
| } |
| |
| // bit reinitialization |
| for (int i = 0; i <= lastPosition; i++) { |
| ReferenceBinding[] interfaces = interfacesToVisit[i]; |
| for (int j = 0, length = interfaces.length; j < length; j++) |
| interfaces[j].tagBits &= ~InterfaceVisited; |
| } |
| } |
| return matchingMethod; |
| } |
| |
| // Internal use only |
| public ReferenceBinding findType( |
| char[] typeName, |
| PackageBinding declarationPackage, |
| PackageBinding invocationPackage) { |
| |
| compilationUnitScope().recordReference(declarationPackage.compoundName, typeName); |
| ReferenceBinding typeBinding = declarationPackage.getType(typeName); |
| if (typeBinding == null) |
| return null; |
| |
| if (typeBinding.isValidBinding()) { |
| if (declarationPackage != invocationPackage && !typeBinding.canBeSeenBy(invocationPackage)) |
| return new ProblemReferenceBinding(typeName, typeBinding, NotVisible); |
| } |
| return typeBinding; |
| } |
| |
| public LocalVariableBinding findVariable(char[] variable) { |
| |
| return null; |
| } |
| |
| public TypeBinding getBaseType(char[] name) { |
| // list should be optimized (with most often used first) |
| int length = name.length; |
| if (length > 2 && length < 8) { |
| switch (name[0]) { |
| case 'i' : |
| if (length == 3 && name[1] == 'n' && name[2] == 't') |
| return IntBinding; |
| break; |
| case 'v' : |
| if (length == 4 && name[1] == 'o' && name[2] == 'i' && name[3] == 'd') |
| return VoidBinding; |
| break; |
| case 'b' : |
| if (length == 7 |
| && name[1] == 'o' |
| && name[2] == 'o' |
| && name[3] == 'l' |
| && name[4] == 'e' |
| && name[5] == 'a' |
| && name[6] == 'n') |
| return BooleanBinding; |
| if (length == 4 && name[1] == 'y' && name[2] == 't' && name[3] == 'e') |
| return ByteBinding; |
| break; |
| case 'c' : |
| if (length == 4 && name[1] == 'h' && name[2] == 'a' && name[3] == 'r') |
| return CharBinding; |
| break; |
| case 'd' : |
| if (length == 6 |
| && name[1] == 'o' |
| && name[2] == 'u' |
| && name[3] == 'b' |
| && name[4] == 'l' |
| && name[5] == 'e') |
| return DoubleBinding; |
| break; |
| case 'f' : |
| if (length == 5 |
| && name[1] == 'l' |
| && name[2] == 'o' |
| && name[3] == 'a' |
| && name[4] == 't') |
| return FloatBinding; |
| break; |
| case 'l' : |
| if (length == 4 && name[1] == 'o' && name[2] == 'n' && name[3] == 'g') |
| return LongBinding; |
| break; |
| case 's' : |
| if (length == 5 |
| && name[1] == 'h' |
| && name[2] == 'o' |
| && name[3] == 'r' |
| && name[4] == 't') |
| return ShortBinding; |
| } |
| } |
| return null; |
| } |
| |
| /* API |
| * |
| * Answer the binding that corresponds to the argument name. |
| * flag is a mask of the following values VARIABLE (= FIELD or LOCAL), TYPE, PACKAGE. |
| * Only bindings corresponding to the mask can be answered. |
| * |
| * For example, getBinding("foo", VARIABLE, site) will answer |
| * the binding for the field or local named "foo" (or an error binding if none exists). |
| * If a type named "foo" exists, it will not be detected (and an error binding will be answered) |
| * |
| * The VARIABLE mask has precedence over the TYPE mask. |
| * |
| * If the VARIABLE mask is not set, neither fields nor locals will be looked for. |
| * |
| * InvocationSite implements: |
| * isSuperAccess(); this is used to determine if the discovered field is visible. |
| * |
| * Limitations: cannot request FIELD independently of LOCAL, or vice versa |
| */ |
| public Binding getBinding(char[] name, int mask, InvocationSite invocationSite, boolean needResolve) { |
| |
| try { |
| Binding binding = null; |
| FieldBinding problemField = null; |
| if ((mask & VARIABLE) != 0) { |
| boolean insideStaticContext = false; |
| boolean insideConstructorCall = false; |
| |
| FieldBinding foundField = null; |
| // can be a problem field which is answered if a valid field is not found |
| ProblemFieldBinding foundInsideProblem = null; |
| // inside Constructor call or inside static context |
| Scope scope = this; |
| int depth = 0; |
| int foundDepth = 0; |
| ReferenceBinding foundActualReceiverType = null; |
| done : while (true) { // done when a COMPILATION_UNIT_SCOPE is found |
| switch (scope.kind) { |
| case METHOD_SCOPE : |
| MethodScope methodScope = (MethodScope) scope; |
| insideStaticContext |= methodScope.isStatic; |
| insideConstructorCall |= methodScope.isConstructorCall; |
| // Fall through... could duplicate the code below to save a cast - questionable optimization |
| case BLOCK_SCOPE : |
| LocalVariableBinding variableBinding = scope.findVariable(name); |
| // looks in this scope only |
| if (variableBinding != null) { |
| if (foundField != null && foundField.isValidBinding()) |
| return new ProblemFieldBinding( |
| foundField, // closest match |
| foundField.declaringClass, |
| name, |
| InheritedNameHidesEnclosingName); |
| if (depth > 0) |
| invocationSite.setDepth(depth); |
| return variableBinding; |
| } |
| break; |
| case CLASS_SCOPE : |
| ClassScope classScope = (ClassScope) scope; |
| SourceTypeBinding enclosingType = classScope.referenceContext.binding; |
| FieldBinding fieldBinding = |
| classScope.findField(enclosingType, name, invocationSite, needResolve); |
| // Use next line instead if willing to enable protected access accross inner types |
| // FieldBinding fieldBinding = findField(enclosingType, name, invocationSite); |
| if (fieldBinding != null) { // skip it if we did not find anything |
| if (fieldBinding.problemId() == Ambiguous) { |
| if (foundField == null || foundField.problemId() == NotVisible) |
| // supercedes any potential InheritedNameHidesEnclosingName problem |
| return fieldBinding; |
| // make the user qualify the field, likely wants the first inherited field (javac generates an ambiguous error instead) |
| return new ProblemFieldBinding( |
| foundField, // closest match |
| foundField.declaringClass, |
| name, |
| InheritedNameHidesEnclosingName); |
| } |
| |
| ProblemFieldBinding insideProblem = null; |
| if (fieldBinding.isValidBinding()) { |
| if (!fieldBinding.isStatic()) { |
| if (insideConstructorCall) { |
| insideProblem = |
| new ProblemFieldBinding( |
| fieldBinding, // closest match |
| fieldBinding.declaringClass, |
| name, |
| NonStaticReferenceInConstructorInvocation); |
| } else if (insideStaticContext) { |
| insideProblem = |
| new ProblemFieldBinding( |
| fieldBinding, // closest match |
| fieldBinding.declaringClass, |
| name, |
| NonStaticReferenceInStaticContext); |
| } |
| } |
| if (enclosingType == fieldBinding.declaringClass |
| || environment().options.complianceLevel >= ClassFileConstants.JDK1_4){ |
| // found a valid field in the 'immediate' scope (ie. not inherited) |
| // OR in 1.4 mode (inherited shadows enclosing) |
| if (foundField == null) { |
| if (depth > 0){ |
| invocationSite.setDepth(depth); |
| invocationSite.setActualReceiverType(enclosingType); |
| } |
| // return the fieldBinding if it is not declared in a superclass of the scope's binding (that is, inherited) |
| return insideProblem == null ? fieldBinding : insideProblem; |
| } |
| if (foundField.isValidBinding()) |
| // if a valid field was found, complain when another is found in an 'immediate' enclosing type (that is, not inherited) |
| if (foundField.declaringClass != fieldBinding.declaringClass) |
| // ie. have we found the same field - do not trust field identity yet |
| return new ProblemFieldBinding( |
| foundField, // closest match |
| foundField.declaringClass, |
| name, |
| InheritedNameHidesEnclosingName); |
| } |
| } |
| |
| if (foundField == null |
| || (foundField.problemId() == NotVisible |
| && fieldBinding.problemId() != NotVisible)) { |
| // only remember the fieldBinding if its the first one found or the previous one was not visible & fieldBinding is... |
| foundDepth = depth; |
| foundActualReceiverType = enclosingType; |
| foundInsideProblem = insideProblem; |
| foundField = fieldBinding; |
| } |
| } |
| depth++; |
| insideStaticContext |= enclosingType.isStatic(); |
| // 1EX5I8Z - accessing outer fields within a constructor call is permitted |
| // in order to do so, we change the flag as we exit from the type, not the method |
| // itself, because the class scope is used to retrieve the fields. |
| MethodScope enclosingMethodScope = scope.methodScope(); |
| insideConstructorCall = |
| enclosingMethodScope == null ? false : enclosingMethodScope.isConstructorCall; |
| break; |
| case COMPILATION_UNIT_SCOPE : |
| break done; |
| } |
| scope = scope.parent; |
| } |
| |
| if (foundInsideProblem != null) |
| return foundInsideProblem; |
| if (foundField != null) { |
| if (foundField.isValidBinding()){ |
| if (foundDepth > 0){ |
| invocationSite.setDepth(foundDepth); |
| invocationSite.setActualReceiverType(foundActualReceiverType); |
| } |
| return foundField; |
| } |
| problemField = foundField; |
| } |
| } |
| |
| // We did not find a local or instance variable. |
| if ((mask & TYPE) != 0) { |
| if ((binding = getBaseType(name)) != null) |
| return binding; |
| binding = getTypeOrPackage(name, (mask & PACKAGE) == 0 ? TYPE : TYPE | PACKAGE); |
| if (binding.isValidBinding() || mask == TYPE) |
| return binding; |
| // answer the problem type binding if we are only looking for a type |
| } else if ((mask & PACKAGE) != 0) { |
| compilationUnitScope().recordSimpleReference(name); |
| if ((binding = environment().getTopLevelPackage(name)) != null) |
| return binding; |
| } |
| if (problemField != null) return problemField; |
| return new ProblemBinding(name, enclosingSourceType(), NotFound); |
| } catch (AbortCompilation e) { |
| e.updateContext(invocationSite, referenceCompilationUnit().compilationResult); |
| throw e; |
| } |
| } |
| |
| public MethodBinding getConstructor(ReferenceBinding receiverType, TypeBinding[] argumentTypes, InvocationSite invocationSite) { |
| try { |
| compilationUnitScope().recordTypeReference(receiverType); |
| compilationUnitScope().recordTypeReferences(argumentTypes); |
| MethodBinding methodBinding = receiverType.getExactConstructor(argumentTypes); |
| if (methodBinding != null && methodBinding.canBeSeenBy(invocationSite, this)) { |
| // targeting a non generic constructor with type arguments ? |
| if (invocationSite.genericTypeArguments() != null) |
| methodBinding = computeCompatibleMethod(methodBinding, argumentTypes, invocationSite); |
| return methodBinding; |
| } |
| MethodBinding[] methods = receiverType.getMethods(ConstructorDeclaration.ConstantPoolName); |
| if (methods == NoMethods) |
| return new ProblemMethodBinding( |
| ConstructorDeclaration.ConstantPoolName, |
| argumentTypes, |
| NotFound); |
| |
| MethodBinding[] compatible = new MethodBinding[methods.length]; |
| int compatibleIndex = 0; |
| MethodBinding problemMethod = null; |
| for (int i = 0, length = methods.length; i < length; i++) { |
| MethodBinding compatibleMethod = computeCompatibleMethod(methods[i], argumentTypes, invocationSite); |
| if (compatibleMethod != null) { |
| if (compatibleMethod.isValidBinding()) |
| compatible[compatibleIndex++] = compatibleMethod; |
| else if (problemMethod == null) |
| problemMethod = compatibleMethod; |
| } |
| } |
| if (compatibleIndex == 0) { |
| if (problemMethod == null) |
| return new ProblemMethodBinding(ConstructorDeclaration.ConstantPoolName, argumentTypes, NotFound); |
| return problemMethod; |
| } |
| // need a more descriptive error... cannot convert from X to Y |
| |
| MethodBinding[] visible = new MethodBinding[compatibleIndex]; |
| int visibleIndex = 0; |
| for (int i = 0; i < compatibleIndex; i++) { |
| MethodBinding method = compatible[i]; |
| if (method.canBeSeenBy(invocationSite, this)) |
| visible[visibleIndex++] = method; |
| } |
| if (visibleIndex == 1) return visible[0]; |
| if (visibleIndex == 0) |
| return new ProblemMethodBinding( |
| compatible[0], |
| ConstructorDeclaration.ConstantPoolName, |
| compatible[0].parameters, |
| NotVisible); |
| return mostSpecificClassMethodBinding(visible, visibleIndex, invocationSite); |
| } catch (AbortCompilation e) { |
| e.updateContext(invocationSite, referenceCompilationUnit().compilationResult); |
| throw e; |
| } |
| } |
| |
| public final PackageBinding getCurrentPackage() { |
| Scope scope, unitScope = this; |
| while ((scope = unitScope.parent) != null) |
| unitScope = scope; |
| return ((CompilationUnitScope) unitScope).fPackage; |
| } |
| |
| /** |
| * Returns the modifiers of the innermost enclosing declaration. |
| * @return modifiers |
| */ |
| public int getDeclarationModifiers(){ |
| switch(this.kind){ |
| case Scope.BLOCK_SCOPE : |
| case Scope.METHOD_SCOPE : |
| MethodScope methodScope = methodScope(); |
| if (!methodScope.isInsideInitializer()){ |
| // check method modifiers to see if deprecated |
| MethodBinding context = ((AbstractMethodDeclaration)methodScope.referenceContext).binding; |
| if (context != null) { |
| return context.modifiers; |
| } |
| } else { |
| SourceTypeBinding type = ((BlockScope)this).referenceType().binding; |
| |
| // inside field declaration ? check field modifier to see if deprecated |
| if (methodScope.initializedField != null) { |
| return methodScope.initializedField.modifiers; |
| } |
| if (type != null) { |
| return type.modifiers; |
| } |
| } |
| break; |
| case Scope.CLASS_SCOPE : |
| ReferenceBinding context = ((ClassScope)this).referenceType().binding; |
| if (context != null) { |
| return context.modifiers; |
| } |
| break; |
| } |
| return -1; |
| } |
| |
| public FieldBinding getField(TypeBinding receiverType, char[] fieldName, InvocationSite invocationSite) { |
| try { |
| FieldBinding field = findField(receiverType, fieldName, invocationSite, true /*resolve*/); |
| if (field != null) return field; |
| |
| return new ProblemFieldBinding( |
| receiverType instanceof ReferenceBinding ? (ReferenceBinding) receiverType : null, |
| fieldName, |
| NotFound); |
| } catch (AbortCompilation e) { |
| e.updateContext(invocationSite, referenceCompilationUnit().compilationResult); |
| throw e; |
| } |
| } |
| |
| /* API |
| * |
| * Answer the method binding that corresponds to selector, argumentTypes. |
| * Start the lookup at the enclosing type of the receiver. |
| * InvocationSite implements |
| * isSuperAccess(); this is used to determine if the discovered method is visible. |
| * setDepth(int); this is used to record the depth of the discovered method |
| * relative to the enclosing type of the receiver. (If the method is defined |
| * in the enclosing type of the receiver, the depth is 0; in the next enclosing |
| * type, the depth is 1; and so on |
| * |
| * If no visible method is discovered, an error binding is answered. |
| */ |
| public MethodBinding getImplicitMethod(char[] selector, TypeBinding[] argumentTypes, InvocationSite invocationSite) { |
| |
| boolean insideStaticContext = false; |
| boolean insideConstructorCall = false; |
| MethodBinding foundMethod = null; |
| MethodBinding foundFuzzyProblem = null; |
| // the weird method lookup case (matches method name in scope, then arg types, then visibility) |
| MethodBinding foundInsideProblem = null; |
| // inside Constructor call or inside static context |
| Scope scope = this; |
| int depth = 0; |
| done : while (true) { // done when a COMPILATION_UNIT_SCOPE is found |
| switch (scope.kind) { |
| case METHOD_SCOPE : |
| MethodScope methodScope = (MethodScope) scope; |
| insideStaticContext |= methodScope.isStatic; |
| insideConstructorCall |= methodScope.isConstructorCall; |
| break; |
| case CLASS_SCOPE : |
| ClassScope classScope = (ClassScope) scope; |
| SourceTypeBinding receiverType = classScope.referenceContext.binding; |
| boolean isExactMatch = true; |
| // retrieve an exact visible match (if possible) |
| MethodBinding methodBinding = |
| (foundMethod == null) |
| ? classScope.findExactMethod(receiverType, selector, argumentTypes, invocationSite) |
| : classScope.findExactMethod( receiverType, foundMethod.selector, foundMethod.parameters, invocationSite); |
| // ? findExactMethod(receiverType, selector, argumentTypes, invocationSite) |
| // : findExactMethod(receiverType, foundMethod.selector, foundMethod.parameters, invocationSite); |
| if (methodBinding == null) { |
| // answers closest approximation, may not check argumentTypes or visibility |
| isExactMatch = false; |
| methodBinding = classScope.findMethod(receiverType, selector, argumentTypes, invocationSite); |
| // methodBinding = findMethod(receiverType, selector, argumentTypes, invocationSite); |
| } |
| if (methodBinding != null) { // skip it if we did not find anything |
| if (methodBinding.problemId() == Ambiguous) { |
| if (foundMethod == null || foundMethod.problemId() == NotVisible) { |
| // supercedes any potential InheritedNameHidesEnclosingName problem |
| return methodBinding; |
| } |
| // make the user qualify the method, likely wants the first inherited method (javac generates an ambiguous error instead) |
| return new ProblemMethodBinding( |
| methodBinding, // closest match |
| selector, |
| argumentTypes, |
| InheritedNameHidesEnclosingName); |
| } |
| MethodBinding fuzzyProblem = null; |
| MethodBinding insideProblem = null; |
| if (methodBinding.isValidBinding()) { |
| if (!isExactMatch) { |
| MethodBinding compatibleMethod = computeCompatibleMethod(methodBinding, argumentTypes, invocationSite); |
| if (compatibleMethod == null) { |
| if (foundMethod == null || foundMethod.problemId() == NotVisible) |
| // inherited mismatch is reported directly, not looking at enclosing matches |
| return new ProblemMethodBinding(methodBinding, selector, argumentTypes, NotFound); |
| // make the user qualify the method, likely wants the first inherited method (javac generates an ambiguous error instead) |
| fuzzyProblem = new ProblemMethodBinding(methodBinding, selector, methodBinding.parameters, InheritedNameHidesEnclosingName); |
| } else if (!compatibleMethod.isValidBinding()) { |
| fuzzyProblem = compatibleMethod; |
| } else { |
| methodBinding = compatibleMethod; |
| if (!methodBinding.canBeSeenBy(receiverType, invocationSite, classScope)) { |
| // using <classScope> instead of <this> for visibility check does grant all access to innerclass |
| fuzzyProblem = new ProblemMethodBinding(methodBinding, selector, methodBinding.parameters, NotVisible); |
| } |
| } |
| } |
| if (fuzzyProblem == null && !methodBinding.isStatic()) { |
| if (insideConstructorCall) { |
| insideProblem = |
| new ProblemMethodBinding( |
| methodBinding, // closest match |
| methodBinding.selector, |
| methodBinding.parameters, |
| NonStaticReferenceInConstructorInvocation); |
| } else if (insideStaticContext) { |
| insideProblem = |
| new ProblemMethodBinding( |
| methodBinding, // closest match |
| methodBinding.selector, |
| methodBinding.parameters, |
| NonStaticReferenceInStaticContext); |
| } |
| } |
| |
| if (receiverType == methodBinding.declaringClass |
| || (receiverType.getMethods(selector)) != NoMethods |
| || ((fuzzyProblem == null || fuzzyProblem.problemId() != NotVisible) && environment().options.complianceLevel >= ClassFileConstants.JDK1_4)){ |
| // found a valid method in the 'immediate' scope (ie. not inherited) |
| // OR the receiverType implemented a method with the correct name |
| // OR in 1.4 mode (inherited visible shadows enclosing) |
| if (foundMethod == null) { |
| if (depth > 0){ |
| invocationSite.setDepth(depth); |
| invocationSite.setActualReceiverType(receiverType); |
| } |
| // return the methodBinding if it is not declared in a superclass of the scope's binding (that is, inherited) |
| if (fuzzyProblem != null) |
| return fuzzyProblem; |
| if (insideProblem != null) |
| return insideProblem; |
| return methodBinding; |
| } |
| // if a method was found, complain when another is found in an 'immediate' enclosing type (that is, not inherited) |
| // NOTE: Unlike fields, a non visible method hides a visible method |
| if (foundMethod.declaringClass != methodBinding.declaringClass) |
| // ie. have we found the same method - do not trust field identity yet |
| return new ProblemMethodBinding( |
| methodBinding, // closest match |
| methodBinding.selector, |
| methodBinding.parameters, |
| InheritedNameHidesEnclosingName); |
| } |
| } |
| |
| if (foundMethod == null |
| || (foundMethod.problemId() == NotVisible |
| && methodBinding.problemId() != NotVisible)) { |
| // only remember the methodBinding if its the first one found or the previous one was not visible & methodBinding is... |
| // remember that private methods are visible if defined directly by an enclosing class |
| if (depth > 0){ |
| invocationSite.setDepth(depth); |
| invocationSite.setActualReceiverType(receiverType); |
| } |
| foundFuzzyProblem = fuzzyProblem; |
| foundInsideProblem = insideProblem; |
| if (fuzzyProblem == null) |
| foundMethod = methodBinding; // only keep it if no error was found |
| } |
| } |
| depth++; |
| insideStaticContext |= receiverType.isStatic(); |
| // 1EX5I8Z - accessing outer fields within a constructor call is permitted |
| // in order to do so, we change the flag as we exit from the type, not the method |
| // itself, because the class scope is used to retrieve the fields. |
| MethodScope enclosingMethodScope = scope.methodScope(); |
| insideConstructorCall = |
| enclosingMethodScope == null ? false : enclosingMethodScope.isConstructorCall; |
| break; |
| case COMPILATION_UNIT_SCOPE : |
| break done; |
| } |
| scope = scope.parent; |
| } |
| |
| if (foundFuzzyProblem != null) |
| return foundFuzzyProblem; |
| if (foundInsideProblem != null) |
| return foundInsideProblem; |
| if (foundMethod != null) |
| return foundMethod; |
| return new ProblemMethodBinding(selector, argumentTypes, NotFound); |
| } |
| |
| public final ReferenceBinding getJavaIoSerializable() { |
| compilationUnitScope().recordQualifiedReference(JAVA_IO_SERIALIZABLE); |
| ReferenceBinding type = environment().getType(JAVA_IO_SERIALIZABLE); |
| if (type != null) return type; |
| |
| problemReporter().isClassPathCorrect(JAVA_IO_SERIALIZABLE, referenceCompilationUnit()); |
| return null; // will not get here since the above error aborts the compilation |
| } |
| |
| public final ReferenceBinding getJavaLangAssertionError() { |
| compilationUnitScope().recordQualifiedReference(JAVA_LANG_ASSERTIONERROR); |
| ReferenceBinding type = environment().getType(JAVA_LANG_ASSERTIONERROR); |
| if (type != null) return type; |
| problemReporter().isClassPathCorrect(JAVA_LANG_ASSERTIONERROR, referenceCompilationUnit()); |
| return null; // will not get here since the above error aborts the compilation |
| } |
| |
| public final ReferenceBinding getJavaLangClass() { |
| compilationUnitScope().recordQualifiedReference(JAVA_LANG_CLASS); |
| ReferenceBinding type = environment().getType(JAVA_LANG_CLASS); |
| if (type != null) return type; |
| |
| problemReporter().isClassPathCorrect(JAVA_LANG_CLASS, referenceCompilationUnit()); |
| return null; // will not get here since the above error aborts the compilation |
| } |
| |
| public final ReferenceBinding getJavaLangCloneable() { |
| compilationUnitScope().recordQualifiedReference(JAVA_LANG_CLONEABLE); |
| ReferenceBinding type = environment().getType(JAVA_LANG_CLONEABLE); |
| if (type != null) return type; |
| |
| problemReporter().isClassPathCorrect(JAVA_LANG_CLONEABLE, referenceCompilationUnit()); |
| return null; // will not get here since the above error aborts the compilation |
| } |
| |
| public final ReferenceBinding getJavaLangError() { |
| compilationUnitScope().recordQualifiedReference(JAVA_LANG_ERROR); |
| ReferenceBinding type = environment().getType(JAVA_LANG_ERROR); |
| if (type != null) return type; |
| |
| problemReporter().isClassPathCorrect(JAVA_LANG_ERROR, referenceCompilationUnit()); |
| return null; // will not get here since the above error aborts the compilation |
| } |
| public final ReferenceBinding getJavaLangIterable() { |
| compilationUnitScope().recordQualifiedReference(JAVA_LANG_ITERABLE); |
| ReferenceBinding type = environment().getType(JAVA_LANG_ITERABLE); |
| if (type != null) return type; |
| |
| problemReporter().isClassPathCorrect(JAVA_LANG_ITERABLE, referenceCompilationUnit()); |
| return null; // will not get here since the above error aborts the compilation |
| } |
| public final ReferenceBinding getJavaLangObject() { |
| compilationUnitScope().recordQualifiedReference(JAVA_LANG_OBJECT); |
| ReferenceBinding type = environment().getType(JAVA_LANG_OBJECT); |
| if (type != null) return type; |
| |
| problemReporter().isClassPathCorrect(JAVA_LANG_OBJECT, referenceCompilationUnit()); |
| return null; // will not get here since the above error aborts the compilation |
| } |
| |
| public final ReferenceBinding getJavaLangRuntimeException() { |
| compilationUnitScope().recordQualifiedReference(JAVA_LANG_RUNTIMEEXCEPTION); |
| ReferenceBinding type = environment().getType(JAVA_LANG_RUNTIMEEXCEPTION); |
| if (type != null) return type; |
| |
| problemReporter().isClassPathCorrect(JAVA_LANG_RUNTIMEEXCEPTION, referenceCompilationUnit()); |
| return null; // will not get here since the above error aborts the compilation |
| } |
| |
| public final ReferenceBinding getJavaLangString() { |
| compilationUnitScope().recordQualifiedReference(JAVA_LANG_STRING); |
| ReferenceBinding type = environment().getType(JAVA_LANG_STRING); |
| if (type != null) return type; |
| |
| problemReporter().isClassPathCorrect(JAVA_LANG_STRING, referenceCompilationUnit()); |
| return null; // will not get here since the above error aborts the compilation |
| } |
| |
| public final ReferenceBinding getJavaLangThrowable() { |
| compilationUnitScope().recordQualifiedReference(JAVA_LANG_THROWABLE); |
| ReferenceBinding type = environment().getType(JAVA_LANG_THROWABLE); |
| if (type != null) return type; |
| |
| problemReporter().isClassPathCorrect(JAVA_LANG_THROWABLE, referenceCompilationUnit()); |
| return null; // will not get here since the above error aborts the compilation |
| } |
| public final ReferenceBinding getJavaUtilIterator() { |
| compilationUnitScope().recordQualifiedReference(JAVA_UTIL_ITERATOR); |
| ReferenceBinding type = environment().getType(JAVA_UTIL_ITERATOR); |
| if (type != null) return type; |
| |
| problemReporter().isClassPathCorrect(JAVA_UTIL_ITERATOR, referenceCompilationUnit()); |
| return null; // will not get here since the above error aborts the compilation |
| } |
| |
| /* Answer the type binding corresponding to the typeName argument, relative to the enclosingType. |
| */ |
| public final ReferenceBinding getMemberType(char[] typeName, ReferenceBinding enclosingType) { |
| ReferenceBinding memberType = findMemberType(typeName, enclosingType); |
| if (memberType != null) return memberType; |
| return new ProblemReferenceBinding(typeName, NotFound); |
| } |
| |
| public MethodBinding getMethod(TypeBinding receiverType, char[] selector, TypeBinding[] argumentTypes, InvocationSite invocationSite) { |
| try { |
| if (receiverType.isArrayType()) |
| return findMethodForArray((ArrayBinding) receiverType, selector, argumentTypes, invocationSite); |
| if (receiverType.isBaseType()) |
| return new ProblemMethodBinding(selector, argumentTypes, NotFound); |
| |
| ReferenceBinding currentType = (ReferenceBinding) receiverType; |
| if (!currentType.canBeSeenBy(this)) |
| return new ProblemMethodBinding(selector, argumentTypes, ReceiverTypeNotVisible); |
| |
| // retrieve an exact visible match (if possible) |
| MethodBinding methodBinding = findExactMethod(currentType, selector, argumentTypes, invocationSite); |
| if (methodBinding != null) return methodBinding; |
| |
| // TODO (kent) performance - we are accumulating super methods which are *hidden* during the walk (see testcase from bug 69141) |
| // answers closest approximation, may not check argumentTypes or visibility |
| methodBinding = findMethod(currentType, selector, argumentTypes, invocationSite); |
| if (methodBinding == null) |
| return new ProblemMethodBinding(selector, argumentTypes, NotFound); |
| if (methodBinding.isValidBinding()) { |
| MethodBinding compatibleMethod = computeCompatibleMethod(methodBinding, argumentTypes, invocationSite); |
| if (compatibleMethod == null) |
| return new ProblemMethodBinding(methodBinding, selector, argumentTypes, NotFound); |
| if (!compatibleMethod.isValidBinding()) |
| return compatibleMethod; |
| |
| methodBinding = compatibleMethod; |
| if (!methodBinding.canBeSeenBy(currentType, invocationSite, this)) |
| return new ProblemMethodBinding( methodBinding, selector, methodBinding.parameters, NotVisible); |
| } |
| return methodBinding; |
| } catch (AbortCompilation e) { |
| e.updateContext(invocationSite, referenceCompilationUnit().compilationResult); |
| throw e; |
| } |
| } |
| |
| /* Answer the package from the compoundName or null if it begins with a type. |
| * Intended to be used while resolving a qualified type name. |
| * |
| * NOTE: If a problem binding is returned, senders should extract the compound name |
| * from the binding & not assume the problem applies to the entire compoundName. |
| */ |
| public final Binding getPackage(char[][] compoundName) { |
| compilationUnitScope().recordQualifiedReference(compoundName); |
| Binding binding = getTypeOrPackage(compoundName[0], TYPE | PACKAGE); |
| if (binding == null) |
| return new ProblemReferenceBinding(compoundName[0], NotFound); |
| if (!binding.isValidBinding()) |
| return (ReferenceBinding) binding; |
| |
| if (!(binding instanceof PackageBinding)) return null; // compoundName does not start with a package |
| |
| int currentIndex = 1; |
| PackageBinding packageBinding = (PackageBinding) binding; |
| while (currentIndex < compoundName.length) { |
| binding = packageBinding.getTypeOrPackage(compoundName[currentIndex++]); |
| if (binding == null) |
| return new ProblemReferenceBinding( |
| CharOperation.subarray(compoundName, 0, currentIndex), |
| NotFound); |
| if (!binding.isValidBinding()) |
| return new ProblemReferenceBinding( |
| CharOperation.subarray(compoundName, 0, currentIndex), |
| binding.problemId()); |
| if (!(binding instanceof PackageBinding)) |
| return packageBinding; |
| packageBinding = (PackageBinding) binding; |
| } |
| return new ProblemReferenceBinding(compoundName, NotFound); |
| } |
| |
| /* Answer the type binding that corresponds the given name, starting the lookup in the receiver. |
| * The name provided is a simple source name (e.g., "Object" , "Point", ...) |
| */ |
| // The return type of this method could be ReferenceBinding if we did not answer base types. |
| // NOTE: We could support looking for Base Types last in the search, however any code using |
| // this feature would be extraordinarily slow. Therefore we don't do this |
| public final TypeBinding getType(char[] name) { |
| // Would like to remove this test and require senders to specially handle base types |
| TypeBinding binding = getBaseType(name); |
| if (binding != null) return binding; |
| return (ReferenceBinding) getTypeOrPackage(name, TYPE); |
| } |
| |
| /* Answer the type binding that corresponds to the given name, starting the lookup in the receiver |
| * or the packageBinding if provided. |
| * The name provided is a simple source name (e.g., "Object" , "Point", ...) |
| */ |
| public final TypeBinding getType(char[] name, PackageBinding packageBinding) { |
| if (packageBinding == null) |
| return getType(name); |
| |
| Binding binding = packageBinding.getTypeOrPackage(name); |
| if (binding == null) |
| return new ProblemReferenceBinding( |
| CharOperation.arrayConcat(packageBinding.compoundName, name), |
| NotFound); |
| if (!binding.isValidBinding()) |
| return new ProblemReferenceBinding( |
| CharOperation.arrayConcat(packageBinding.compoundName, name), |
| binding.problemId()); |
| |
| ReferenceBinding typeBinding = (ReferenceBinding) binding; |
| if (!typeBinding.canBeSeenBy(this)) |
| return new ProblemReferenceBinding( |
| CharOperation.arrayConcat(packageBinding.compoundName, name), |
| typeBinding, |
| NotVisible); |
| return typeBinding; |
| } |
| |
| /* Answer the type binding corresponding to the compoundName. |
| * |
| * NOTE: If a problem binding is returned, senders should extract the compound name |
| * from the binding & not assume the problem applies to the entire compoundName. |
| */ |
| public final TypeBinding getType(char[][] compoundName, int typeNameLength) { |
| if (typeNameLength == 1) { |
| // Would like to remove this test and require senders to specially handle base types |
| TypeBinding binding = getBaseType(compoundName[0]); |
| if (binding != null) return binding; |
| } |
| |
| compilationUnitScope().recordQualifiedReference(compoundName); |
| Binding binding = |
| getTypeOrPackage(compoundName[0], typeNameLength == 1 ? TYPE : TYPE | PACKAGE); |
| if (binding == null) |
| return new ProblemReferenceBinding(compoundName[0], NotFound); |
| if (!binding.isValidBinding()) |
| return (ReferenceBinding) binding; |
| |
| int currentIndex = 1; |
| boolean checkVisibility = false; |
| if (binding instanceof PackageBinding) { |
| PackageBinding packageBinding = (PackageBinding) binding; |
| while (currentIndex < typeNameLength) { |
| binding = packageBinding.getTypeOrPackage(compoundName[currentIndex++]); // does not check visibility |
| if (binding == null) |
| return new ProblemReferenceBinding( |
| CharOperation.subarray(compoundName, 0, currentIndex), |
| NotFound); |
| if (!binding.isValidBinding()) |
| return new ProblemReferenceBinding( |
| CharOperation.subarray(compoundName, 0, currentIndex), |
| binding.problemId()); |
| if (!(binding instanceof PackageBinding)) |
| break; |
| packageBinding = (PackageBinding) binding; |
| } |
| if (binding instanceof PackageBinding) |
| return new ProblemReferenceBinding( |
| CharOperation.subarray(compoundName, 0, currentIndex), |
| NotFound); |
| checkVisibility = true; |
| } |
| |
| // binding is now a ReferenceBinding |
| ReferenceBinding typeBinding = (ReferenceBinding) binding; |
| compilationUnitScope().recordTypeReference(typeBinding); |
| if (checkVisibility) // handles the fall through case |
| if (!typeBinding.canBeSeenBy(this)) |
| return new ProblemReferenceBinding( |
| CharOperation.subarray(compoundName, 0, currentIndex), |
| typeBinding, |
| NotVisible); |
| |
| while (currentIndex < typeNameLength) { |
| typeBinding = getMemberType(compoundName[currentIndex++], typeBinding); |
| if (!typeBinding.isValidBinding()) { |
| if (typeBinding instanceof ProblemReferenceBinding) { |
| ProblemReferenceBinding problemBinding = (ProblemReferenceBinding) typeBinding; |
| return new ProblemReferenceBinding( |
| CharOperation.subarray(compoundName, 0, currentIndex), |
| problemBinding.original, |
| typeBinding.problemId()); |
| } |
| return new ProblemReferenceBinding( |
| CharOperation.subarray(compoundName, 0, currentIndex), |
| typeBinding.problemId()); |
| } |
| } |
| return typeBinding; |
| } |
| |
| /* Internal use only |
| */ |
| final Binding getTypeOrPackage(char[] name, int mask) { |
| Scope scope = this; |
| ReferenceBinding foundType = null; |
| boolean insideStaticContext = false; |
| if ((mask & TYPE) == 0) { |
| Scope next = scope; |
| while ((next = scope.parent) != null) |
| scope = next; |
| } else { |
| done : while (true) { // done when a COMPILATION_UNIT_SCOPE is found |
| switch (scope.kind) { |
| case METHOD_SCOPE : |
| MethodScope methodScope = (MethodScope) scope; |
| AbstractMethodDeclaration methodDecl = methodScope.referenceMethod(); |
| if (methodDecl != null && methodDecl.binding != null) { |
| TypeVariableBinding typeVariable = methodDecl.binding.getTypeVariable(name); |
| if (typeVariable != null) return typeVariable; |
| } |
| insideStaticContext |= methodScope.isStatic; |
| case BLOCK_SCOPE : |
| ReferenceBinding localType = ((BlockScope) scope).findLocalType(name); // looks in this scope only |
| if (localType != null) { |
| if (foundType != null && foundType != localType) |
| return new ProblemReferenceBinding(name, InheritedNameHidesEnclosingName); |
| return localType; |
| } |
| break; |
| case CLASS_SCOPE : |
| SourceTypeBinding sourceType = ((ClassScope) scope).referenceContext.binding; |
| if (sourceType.isHierarchyBeingConnected()) { |
| // type variables take precedence over the source type, ex. class X <X> extends X == class X <Y> extends Y |
| TypeVariableBinding typeVariable = sourceType.getTypeVariable(name); |
| if (typeVariable != null) |
| return typeVariable; |
| if (CharOperation.equals(name, sourceType.sourceName)) |
| return sourceType; |
| break; |
| } |
| // type variables take precedence over member types |
| TypeVariableBinding typeVariable = sourceType.getTypeVariable(name); |
| if (typeVariable != null) { |
| if (insideStaticContext) // do not consider this type modifiers: access is legite within same type |
| return new ProblemReferenceBinding(name, NonStaticReferenceInStaticContext); |
| return typeVariable; |
| } |
| insideStaticContext |= (sourceType.modifiers & AccStatic) != 0; // not isStatic() |
| // 6.5.5.1 - member types have precedence over top-level type in same unit |
| ReferenceBinding memberType = findMemberType(name, sourceType); |
| if (memberType != null) { // skip it if we did not find anything |
| if (memberType.problemId() == Ambiguous) { |
| if (foundType == null || foundType.problemId() == NotVisible) |
| // supercedes any potential InheritedNameHidesEnclosingName problem |
| return memberType; |
| else |
| // make the user qualify the type, likely wants the first inherited type |
| return new ProblemReferenceBinding(name, InheritedNameHidesEnclosingName); |
| } |
| if (memberType.isValidBinding()) { |
| if (sourceType == memberType.enclosingType() |
| || environment().options.complianceLevel >= ClassFileConstants.JDK1_4) { |
| // found a valid type in the 'immediate' scope (ie. not inherited) |
| // OR in 1.4 mode (inherited shadows enclosing) |
| if (foundType == null) |
| return memberType; |
| if (foundType.isValidBinding()) |
| // if a valid type was found, complain when another is found in an 'immediate' enclosing type (ie. not inherited) |
| if (foundType != memberType) |
| return new ProblemReferenceBinding(name, InheritedNameHidesEnclosingName); |
| } |
| } |
| if (foundType == null || (foundType.problemId() == NotVisible && memberType.problemId() != NotVisible)) |
| // only remember the memberType if its the first one found or the previous one was not visible & memberType is... |
| foundType = memberType; |
| } |
| if (CharOperation.equals(sourceType.sourceName, name)) { |
| if (foundType != null && foundType != sourceType && foundType.problemId() != NotVisible) |
| return new ProblemReferenceBinding(name, InheritedNameHidesEnclosingName); |
| return sourceType; |
| } |
| break; |
| case COMPILATION_UNIT_SCOPE : |
| break done; |
| } |
| scope = scope.parent; |
| } |
| if (foundType != null && foundType.problemId() != NotVisible) |
| return foundType; |
| } |
| |
| // at this point the scope is a compilation unit scope |
| CompilationUnitScope unitScope = (CompilationUnitScope) scope; |
| PackageBinding currentPackage = unitScope.fPackage; |
| // ask for the imports + name |
| if ((mask & TYPE) != 0) { |
| // check single type imports. |
| |
| ImportBinding[] imports = unitScope.imports; |
| if (imports != null) { |
| HashtableOfObject typeImports = unitScope.resolvedSingeTypeImports; |
| if (typeImports != null) { |
| ImportBinding typeImport = (ImportBinding) typeImports.get(name); |
| if (typeImport != null) { |
| ImportReference importReference = typeImport.reference; |
| if (importReference != null) importReference.used = true; |
| return typeImport.resolvedImport; // already know its visible |
| } |
| } else { |
| // walk all the imports since resolvedSingeTypeImports is not yet initialized |
| for (int i = 0, length = imports.length; i < length; i++) { |
| ImportBinding typeImport = imports[i]; |
| if (!typeImport.onDemand) { |
| if (CharOperation.equals(typeImport.compoundName[typeImport.compoundName.length - 1], name)) { |
| if (unitScope.resolveSingleTypeImport(typeImport) != null) { |
| ImportReference importReference = typeImport.reference; |
| if (importReference != null) importReference.used = true; |
| return typeImport.resolvedImport; // already know its visible |
| } |
| } |
| } |
| } |
| } |
| } |
| // check if the name is in the current package, skip it if its a sub-package |
| unitScope.recordReference(currentPackage.compoundName, name); |
| Binding binding = currentPackage.getTypeOrPackage(name); |
| if (binding instanceof ReferenceBinding) return binding; // type is always visible to its own package |
| |
| // check on demand imports |
| if (imports != null) { |
| boolean foundInImport = false; |
| ReferenceBinding type = null; |
| for (int i = 0, length = imports.length; i < length; i++) { |
| ImportBinding someImport = imports[i]; |
| if (someImport.onDemand) { |
| Binding resolvedImport = someImport.resolvedImport; |
| ReferenceBinding temp = resolvedImport instanceof PackageBinding |
| ? findType(name, (PackageBinding) resolvedImport, currentPackage) |
| : findDirectMemberType(name, (ReferenceBinding) resolvedImport); |
| if (temp != null) { |
| if (temp.isValidBinding()) { |
| ImportReference importReference = someImport.reference; |
| if (importReference != null) importReference.used = true; |
| if (foundInImport) |
| // Answer error binding -- import on demand conflict; name found in two import on demand packages. |
| return new ProblemReferenceBinding(name, Ambiguous); |
| type = temp; |
| foundInImport = true; |
| } else if (foundType == null) { |
| foundType = temp; |
| } |
| } |
| } |
| } |
| if (type != null) return type; |
| } |
| } |
| |
| unitScope.recordSimpleReference(name); |
| if ((mask & PACKAGE) != 0) { |
| PackageBinding packageBinding = unitScope.environment.getTopLevelPackage(name); |
| if (packageBinding != null) return packageBinding; |
| } |
| |
| // Answer error binding -- could not find name |
| if (foundType != null) return foundType; // problem type from above |
| return new ProblemReferenceBinding(name, NotFound); |
| } |
| |
| // Added for code assist... NOT Public API |
| // DO NOT USE to resolve import references since this method assumes 'A.B' is relative to a single type import of 'p1.A' |
| // when it may actually mean the type B in the package A |
| // use CompilationUnitScope.getImport(char[][]) instead |
| public final Binding getTypeOrPackage(char[][] compoundName) { |
| int nameLength = compoundName.length; |
| if (nameLength == 1) { |
| TypeBinding binding = getBaseType(compoundName[0]); |
| if (binding != null) return binding; |
| } |
| Binding binding = getTypeOrPackage(compoundName[0], TYPE | PACKAGE); |
| if (!binding.isValidBinding()) return binding; |
| |
| int currentIndex = 1; |
| boolean checkVisibility = false; |
| if (binding instanceof PackageBinding) { |
| PackageBinding packageBinding = (PackageBinding) binding; |
| |
| while (currentIndex < nameLength) { |
| binding = packageBinding.getTypeOrPackage(compoundName[currentIndex++]); |
| if (binding == null) |
| return new ProblemReferenceBinding( |
| CharOperation.subarray(compoundName, 0, currentIndex), |
| NotFound); |
| if (!binding.isValidBinding()) |
| return new ProblemReferenceBinding( |
| CharOperation.subarray(compoundName, 0, currentIndex), |
| binding.problemId()); |
| if (!(binding instanceof PackageBinding)) |
| break; |
| packageBinding = (PackageBinding) binding; |
| } |
| if (binding instanceof PackageBinding) return binding; |
| checkVisibility = true; |
| } |
| // binding is now a ReferenceBinding |
| ReferenceBinding typeBinding = (ReferenceBinding) binding; |
| if (checkVisibility) // handles the fall through case |
| if (!typeBinding.canBeSeenBy(this)) |
| return new ProblemReferenceBinding( |
| CharOperation.subarray(compoundName, 0, currentIndex), |
| typeBinding, |
| NotVisible); |
| |
| while (currentIndex < nameLength) { |
| typeBinding = getMemberType(compoundName[currentIndex++], typeBinding); |
| // checks visibility |
| if (!typeBinding.isValidBinding()) |
| return new ProblemReferenceBinding( |
| CharOperation.subarray(compoundName, 0, currentIndex), |
| typeBinding.problemId()); |
| } |
| return typeBinding; |
| } |
| |
| // 5.1.10 |
| public TypeBinding[] greaterLowerBound(TypeBinding[] types) { |
| if (types == null) return null; |
| int length = types.length; |
| TypeBinding[] result = types; |
| int removed = 0; |
| for (int i = 0; i < length; i++) { |
| TypeBinding iType = result[i]; |
| for (int j = 0; j < length; j++) { |
| if (i == j) continue; |
| TypeBinding jType = result[j]; |
| if (jType == null) continue; |
| if (iType.isCompatibleWith(jType)) { // if Vi <: Vj, Vj is removed |
| if (result == types) { // defensive copy |
| System.arraycopy(result, 0, result = new TypeBinding[length], 0, length); |
| } |
| result[j] = null; |
| removed ++; |
| } |
| } |
| } |
| if (removed == 0) return result; |
| TypeBinding[] trimmedResult = new TypeBinding[length - removed]; |
| for (int i = 0, index = 0; i < length; i++) { |
| TypeBinding iType = result[i]; |
| if (iType != null) { |
| trimmedResult[index++] = iType; |
| } |
| } |
| return trimmedResult; |
| } |
| |
| /* Answer true if the scope is nested inside a given field declaration. |
| * Note: it works as long as the scope.fieldDeclarationIndex is reflecting the field being traversed |
| * e.g. during name resolution. |
| */ |
| public final boolean isDefinedInField(FieldBinding field) { |
| Scope scope = this; |
| do { |
| if (scope instanceof MethodScope) { |
| MethodScope methodScope = (MethodScope) scope; |
| if (methodScope.initializedField == field) return true; |
| } |
| scope = scope.parent; |
| } while (scope != null); |
| return false; |
| } |
| |
| /* Answer true if the scope is nested inside a given method declaration |
| */ |
| public final boolean isDefinedInMethod(MethodBinding method) { |
| Scope scope = this; |
| do { |
| if (scope instanceof MethodScope) { |
| ReferenceContext refContext = ((MethodScope) scope).referenceContext; |
| if (refContext instanceof AbstractMethodDeclaration |
| && ((AbstractMethodDeclaration)refContext).binding == method) { |
| return true; |
| } |
| } |
| scope = scope.parent; |
| } while (scope != null); |
| return false; |
| } |
| |
| /* Answer whether the type is defined in the same compilation unit as the receiver |
| */ |
| public final boolean isDefinedInSameUnit(ReferenceBinding type) { |
| // find the outer most enclosing type |
| ReferenceBinding enclosingType = type; |
| while ((type = enclosingType.enclosingType()) != null) |
| enclosingType = type; |
| |
| // find the compilation unit scope |
| Scope scope, unitScope = this; |
| while ((scope = unitScope.parent) != null) |
| unitScope = scope; |
| |
| // test that the enclosingType is not part of the compilation unit |
| SourceTypeBinding[] topLevelTypes = |
| ((CompilationUnitScope) unitScope).topLevelTypes; |
| for (int i = topLevelTypes.length; --i >= 0;) |
| if (topLevelTypes[i] == enclosingType) |
| return true; |
| return false; |
| } |
| |
| /* Answer true if the scope is nested inside a given type declaration |
| */ |
| public final boolean isDefinedInType(ReferenceBinding type) { |
| Scope scope = this; |
| do { |
| if (scope instanceof ClassScope) |
| if (((ClassScope) scope).referenceContext.binding == type){ |
| return true; |
| } |
| scope = scope.parent; |
| } while (scope != null); |
| return false; |
| } |
| |
| public boolean isInsideDeprecatedCode(){ |
| switch(this.kind){ |
| case Scope.BLOCK_SCOPE : |
| case Scope.METHOD_SCOPE : |
| MethodScope methodScope = methodScope(); |
| if (!methodScope.isInsideInitializer()){ |
| // check method modifiers to see if deprecated |
| MethodBinding context = ((AbstractMethodDeclaration)methodScope.referenceContext).binding; |
| if (context != null && context.isViewedAsDeprecated()) { |
| return true; |
| } |
| } else { |
| SourceTypeBinding type = ((BlockScope)this).referenceType().binding; |
| // inside field declaration ? check field modifier to see if deprecated |
| if (methodScope.initializedField != null && methodScope.initializedField.isViewedAsDeprecated()) { |
| return true; |
| } |
| if (type != null && type.isViewedAsDeprecated()) { |
| return true; |
| } |
| } |
| break; |
| case Scope.CLASS_SCOPE : |
| ReferenceBinding context = ((ClassScope)this).referenceType().binding; |
| if (context != null && context.isViewedAsDeprecated()) { |
| return true; |
| } |
| break; |
| } |
| return false; |
| } |
| private TypeBinding leastContainingInvocation(TypeBinding mec, List invocations) { |
| int length = invocations.size(); |
| if (length == 0) return mec; |
| if (length == 1) return (TypeBinding) invocations.get(0); |
| int argLength = mec.typeVariables().length; |
| if (argLength == 0) return mec; // should be caught by no invocation check |
| |
| // infer proper parameterized type from invocations |
| TypeBinding[] bestArguments = new TypeBinding[argLength]; |
| for (int i = 0; i < length; i++) { |
| TypeBinding invocation = (TypeBinding)invocations.get(i); |
| if (invocation.isGenericType()) { |
| for (int j = 0; j < argLength; j++) { |
| TypeBinding bestArgument = leastContainingTypeArgument(bestArguments[j], invocation.typeVariables()[j], (ReferenceBinding) mec, j); |
| if (bestArgument == null) return null; |
| bestArguments[j] = bestArgument; |
| } |
| } else if (invocation.isParameterizedType()) { |
| ParameterizedTypeBinding parameterizedType = (ParameterizedTypeBinding)invocation; |
| for (int j = 0; j < argLength; j++) { |
| TypeBinding bestArgument = leastContainingTypeArgument(bestArguments[j], parameterizedType.arguments[j], (ReferenceBinding) mec, j); |
| if (bestArgument == null) return null; |
| bestArguments[j] = bestArgument; |
| } |
| } else if (invocation.isRawType()) { |
| return invocation; // raw type is taking precedence |
| } |
| } |
| return createParameterizedType((ReferenceBinding) mec, bestArguments, null); |
| } |
| |
| // JLS 15.12.2 |
| private TypeBinding leastContainingTypeArgument(TypeBinding u, TypeBinding v, ReferenceBinding genericType, int rank) { |
| if (u == null) return v; |
| if (u == v) return u; |
| if (v.isWildcard()) { |
| WildcardBinding wildV = (WildcardBinding) v; |
| if (u.isWildcard()) { |
| WildcardBinding wildU = (WildcardBinding) u; |
| switch (wildU.kind) { |
| // ? extends U |
| case Wildcard.EXTENDS : |
| switch(wildV.kind) { |
| // ? extends U, ? extends V |
| case Wildcard.EXTENDS : |
| TypeBinding lub = lowerUpperBound(new TypeBinding[]{wildU.bound,wildV.bound}); |
| if (lub == null) return null; |
| return environment().createWildcard(genericType, rank, lub, Wildcard.EXTENDS); |
| // ? extends U, ? SUPER V |
| case Wildcard.SUPER : |
| if (wildU.bound == wildV.bound) return wildU.bound; |
| return environment().createWildcard(genericType, rank, null, Wildcard.UNBOUND); |
| } |
| break; |
| // ? super U |
| case Wildcard.SUPER : |
| // ? super U, ? super V |
| if (wildU.kind == Wildcard.SUPER) { |
| TypeBinding[] glb = greaterLowerBound(new TypeBinding[]{wildU.bound,wildV.bound}); |
| if (glb == null) return null; |
| return environment().createWildcard(genericType, rank, glb[0], Wildcard.SUPER); // TODO (philippe) need to capture entire bounds |
| } |
| } |
| } else { |
| switch (wildV.kind) { |
| // U, ? extends V |
| case Wildcard.EXTENDS : |
| TypeBinding lub = lowerUpperBound(new TypeBinding[]{u,wildV.bound}); |
| if (lub == null) return null; |
| return environment().createWildcard(genericType, rank, lub, Wildcard.EXTENDS); |
| // U, ? super V |
| case Wildcard.SUPER : |
| TypeBinding[] glb = greaterLowerBound(new TypeBinding[]{u,wildV.bound}); |
| if (glb == null) return null; |
| return environment().createWildcard(genericType, rank, glb[0], Wildcard.SUPER); // TODO (philippe) need to capture entire bounds |
| case Wildcard.UNBOUND : |
| } |
| } |
| } else if (u.isWildcard()) { |
| WildcardBinding wildU = (WildcardBinding) u; |
| switch (wildU.kind) { |
| // U, ? extends V |
| case Wildcard.EXTENDS : |
| TypeBinding lub = lowerUpperBound(new TypeBinding[]{wildU.bound, v}); |
| if (lub == null) return null; |
| return environment().createWildcard(genericType, rank, lub, Wildcard.EXTENDS); |
| // U, ? super V |
| case Wildcard.SUPER : |
| TypeBinding[] glb = greaterLowerBound(new TypeBinding[]{wildU.bound, v}); |
| if (glb == null) return null; |
| return environment().createWildcard(genericType, rank, glb[0], Wildcard.SUPER); // TODO (philippe) need to capture entire bounds |
| case Wildcard.UNBOUND : |
| } |
| } |
| TypeBinding lub = lowerUpperBound(new TypeBinding[]{u,v}); |
| if (lub == null) return null; |
| return environment().createWildcard(genericType, rank, lub, Wildcard.EXTENDS); |
| } |
| |
| // 15.12.2 |
| public TypeBinding lowerUpperBound(TypeBinding[] types) { |
| |
| ArrayList invocations = new ArrayList(1); |
| TypeBinding mec = minimalErasedCandidate(types, invocations); |
| return leastContainingInvocation(mec, invocations); |
| } |
| |
| public final MethodScope methodScope() { |
| Scope scope = this; |
| do { |
| if (scope instanceof MethodScope) |
| return (MethodScope) scope; |
| scope = scope.parent; |
| } while (scope != null); |
| return null; |
| } |
| |
| /** |
| * Returns the most specific type compatible with all given types. |
| * (i.e. most specific common super type) |
| * If no types is given, will return VoidBinding. If not compatible |
| * reference type is found, returns null. |
| */ |
| private TypeBinding minimalErasedCandidate(TypeBinding[] types, List invocations) { |
| Map allInvocations = new HashMap(2); |
| int length = types.length; |
| int indexOfFirst = -1, actualLength = 0; |
| for (int i = 0; i < length; i++) { |
| TypeBinding type = types[i]; |
| if (type == null) continue; |
| if (type.isBaseType()) return null; |
| if (indexOfFirst < 0) indexOfFirst = i; |
| actualLength ++; |
| } |
| switch (actualLength) { |
| case 0: return VoidBinding; |
| case 1: return types[indexOfFirst]; |
| } |
| |
| // record all supertypes of type |
| // intersect with all supertypes of otherType |
| TypeBinding firstType = types[indexOfFirst]; |
| TypeBinding[] superTypes; |
| int superLength; |
| if (firstType.isBaseType()) { |
| return null; |
| } else if (firstType.isArrayType()) { |
| superLength = 4; |
| if (firstType.erasure() != firstType) { |
| ArrayList someInvocations = new ArrayList(1); |
| someInvocations.add(firstType); |
| allInvocations.put(firstType.erasure(), someInvocations); |
| } |
| superTypes = new TypeBinding[] { |
| firstType.erasure(), |
| getJavaIoSerializable(), |
| getJavaLangCloneable(), |
| getJavaLangObject(), |
| }; |
| } else { |
| ArrayList typesToVisit = new ArrayList(5); |
| if (firstType.erasure() != firstType) { |
| ArrayList someInvocations = new ArrayList(1); |
| someInvocations.add(firstType); |
| allInvocations.put(firstType.erasure(), someInvocations); |
| } |
| typesToVisit.add(firstType.erasure()); |
| ReferenceBinding currentType = (ReferenceBinding)firstType; |
| for (int i = 0, max = 1; i < max; i++) { |
| currentType = (ReferenceBinding) typesToVisit.get(i); |
| TypeBinding itsSuperclass = currentType.superclass(); |
| if (itsSuperclass != null) { |
| TypeBinding itsSuperclassErasure = itsSuperclass.erasure(); |
| if (!typesToVisit.contains(itsSuperclassErasure)) { |
| if (itsSuperclassErasure != itsSuperclass) { |
| ArrayList someInvocations = new ArrayList(1); |
| someInvocations.add(itsSuperclass); |
| allInvocations.put(itsSuperclassErasure, someInvocations); |
| } |
| typesToVisit.add(itsSuperclassErasure); |
| max++; |
| } |
| } |
| ReferenceBinding[] itsInterfaces = currentType.superInterfaces(); |
| for (int j = 0, count = itsInterfaces.length; j < count; j++) { |
| TypeBinding itsInterface = itsInterfaces[j]; |
| TypeBinding itsInterfaceErasure = itsInterface.erasure(); |
| if (!typesToVisit.contains(itsInterfaceErasure)) { |
| if (itsInterfaceErasure != itsInterface) { |
| ArrayList someInvocations = new ArrayList(1); |
| someInvocations.add(itsInterface); |
| allInvocations.put(itsInterfaceErasure, someInvocations); |
| } |
| typesToVisit.add(itsInterfaceErasure); |
| max++; |
| } |
| } |
| } |
| superLength = typesToVisit.size(); |
| superTypes = new TypeBinding[superLength]; |
| typesToVisit.toArray(superTypes); |
| } |
| int remaining = superLength; |
| nextOtherType: for (int i = indexOfFirst+1; i < length; i++) { |
| TypeBinding otherType = types[i]; |
| if (otherType == null) |
| continue nextOtherType; |
| else if (otherType.isArrayType()) { |
| nextSuperType: for (int j = 0; j < superLength; j++) { |
| TypeBinding superType = superTypes[j]; |
| if (superType == null || superType == otherType) continue nextSuperType; |
| switch (superType.id) { |
| case T_JavaIoSerializable : |
| case T_JavaLangCloneable : |
| case T_JavaLangObject : |
| continue nextSuperType; |
| } |
| superTypes[j] = null; |
| if (--remaining == 0) return null; |
| |
| } |
| continue nextOtherType; |
| } |
| ReferenceBinding otherRefType = (ReferenceBinding) otherType; |
| nextSuperType: for (int j = 0; j < superLength; j++) { |
| TypeBinding superType = superTypes[j]; |
| if (superType == null) continue nextSuperType; |
| if (otherRefType.erasure().isCompatibleWith(superType)) { |
| TypeBinding match = otherRefType.findSuperTypeErasingTo((ReferenceBinding)superType); |
| if (match != null && match.erasure() != match) { // match can be null: interface.findSuperTypeErasingTo(Object) |
| ArrayList someInvocations = (ArrayList) allInvocations.get(superType); |
| if (someInvocations == null) someInvocations = new ArrayList(1); |
| someInvocations.add(match); |
| allInvocations.put(superType, someInvocations); |
| } |
| break nextSuperType; |
| } else { |
| superTypes[j] = null; |
| if (--remaining == 0) return null; |
| } |
| } |
| } |
| // per construction, first non-null supertype is most specific common supertype |
| for (int i = 0; i < superLength; i++) { |
| TypeBinding superType = superTypes[i]; |
| if (superType != null) { |
| List matchingInvocations = (List)allInvocations.get(superType); |
| if (matchingInvocations != null) invocations.addAll(matchingInvocations); |
| return superType; |
| } |
| } |
| return null; |
| } |
| |
| // Internal use only |
| /* All methods in visible are acceptable matches for the method in question... |
| * The methods defined by the receiver type appear before those defined by its |
| * superclass and so on. We want to find the one which matches best. |
| * |
| * Since the receiver type is a class, we know each method's declaring class is |
| * either the receiver type or one of its superclasses. It is an error if the best match |
| * is defined by a superclass, when a lesser match is defined by the receiver type |
| * or a closer superclass. |
| */ |
| protected final MethodBinding mostSpecificClassMethodBinding(MethodBinding[] visible, int visibleSize, InvocationSite invocationSite) { |
| MethodBinding problemMethod = null; |
| MethodBinding previous = null; |
| nextVisible : for (int i = 0; i < visibleSize; i++) { |
| MethodBinding method = visible[i]; |
| if (previous != null && method.declaringClass != previous.declaringClass) |
| break; // cannot answer a method farther up the hierarchy than the first method found |
| |
| if (!method.isStatic()) previous = method; // no ambiguity for static methods |
| for (int j = 0; j < visibleSize; j++) { |
| if (i == j) continue; |
| MethodBinding compatibleMethod = computeCompatibleMethod(visible[j], method.parameters, invocationSite); |
| if (compatibleMethod == null || !compatibleMethod.isValidBinding()) { |
| if (problemMethod == null) |
| problemMethod = compatibleMethod; |
| continue nextVisible; |
| } |
| } |
| compilationUnitScope().recordTypeReferences(method.thrownExceptions); |
| return method; |
| } |
| if (problemMethod == null) |
| return new ProblemMethodBinding(visible[0].selector, visible[0].parameters, Ambiguous); |
| return problemMethod; |
| } |
| |
| /** |
| * Returns the most specific type compatible with all given types. |
| * (i.e. most specific common super type) |
| * If no types is given, will return VoidBinding. If not compatible |
| * reference type is found, returns null. |
| * @deprecated - use lowerUpperBound(TypeBinding[]) instead |
| */ |
| public TypeBinding mostSpecificCommonType(TypeBinding[] types) { |
| int length = types.length; |
| int indexOfFirst = -1, actualLength = 0; |
| for (int i = 0; i < length; i++) { |
| TypeBinding type = types[i]; |
| if (type == null) continue; |
| if (type.isBaseType()) return null; |
| if (indexOfFirst < 0) indexOfFirst = i; |
| actualLength ++; |
| } |
| switch (actualLength) { |
| case 0: return VoidBinding; |
| case 1: return types[indexOfFirst]; |
| } |
| |
| // record all supertypes of type |
| // intersect with all supertypes of otherType |
| TypeBinding firstType = types[indexOfFirst]; |
| TypeBinding[] superTypes; |
| int superLength; |
| if (firstType.isBaseType()) { |
| return null; |
| } else if (firstType.isArrayType()) { |
| superLength = 4; |
| superTypes = new TypeBinding[] { |
| firstType, |
| getJavaIoSerializable(), |
| getJavaLangCloneable(), |
| getJavaLangObject(), |
| }; |
| } else { |
| ArrayList typesToVisit = new ArrayList(5); |
| typesToVisit.add(firstType); |
| ReferenceBinding currentType = (ReferenceBinding)firstType; |
| for (int i = 0, max = 1; i < max; i++) { |
| currentType = (ReferenceBinding) typesToVisit.get(i); |
| ReferenceBinding itsSuperclass = currentType.superclass(); |
| if (itsSuperclass != null && !typesToVisit.contains(itsSuperclass)) { |
| typesToVisit.add(itsSuperclass); |
| max++; |
| } |
| ReferenceBinding[] itsInterfaces = currentType.superInterfaces(); |
| for (int j = 0, count = itsInterfaces.length; j < count; j++) |
| if (!typesToVisit.contains(itsInterfaces[j])) { |
| typesToVisit.add(itsInterfaces[j]); |
| max++; |
| } |
| } |
| superLength = typesToVisit.size(); |
| superTypes = new TypeBinding[superLength]; |
| typesToVisit.toArray(superTypes); |
| } |
| int remaining = superLength; |
| nextOtherType: for (int i = indexOfFirst+1; i < length; i++) { |
| TypeBinding otherType = types[i]; |
| if (otherType == null) |
| continue nextOtherType; |
| else if (otherType.isArrayType()) { |
| nextSuperType: for (int j = 0; j < superLength; j++) { |
| TypeBinding superType = superTypes[j]; |
| if (superType == null || superType == otherType) continue nextSuperType; |
| switch (superType.id) { |
| case T_JavaIoSerializable : |
| case T_JavaLangCloneable : |
| case T_JavaLangObject : |
| continue nextSuperType; |
| } |
| superTypes[j] = null; |
| if (--remaining == 0) return null; |
| |
| } |
| continue nextOtherType; |
| } |
| ReferenceBinding otherRefType = (ReferenceBinding) otherType; |
| nextSuperType: for (int j = 0; j < superLength; j++) { |
| TypeBinding superType = superTypes[j]; |
| if (superType == null) continue nextSuperType; |
| if (otherRefType.isCompatibleWith(superType)) { |
| break nextSuperType; |
| } else { |
| superTypes[j] = null; |
| if (--remaining == 0) return null; |
| } |
| } |
| } |
| // per construction, first non-null supertype is most specific common supertype |
| for (int i = 0; i < superLength; i++) { |
| TypeBinding superType = superTypes[i]; |
| if (superType != null) return superType; |
| } |
| return null; |
| } |
| |
| // Internal use only |
| /* All methods in visible are acceptable matches for the method in question... |
| * Since the receiver type is an interface, we ignore the possibility that 2 inherited |
| * but unrelated superinterfaces may define the same method in acceptable but |
| * not identical ways... we just take the best match that we find since any class which |
| * implements the receiver interface MUST implement all signatures for the method... |
| * in which case the best match is correct. |
| * |
| * NOTE: This is different than javac... in the following example, the message send of |
| * bar(X) in class Y is supposed to be ambiguous. But any class which implements the |
| * interface I MUST implement both signatures for bar. If this class was the receiver of |
| * the message send instead of the interface I, then no problem would be reported. |
| * |
| interface I1 { |
| void bar(J j); |
| } |
| interface I2 { |
| // void bar(J j); |
| void bar(Object o); |
| } |
| interface I extends I1, I2 {} |
| interface J {} |
| |
| class X implements J {} |
| |
| class Y extends X { |
| public void foo(I i, X x) { i.bar(x); } |
| } |
| */ |
| protected final MethodBinding mostSpecificInterfaceMethodBinding(MethodBinding[] visible, int visibleSize, InvocationSite invocationSite) { |
| MethodBinding problemMethod = null; |
| nextVisible : for (int i = 0; i < visibleSize; i++) { |
| MethodBinding method = visible[i]; |
| for (int j = 0; j < visibleSize; j++) { |
| if (i == j) continue; |
| MethodBinding compatibleMethod = computeCompatibleMethod(visible[j], method.parameters, invocationSite); |
| if (compatibleMethod == null || !compatibleMethod.isValidBinding()) { |
| if (problemMethod == null) |
| problemMethod = compatibleMethod; |
| continue nextVisible; |
| } |
| } |
| compilationUnitScope().recordTypeReferences(method.thrownExceptions); |
| return method; |
| } |
| if (problemMethod == null) |
| return new ProblemMethodBinding(visible[0].selector, visible[0].parameters, Ambiguous); |
| return problemMethod; |
| } |
| |
| // Internal use only |
| /* All methods in visible are acceptable matches for the method in question... |
| * Since 1.4, the inherited ambiguous case has been removed from mostSpecificClassMethodBinding |
| */ |
| protected final MethodBinding mostSpecificMethodBinding(MethodBinding[] visible, int visibleSize, InvocationSite invocationSite) { |
| MethodBinding method = null; |
| nextVisible : for (int i = 0; i < visibleSize; i++) { |
| method = visible[i]; |
| for (int j = 0; j < visibleSize; j++) { |
| if (i == j) continue; |
| MethodBinding compatibleMethod = computeCompatibleMethod(visible[j], method.parameters, invocationSite); |
| if (compatibleMethod == null) |
| continue nextVisible; |
| } |
| compilationUnitScope().recordTypeReferences(method.thrownExceptions); |
| return method; |
| } |
| return new ProblemMethodBinding(visible[0].selector, visible[0].parameters, Ambiguous); |
| } |
| |
| public final ClassScope outerMostClassScope() { |
| ClassScope lastClassScope = null; |
| Scope scope = this; |
| do { |
| if (scope instanceof ClassScope) |
| lastClassScope = (ClassScope) scope; |
| scope = scope.parent; |
| } while (scope != null); |
| return lastClassScope; // may answer null if no class around |
| } |
| |
| public final MethodScope outerMostMethodScope() { |
| MethodScope lastMethodScope = null; |
| Scope scope = this; |
| do { |
| if (scope instanceof MethodScope) |
| lastMethodScope = (MethodScope) scope; |
| scope = scope.parent; |
| } while (scope != null); |
| return lastMethodScope; // may answer null if no method around |
| } |
| |
| public abstract ProblemReporter problemReporter(); |
| |
| public final CompilationUnitDeclaration referenceCompilationUnit() { |
| Scope scope, unitScope = this; |
| while ((scope = unitScope.parent) != null) |
| unitScope = scope; |
| return ((CompilationUnitScope) unitScope).referenceContext; |
| } |
| // start position in this scope - for ordering scopes vs. variables |
| int startIndex() { |
| return 0; |
| } |
| |
| /** |
| * Returns the immediately enclosing switchCase statement (carried by closest blockScope), |
| */ |
| public CaseStatement switchCase() { |
| Scope scope = this; |
| do { |
| if (scope instanceof BlockScope) |
| return ((BlockScope) scope).switchCase; |
| scope = scope.parent; |
| } while (scope != null); |
| return null; |
| } |
| /* |
| * Unboxing primitive |
| */ |
| public int unboxing(int id) { |
| switch (id) { |
| case T_JavaLangInteger : |
| return T_int; |
| case T_JavaLangByte : |
| return T_byte; |
| case T_JavaLangShort : |
| return T_short; |
| case T_JavaLangCharacter : |
| return T_char; |
| case T_JavaLangLong : |
| return T_long; |
| case T_JavaLangFloat : |
| return T_float; |
| case T_JavaLangDouble : |
| return T_double; |
| case T_JavaLangBoolean : |
| return T_boolean; |
| case T_JavaLangVoid : |
| return T_void; |
| } |
| return id; |
| } |
| /* |
| * Unboxing primitive |
| */ |
| public TypeBinding unboxing(TypeBinding type) { |
| switch (type.id) { |
| case T_JavaLangInteger : |
| return IntBinding; |
| case T_JavaLangByte : |
| return ByteBinding; |
| case T_JavaLangShort : |
| return ShortBinding; |
| case T_JavaLangCharacter : |
| return CharBinding; |
| case T_JavaLangLong : |
| return LongBinding; |
| case T_JavaLangFloat : |
| return FloatBinding; |
| case T_JavaLangDouble : |
| return DoubleBinding; |
| case T_JavaLangBoolean : |
| return BooleanBinding; |
| case T_JavaLangVoid : |
| return VoidBinding; |
| } |
| return type; |
| } |
| } |