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