blob: 1ad5e5a9c9970c29c8baa6908c1286cd19fb5571 [file] [log] [blame]
/*******************************************************************************
* 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, 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;
public final static int NOT_COMPATIBLE = -1;
public final static int COMPATIBLE = 0;
public final static int AUTOBOX_COMPATIBLE = 1;
public final static int VARARGS_COMPATIBLE = 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 TypeBinding boxing(TypeBinding type) {
if (type.isBaseType())
return environment().computeBoxingType(type);
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;
TypeVariableBinding[] typeVariables = method.typeVariables;
if (parameters == arguments
&& (method.returnType.tagBits & HasTypeVariable) == 0
&& genericTypeArguments == null
&& typeVariables == NoTypeVariables)
return method;
int argLength = arguments.length;
int paramLength = parameters.length;
boolean isVarArgs = method.isVarargs();
if (argLength != paramLength)
if (!isVarArgs || argLength < paramLength - 1)
return null; // incompatible
if (typeVariables != NoTypeVariables) { // generic method
TypeBinding[] newArgs = null;
for (int i = 0; i < argLength; i++) {
TypeBinding param = i < paramLength ? parameters[i] : parameters[paramLength - 1];
if (arguments[i].isBaseType() != param.isBaseType()) {
if (newArgs == null) {
newArgs = new TypeBinding[argLength];
System.arraycopy(arguments, 0, newArgs, 0, argLength);
}
newArgs[i] = environment().computeBoxingType(arguments[i]);
}
}
if (newArgs != null)
arguments = newArgs;
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 (!((ParameterizedGenericMethodBinding) method).wasInferred) {
// attempt to invoke generic method of raw type with type hints <String>foo()
return new ProblemMethodBinding(method, method.selector, genericTypeArguments, TypeArgumentsForRawGenericMethod);
}
} else {
return new ProblemMethodBinding(method, method.selector, genericTypeArguments, TypeParameterArityMismatch);
}
}
if (parameterCompatibilityLevel(method, arguments) > NOT_COMPATIBLE)
return method;
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, false/*no bound check*/)
: (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, false)
: (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;
}
if (superType.isParameterizedType()) {
ReferenceBinding match = typeVariable.superclass.findSuperTypeErasingTo((ReferenceBinding) superType.erasure());
boolean isCollision = match != null && match != superType;
for (int index = typeVariable.superInterfaces.length; !isCollision && --index >= 0;) {
ReferenceBinding temp = typeVariable.superInterfaces[index];
isCollision = superType != temp && superType.erasure() == temp.erasure();
}
if (isCollision) {
problemReporter().boundHasConflictingArguments(typeRef, superType);
typeVariable.tagBits |= HierarchyHasProblems;
noProblems = false;
continue nextVariable;
}
}
for (int index = typeVariable.superInterfaces.length; --index >= 0;) {
if (superType.erasure() == typeVariable.superInterfaces[index].erasure()) {
problemReporter().duplicateBounds(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) {
int dimension = type.dimensions();
TypeBinding originalType = type.leafComponentType();
if (originalType instanceof ReferenceBinding) {
ReferenceBinding convertedType = (ReferenceBinding) originalType;
ReferenceBinding originalEnclosing = originalType.enclosingType();
ReferenceBinding convertedEnclosing = originalEnclosing;
if (originalEnclosing != null && convertedType.isStatic() && originalEnclosing.isGenericType()) {
convertedEnclosing = (ReferenceBinding) convertToRawType(originalEnclosing);
}
if (originalType.isGenericType()) {
convertedType = environment().createRawType(convertedType, convertedEnclosing);
} else if (originalEnclosing != convertedEnclosing) {
convertedType = createParameterizedType(convertedType, null, convertedEnclosing);
}
if (originalType != convertedType) {
return dimension > 0 ? (TypeBinding)createArrayType(convertedType, dimension) : convertedType;
}
}
return type;
}
// TypeBinding leafType = type.leafComponentType();
// int dimension = type.dimensions();
// ReferenceBinding originalEnclosing = leafType.enclosingType();
// ReferenceBinding convertedEnclosing = originalEnclosing;
// if (originalEnclosing != null && ((ReferenceBinding)leafType).isStatic()) {
// convertedEnclosing = (ReferenceBinding) convertToRawType(originalEnclosing);
// }
// if (leafType.isGenericType()) {
// type = environment().createRawType((ReferenceBinding) leafType, convertedEnclosing);
// if (dimension > 0)
// type = createArrayType(type, dimension);
// } else if (originalEnclosing != convertedEnclosing) {
// type = createParameterizedType((ReferenceBinding)leafType, null, convertedEnclosing);
// if (dimension > 0)
// type = createArrayType(type, dimension);
// }
// 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 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();
}
CompilationUnitScope unitScope = compilationUnitScope();
int foundSize = found.size;
if (foundSize == startFoundSize) {
if (matchingMethod != null)
unitScope.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) {
unitScope.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
boolean isCompliant14 = unitScope.environment.options.complianceLevel >= ClassFileConstants.JDK1_4;
if (isCompliant14)
return mostSpecificMethodBinding(candidates, candidatesCount, argumentTypes, invocationSite);
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 unitScope = compilationUnitScope();
unitScope.recordReference(enclosingType, typeName);
ReferenceBinding memberType = enclosingType.getMemberType(typeName);
if (memberType != null) {
unitScope.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 unitScope = compilationUnitScope();
unitScope.recordTypeReferences(argumentTypes);
MethodBinding exactMethod = receiverType.getExactMethod(selector, argumentTypes, unitScope);
if (exactMethod != null) {
unitScope.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_JavaLangObject
&& 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;
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordTypeReference(receiverType);
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;
}
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 (invocationSite == null
? field.canBeSeenBy(getCurrentPackage())
: 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 == null) { // needed for statically imported types which don't know their hierarchy yet
((SourceTypeBinding) currentType).scope.connectTypeHierarchy();
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;
unitScope.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;
unitScope.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 unitScope = compilationUnitScope();
unitScope.recordReference(enclosingType, typeName);
ReferenceBinding memberType = enclosingType.getMemberType(typeName);
if (memberType != null) {
unitScope.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 == null) { // needed for statically imported types which don't know their hierarchy yet
((SourceTypeBinding) currentType).scope.connectTypeHierarchy();
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;
unitScope.recordReference(currentType, typeName);
if ((memberType = currentType.getMemberType(typeName)) != null) {
unitScope.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;
unitScope.recordReference(anInterface, typeName);
if ((memberType = anInterface.getMemberType(typeName)) != null) {
unitScope.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 unitScope = compilationUnitScope();
unitScope.recordTypeReferences(argumentTypes);
if (currentType.isInterface()) {
unitScope.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 = unitScope.environment.options.complianceLevel >= ClassFileConstants.JDK1_4;
// superclass lookup
ReferenceBinding classHierarchyStart = currentType;
while (currentType != null) {
unitScope.recordTypeReference(currentType);
MethodBinding[] currentMethods = currentType.getMethods(selector);
int currentLength = currentMethods.length;
if (isCompliant14 && matchingMethod != null || found.size > 0) {
nextMethod: for (int i = 0; i < currentLength; i++) {
MethodBinding currentMethod = currentMethods[i];
// if 1.4 compliant, must filter out redundant protected methods from superclasses
// 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;
// BUT we can also ignore any overridden method since we already know the better match (fixes 80028)
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;
unitScope.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;
}
if (found.size == 0) return null;
return (MethodBinding) found.elementAt(0); // no good match so just use the first one found
}
// check for duplicate parameterized methods
if (unitScope.environment.options.sourceLevel >= ClassFileConstants.JDK1_5) {
for (int i = 0; i < candidatesCount; i++) {
MethodBinding current = candidates[i];
if (current instanceof ParameterizedGenericMethodBinding)
current = ((ParameterizedGenericMethodBinding) current).originalMethod;
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) {
unitScope.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, argumentTypes, 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 static 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 & Binding.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);
// SourceTypeBinding initialType = this.enclosingSourceType();
// if ((fieldBinding == null || !fieldBinding.isValidBinding()) && enclosingType.hasMemberTypes()) { // check member enums
// ReferenceBinding[] memberTypes = enclosingType.memberTypes();
// for (int i = 0, length = memberTypes.length; i < length; i++) {
// ReferenceBinding memberType = memberTypes[i];
// if (memberType != initialType && memberType.isEnum()) { // do not find one's field through its enclosing members
// FieldBinding enumField = ((SourceTypeBinding)memberType).scope.findField(memberType, name, invocationSite, needResolve);
// if (enumField != null && (enumField.modifiers & AccEnum) != 0) {
// // grant access to enum constants of enclosing members
// // TODO (kent) need to revisit to see whether should walk sibling enums and issue an ambiguous match
// return enumField;
// }
// }
// }
// }
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;
}
if (environment().options.sourceLevel >= ClassFileConstants.JDK1_5) {
// at this point the scope is a compilation unit scope & need to check for imported static fields
CompilationUnitScope unitScope = (CompilationUnitScope) scope;
ImportBinding[] imports = unitScope.imports;
if (imports != null) {
// check single static imports
for (int i = 0, length = imports.length; i < length; i++) {
ImportBinding importBinding = imports[i];
if (importBinding.isStatic() && !importBinding.onDemand) {
if (CharOperation.equals(importBinding.compoundName[importBinding.compoundName.length - 1], name)) {
if (unitScope.resolveSingleImport(importBinding) != null && importBinding.resolvedImport instanceof FieldBinding) {
ImportReference importReference = importBinding.reference;
if (importReference != null) importReference.used = true;
return importBinding.resolvedImport; // already know its visible
}
}
}
}
// check on demand imports
boolean foundInImport = false;
for (int i = 0, length = imports.length; i < length; i++) {
ImportBinding importBinding = imports[i];
if (importBinding.isStatic() && importBinding.onDemand) {
Binding resolvedImport = importBinding.resolvedImport;
if (resolvedImport instanceof ReferenceBinding) {
FieldBinding temp = findField((ReferenceBinding) resolvedImport, name, invocationSite, needResolve);
if (temp != null) {
if (!temp.isValidBinding()) {
problemField = temp;
} else if (temp.isStatic()) {
ImportReference importReference = importBinding.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);
foundField = temp;
foundInImport = true;
}
}
}
}
}
if (foundField != null) return foundField;
}
}
}
// We did not find a local or instance variable.
if ((mask & Binding.TYPE) != 0) {
if ((binding = getBaseType(name)) != null)
return binding;
binding = getTypeOrPackage(name, (mask & Binding.PACKAGE) == 0 ? Binding.TYPE : Binding.TYPE | Binding.PACKAGE);
if (binding.isValidBinding() || mask == Binding.TYPE)
return binding;
// answer the problem type binding if we are only looking for a type
} else if ((mask & Binding.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 unitScope = compilationUnitScope();
unitScope.recordTypeReference(receiverType);
unitScope.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(TypeConstants.INIT);
if (methods == NoMethods)
return new ProblemMethodBinding(
TypeConstants.INIT,
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(TypeConstants.INIT, 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],
TypeConstants.INIT,
compatible[0].parameters,
NotVisible);
// all of visible are from the same declaringClass, even before 1.4 we can call this method instead of mostSpecificClassMethodBinding
return mostSpecificMethodBinding(visible, visibleIndex, argumentTypes, 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)
// compilationUnitScope().recordTypeReference(receiverType); not needed since receiver is the source type
MethodBinding methodBinding =
(foundMethod == null)
? classScope.findExactMethod(receiverType, selector, argumentTypes, invocationSite)
: classScope.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;
if (insideStaticContext && environment().options.sourceLevel >= ClassFileConstants.JDK1_5) {
// at this point the scope is a compilation unit scope & need to check for imported static methods
CompilationUnitScope unitScope = (CompilationUnitScope) scope;
ImportBinding[] imports = unitScope.imports;
if (imports != null) {
// check on demand imports
boolean foundInImport = false;
for (int i = 0, length = imports.length; i < length; i++) {
ImportBinding importBinding = imports[i];
if (importBinding.isStatic() && importBinding.onDemand) {
Binding resolvedImport = importBinding.resolvedImport;
if (resolvedImport instanceof ReferenceBinding) {
// answers closest approximation, may not check argumentTypes or visibility
MethodBinding temp = findMethod((ReferenceBinding) resolvedImport, selector, argumentTypes, invocationSite);
if (temp != null) {
if (!temp.isValidBinding()) {
if (foundMethod == null)
foundMethod = temp;
} else if (temp.isStatic()) {
MethodBinding compatibleMethod = computeCompatibleMethod(temp, argumentTypes, invocationSite);
if (compatibleMethod != null) {
if (compatibleMethod.isValidBinding()) {
if (compatibleMethod.canBeSeenBy(unitScope.fPackage)) {
ImportReference importReference = importBinding.reference;
if (importReference != null) importReference.used = true;
if (foundInImport)
// Answer error binding -- import on demand conflict; name found in two import on demand types.
return new ProblemMethodBinding(compatibleMethod, selector, compatibleMethod.parameters, Ambiguous);
foundMethod = compatibleMethod;
foundInImport = true;
} else if (foundMethod == null) {
foundMethod = new ProblemMethodBinding(compatibleMethod, selector, compatibleMethod.parameters, NotVisible);
}
}
} else if (foundMethod == null) {
foundMethod = new ProblemMethodBinding(temp, selector, argumentTypes, NotFound);
}
}
}
}
}
}
}
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 getJavaLangAnnotationAnnotation() {
compilationUnitScope().recordQualifiedReference(JAVA_LANG_ANNOTATION_ANNOTATION);
ReferenceBinding type = environment().getType(JAVA_LANG_ANNOTATION_ANNOTATION);
if (type != null) return type;
problemReporter().isClassPathCorrect(JAVA_LANG_ANNOTATION_ANNOTATION, 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 getJavaLangEnum() {
compilationUnitScope().recordQualifiedReference(JAVA_LANG_ENUM);
ReferenceBinding type = environment().getType(JAVA_LANG_ENUM);
if (type != null) return type;
problemReporter().isClassPathCorrect(JAVA_LANG_ENUM, 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.isBaseType())
return new ProblemMethodBinding(selector, argumentTypes, NotFound);
compilationUnitScope().recordTypeReference(receiverType);
if (receiverType.isArrayType())
return findMethodForArray((ArrayBinding) receiverType, selector, argumentTypes, invocationSite);
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], Binding.TYPE | Binding.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, Binding.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 unitScope = compilationUnitScope();
unitScope.recordQualifiedReference(compoundName);
Binding binding =
getTypeOrPackage(compoundName[0], typeNameLength == 1 ? Binding.TYPE : Binding.TYPE | Binding.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;
unitScope.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 & Binding.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;
// 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 a valid type was found, complain when another is found in an 'immediate' enclosing type (ie. not inherited)
if (foundType.isValidBinding() && 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 & Binding.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.resolveSingleImport(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)
: (someImport.isStatic()
? findMemberType(name, (ReferenceBinding) resolvedImport) // static imports are allowed to see inherited member types
: 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 & Binding.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], Binding.TYPE | Binding.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 qualifiedType = null;
ReferenceBinding typeBinding = (ReferenceBinding) binding;
if (typeBinding.isGenericType()) {
qualifiedType = this.environment().createRawType(typeBinding, qualifiedType);
} else {
qualifiedType = (qualifiedType != null && (qualifiedType.isRawType() || qualifiedType.isParameterizedType()))
? this.createParameterizedType(typeBinding, null, qualifiedType)
: typeBinding;
}
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);
if (typeBinding.isGenericType()) {
qualifiedType = this.environment().createRawType(typeBinding, qualifiedType);
} else {
qualifiedType = (qualifiedType != null && (qualifiedType.isRawType() || qualifiedType.isParameterizedType()))
? this.createParameterizedType(typeBinding, null, qualifiedType)
: typeBinding;
}
// checks visibility
if (!qualifiedType.isValidBinding())
return new ProblemReferenceBinding(
CharOperation.subarray(compoundName, 0, currentIndex),
qualifiedType.problemId());
}
return qualifiedType;
}
// 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;
}
public boolean isBoxingCompatibleWith(TypeBinding left, TypeBinding right) {
return left.isBaseType() != right.isBaseType() && environment().isBoxingCompatibleWith(left, right);
}
/* 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)
if (((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);
TypeVariableBinding[] invocationVariables = invocation.typeVariables();
if (invocation.isGenericType()) {
for (int j = 0; j < argLength; j++) {
TypeBinding bestArgument = leastContainingTypeArgument(bestArguments[j], invocationVariables[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.erasure(), 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) {
if (types.length == 1) {
TypeBinding type = types[0];
return type == null ? VoidBinding : type;
}
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;
if (!visible[j].areParametersCompatibleWith(method.parameters))
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 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;
if (!visible[j].areParametersCompatibleWith(method.parameters))
continue nextVisible;
}
compilationUnitScope().recordTypeReferences(method.thrownExceptions);
return method;
}
if (problemMethod == null)
return new ProblemMethodBinding(visible[0].selector, visible[0].parameters, Ambiguous);
return problemMethod;
}
protected final MethodBinding mostSpecificMethodBinding(MethodBinding[] visible, int visibleSize, TypeBinding[] argumentTypes, InvocationSite invocationSite) {
int[] compatibilityLevels = new int[visibleSize];
for (int i = 0; i < visibleSize; i++)
compatibilityLevels[i] = parameterCompatibilityLevel(visible[i], argumentTypes);
for (int level = 0; level <= VARARGS_COMPATIBLE; level++) {
nextVisible : for (int i = 0; i < visibleSize; i++) {
if (compatibilityLevels[i] != level) continue nextVisible; // skip this method for now
MethodBinding method = visible[i];
for (int j = 0; j < visibleSize; j++) {
if (i == j || compatibilityLevels[j] != level) continue;
// tiebreak generic methods using variant where type params are substituted by their erasures
if (!visible[j].tiebreakMethod().areParametersCompatibleWith(method.tiebreakMethod().parameters)) {
if (method.isVarargs() && visible[j].isVarargs()) {
int paramLength = method.parameters.length;
if (paramLength == visible[j].parameters.length && paramLength == argumentTypes.length + 1) {
TypeBinding elementsType = ((ArrayBinding) visible[j].parameters[paramLength - 1]).elementsType();
if (method.parameters[paramLength - 1].isCompatibleWith(elementsType))
continue; // special case to choose between 2 varargs methods when the last arg is missing
}
}
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
}
protected int parameterCompatibilityLevel(MethodBinding method, TypeBinding[] arguments) {
TypeBinding[] parameters = method.parameters;
int paramLength = parameters.length;
int argLength = arguments.length;
LookupEnvironment env = environment();
if (env.options.sourceLevel < ClassFileConstants.JDK1_5) {
if (paramLength != argLength)
return NOT_COMPATIBLE;
for (int i = 0; i < argLength; i++) {
TypeBinding param = parameters[i];
TypeBinding arg = arguments[i];
if (arg != param && !arg.isCompatibleWith(param))
return NOT_COMPATIBLE;
}
return COMPATIBLE;
}
int level = COMPATIBLE; // no autoboxing or varargs support needed
int lastIndex = argLength;
if (method.isVarargs()) {
lastIndex = paramLength - 1;
if (paramLength == argLength) { // accept X or X[] but not X[][]
TypeBinding param = parameters[lastIndex]; // is an ArrayBinding by definition
TypeBinding arg = arguments[lastIndex];
if (param != arg) {
level = parameterCompatibilityLevel(arg, param, env);
if (level == NOT_COMPATIBLE) {
// expect X[], is it called with X
param = ((ArrayBinding) param).elementsType();
if (parameterCompatibilityLevel(arg, param, env) == NOT_COMPATIBLE)
return NOT_COMPATIBLE;
level = VARARGS_COMPATIBLE; // varargs support needed
}
}
} else {
if (paramLength < argLength) { // all remainig argument types must be compatible with the elementsType of varArgType
TypeBinding param = ((ArrayBinding) parameters[lastIndex]).elementsType();
for (int i = lastIndex; i < argLength; i++) {
TypeBinding arg = arguments[i];
if (param != arg && parameterCompatibilityLevel(arg, param, env) == NOT_COMPATIBLE)
return NOT_COMPATIBLE;
}
} else if (lastIndex != argLength) { // can call foo(int i, X ... x) with foo(1) but NOT foo();
return NOT_COMPATIBLE;
}
level = VARARGS_COMPATIBLE; // varargs support needed
}
// now compare standard arguments from 0 to lastIndex
}
for (int i = 0; i < lastIndex; i++) {
TypeBinding param = parameters[i];
TypeBinding arg = arguments[i];
if (arg != param) {
int newLevel = parameterCompatibilityLevel(arg, param, env);
if (newLevel == NOT_COMPATIBLE)
return NOT_COMPATIBLE;
if (newLevel > level)
level = newLevel;
}
}
return level;
}
private int parameterCompatibilityLevel(TypeBinding arg, TypeBinding param, LookupEnvironment env) {
// only called if env.options.sourceLevel >= ClassFileConstants.JDK1_5
if (arg.isCompatibleWith(param))
return COMPATIBLE;
if (arg.isBaseType() != param.isBaseType()) {
TypeBinding convertedType = env.computeBoxingType(arg);
if (convertedType == param || convertedType.isCompatibleWith(param))
return AUTOBOX_COMPATIBLE;
}
return NOT_COMPATIBLE;
}
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;
}
}