Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*******************************************************************************
* Copyright (c) 2000, 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
import java.util.*;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.*;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
import org.eclipse.jdt.internal.compiler.util.HashtableOfObject;
import org.eclipse.jdt.internal.compiler.util.ObjectVector;
import org.eclipse.jdt.internal.compiler.util.SimpleSet;
public abstract class Scope {
/* Scope kinds */
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;
/* Argument Compatibilities */
public final static int NOT_COMPATIBLE = -1;
public final static int COMPATIBLE = 0;
public final static int AUTOBOX_COMPATIBLE = 1;
public final static int VARARGS_COMPATIBLE = 2;
/* Type Compatibilities */
public static final int EQUAL_OR_MORE_SPECIFIC = -1;
public static final int NOT_RELATED = 0;
public static final int MORE_GENERIC = 1;
public int kind;
public Scope parent;
protected Scope(int kind, Scope parent) {
this.kind = kind;
this.parent = parent;
}
/* Answer an int describing the relationship between the given types.
*
* NOT_RELATED
* EQUAL_OR_MORE_SPECIFIC : left is compatible with right
* MORE_GENERIC : right is compatible with left
*/
public static int compareTypes(TypeBinding left, TypeBinding right) {
if (left.isCompatibleWith(right))
return Scope.EQUAL_OR_MORE_SPECIFIC;
if (right.isCompatibleWith(left))
return Scope.MORE_GENERIC;
return Scope.NOT_RELATED;
}
/**
* Returns a type where either all variables or specific ones got discarded.
* e.g. List (discarding ) will return: List extends Enum>>
*/
public static TypeBinding convertEliminatingTypeVariables(TypeBinding originalType, ReferenceBinding genericType, int rank, Set eliminatedVariables) {
if ((originalType.tagBits & TagBits.HasTypeVariable) != 0) {
switch (originalType.kind()) {
case Binding.ARRAY_TYPE :
ArrayBinding originalArrayType = (ArrayBinding) originalType;
TypeBinding originalLeafComponentType = originalArrayType.leafComponentType;
TypeBinding substitute = convertEliminatingTypeVariables(originalLeafComponentType, genericType, rank, eliminatedVariables); // substitute could itself be array type
if (substitute != originalLeafComponentType) {
return originalArrayType.environment.createArrayType(substitute.leafComponentType(), substitute.dimensions() + originalArrayType.dimensions());
}
break;
case Binding.PARAMETERIZED_TYPE :
ParameterizedTypeBinding paramType = (ParameterizedTypeBinding) originalType;
ReferenceBinding originalEnclosing = paramType.enclosingType();
ReferenceBinding substitutedEnclosing = originalEnclosing;
if (originalEnclosing != null) {
substitutedEnclosing = (ReferenceBinding) convertEliminatingTypeVariables(originalEnclosing, genericType, rank, eliminatedVariables);
}
TypeBinding[] originalArguments = paramType.arguments;
TypeBinding[] substitutedArguments = originalArguments;
for (int i = 0, length = originalArguments == null ? 0 : originalArguments.length; i < length; i++) {
TypeBinding originalArgument = originalArguments[i];
TypeBinding substitutedArgument = convertEliminatingTypeVariables(originalArgument, paramType.genericType(), i, eliminatedVariables);
if (substitutedArgument != originalArgument) {
if (substitutedArguments == originalArguments) {
System.arraycopy(originalArguments, 0, substitutedArguments = new TypeBinding[length], 0, i);
}
substitutedArguments[i] = substitutedArgument;
} else if (substitutedArguments != originalArguments) {
substitutedArguments[i] = originalArgument;
}
}
if (originalEnclosing != substitutedEnclosing || originalArguments != substitutedArguments) {
return paramType.environment.createParameterizedType(paramType.genericType(), substitutedArguments, substitutedEnclosing);
}
break;
case Binding.TYPE_PARAMETER :
if (genericType == null) {
break;
}
TypeVariableBinding originalVariable = (TypeVariableBinding) originalType;
if (eliminatedVariables != null && eliminatedVariables.contains(originalType)) {
return originalVariable.environment.createWildcard(genericType, rank, null, null, Wildcard.UNBOUND);
}
TypeBinding originalUpperBound = originalVariable.upperBound();
if (eliminatedVariables == null) {
eliminatedVariables = new HashSet(2);
}
eliminatedVariables.add(originalVariable);
TypeBinding substitutedUpperBound = convertEliminatingTypeVariables(originalUpperBound, genericType, rank, eliminatedVariables);
eliminatedVariables.remove(originalVariable);
return originalVariable.environment.createWildcard(genericType, rank, substitutedUpperBound, null, Wildcard.EXTENDS);
case Binding.RAW_TYPE :
break;
case Binding.GENERIC_TYPE :
ReferenceBinding currentType = (ReferenceBinding) originalType;
originalEnclosing = currentType.enclosingType();
substitutedEnclosing = originalEnclosing;
if (originalEnclosing != null) {
substitutedEnclosing = (ReferenceBinding) convertEliminatingTypeVariables(originalEnclosing, genericType, rank, eliminatedVariables);
}
originalArguments = currentType.typeVariables();
substitutedArguments = originalArguments;
for (int i = 0, length = originalArguments == null ? 0 : originalArguments.length; i < length; i++) {
TypeBinding originalArgument = originalArguments[i];
TypeBinding substitutedArgument = convertEliminatingTypeVariables(originalArgument, currentType, i, eliminatedVariables);
if (substitutedArgument != originalArgument) {
if (substitutedArguments == originalArguments) {
System.arraycopy(originalArguments, 0, substitutedArguments = new TypeBinding[length], 0, i);
}
substitutedArguments[i] = substitutedArgument;
} else if (substitutedArguments != originalArguments) {
substitutedArguments[i] = originalArgument;
}
}
if (originalEnclosing != substitutedEnclosing || originalArguments != substitutedArguments) {
return ((TypeVariableBinding)originalArguments[0]).environment.createParameterizedType(genericType, substitutedArguments, substitutedEnclosing);
}
break;
case Binding.WILDCARD_TYPE :
WildcardBinding wildcard = (WildcardBinding) originalType;
TypeBinding originalBound = wildcard.bound;
TypeBinding substitutedBound = originalBound;
if (originalBound != null) {
substitutedBound = convertEliminatingTypeVariables(originalBound, genericType, rank, eliminatedVariables);
if (substitutedBound != originalBound) {
return wildcard.environment.createWildcard(wildcard.genericType, wildcard.rank, substitutedBound, null, wildcard.boundKind);
}
}
break;
case Binding.INTERSECTION_TYPE :
WildcardBinding intersection = (WildcardBinding) originalType;
originalBound = intersection.bound;
substitutedBound = originalBound;
if (originalBound != null) {
substitutedBound = convertEliminatingTypeVariables(originalBound, genericType, rank, eliminatedVariables);
}
TypeBinding[] originalOtherBounds = intersection.otherBounds;
TypeBinding[] substitutedOtherBounds = originalOtherBounds;
for (int i = 0, length = originalOtherBounds == null ? 0 : originalOtherBounds.length; i < length; i++) {
TypeBinding originalOtherBound = originalOtherBounds[i];
TypeBinding substitutedOtherBound = convertEliminatingTypeVariables(originalOtherBound, genericType, rank, eliminatedVariables);
if (substitutedOtherBound != originalOtherBound) {
if (substitutedOtherBounds == originalOtherBounds) {
System.arraycopy(originalOtherBounds, 0, substitutedOtherBounds = new TypeBinding[length], 0, i);
}
substitutedOtherBounds[i] = substitutedOtherBound;
} else if (substitutedOtherBounds != originalOtherBounds) {
substitutedOtherBounds[i] = originalOtherBound;
}
}
if (substitutedBound != originalBound || substitutedOtherBounds != originalOtherBounds) {
return intersection.environment.createWildcard(intersection.genericType, intersection.rank, substitutedBound, substitutedOtherBounds, intersection.boundKind);
}
break;
}
}
return originalType;
}
public static TypeBinding getBaseType(char[] name) {
// list should be optimized (with most often used first)
int length = name.length;
if (length > 2 && length < 8) {
switch (name[0]) {
case 'i' :
if (length == 3 && name[1] == 'n' && name[2] == 't')
return TypeBinding.INT;
break;
case 'v' :
if (length == 4 && name[1] == 'o' && name[2] == 'i' && name[3] == 'd')
return TypeBinding.VOID;
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 TypeBinding.BOOLEAN;
if (length == 4 && name[1] == 'y' && name[2] == 't' && name[3] == 'e')
return TypeBinding.BYTE;
break;
case 'c' :
if (length == 4 && name[1] == 'h' && name[2] == 'a' && name[3] == 'r')
return TypeBinding.CHAR;
break;
case 'd' :
if (length == 6
&& name[1] == 'o'
&& name[2] == 'u'
&& name[3] == 'b'
&& name[4] == 'l'
&& name[5] == 'e')
return TypeBinding.DOUBLE;
break;
case 'f' :
if (length == 5
&& name[1] == 'l'
&& name[2] == 'o'
&& name[3] == 'a'
&& name[4] == 't')
return TypeBinding.FLOAT;
break;
case 'l' :
if (length == 4 && name[1] == 'o' && name[2] == 'n' && name[3] == 'g')
return TypeBinding.LONG;
break;
case 's' :
if (length == 5
&& name[1] == 'h'
&& name[2] == 'o'
&& name[3] == 'r'
&& name[4] == 't')
return TypeBinding.SHORT;
}
}
return null;
}
// 5.1.10
public static ReferenceBinding[] greaterLowerBound(ReferenceBinding[] types) {
if (types == null) return null;
int length = types.length;
if (length == 0) return null;
ReferenceBinding[] result = types;
int removed = 0;
for (int i = 0; i < length; i++) {
ReferenceBinding iType = result[i];
if (iType == null) continue;
for (int j = 0; j < length; j++) {
if (i == j) continue;
ReferenceBinding 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 ReferenceBinding[length], 0, length);
}
result[j] = null;
removed ++;
}
}
}
if (removed == 0) return result;
if (length == removed) return null;
ReferenceBinding[] trimmedResult = new ReferenceBinding[length - removed];
for (int i = 0, index = 0; i < length; i++) {
ReferenceBinding iType = result[i];
if (iType != null) {
trimmedResult[index++] = iType;
}
}
return trimmedResult;
}
// 5.1.10
public static TypeBinding[] greaterLowerBound(TypeBinding[] types) {
if (types == null) return null;
int length = types.length;
if (length == 0) return null;
TypeBinding[] result = types;
int removed = 0;
for (int i = 0; i < length; i++) {
TypeBinding iType = result[i];
if (iType == null) continue;
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;
if (length == removed) return null;
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;
}
/**
* 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) {
if (originalTypes == null) return null;
ReferenceBinding[] substitutedTypes = originalTypes;
for (int i = 0, length = originalTypes.length; i < length; i++) {
ReferenceBinding originalType = originalTypes[i];
TypeBinding substitutedType = substitute(substitution, originalType);
if (!(substitutedType instanceof ReferenceBinding)) {
return null; // impossible substitution
}
if (substitutedType != originalType) {
if (substitutedTypes == originalTypes) {
System.arraycopy(originalTypes, 0, substitutedTypes = new ReferenceBinding[length], 0, i);
}
substitutedTypes[i] = (ReferenceBinding)substitutedType;
} else if (substitutedTypes != originalTypes) {
substitutedTypes[i] = originalType;
}
}
return substitutedTypes;
}
/**
* Returns a type, where original type was substituted using the receiver
* parameterized type.
* In raw mode, all parameterized type denoting same original type are converted
* to raw types. e.g.
* class X {
* X foo;
* X bar;
* } when used in raw fashion, then type of both foo and bar is raw type X.
*
*/
public static TypeBinding substitute(Substitution substitution, TypeBinding originalType) {
if (originalType == null) return null;
switch (originalType.kind()) {
case Binding.TYPE_PARAMETER:
return substitution.substitute((TypeVariableBinding) originalType);
case Binding.PARAMETERIZED_TYPE:
ParameterizedTypeBinding originalParameterizedType = (ParameterizedTypeBinding) originalType;
ReferenceBinding originalEnclosing = originalType.enclosingType();
ReferenceBinding substitutedEnclosing = originalEnclosing;
if (originalEnclosing != null) {
substitutedEnclosing = (ReferenceBinding) substitute(substitution, originalEnclosing);
}
TypeBinding[] originalArguments = originalParameterizedType.arguments;
TypeBinding[] substitutedArguments = originalArguments;
if (originalArguments != null) {
if (substitution.isRawSubstitution()) {
return originalParameterizedType.environment.createRawType(originalParameterizedType.genericType(), substitutedEnclosing);
}
substitutedArguments = substitute(substitution, originalArguments);
}
if (substitutedArguments != originalArguments || substitutedEnclosing != originalEnclosing) {
return originalParameterizedType.environment.createParameterizedType(
originalParameterizedType.genericType(), substitutedArguments, substitutedEnclosing);
}
break;
case Binding.ARRAY_TYPE:
ArrayBinding originalArrayType = (ArrayBinding) originalType;
TypeBinding originalLeafComponentType = originalArrayType.leafComponentType;
TypeBinding substitute = substitute(substitution, originalLeafComponentType); // substitute could itself be array type
if (substitute != originalLeafComponentType) {
return originalArrayType.environment.createArrayType(substitute.leafComponentType(), substitute.dimensions() + originalType.dimensions());
}
break;
case Binding.WILDCARD_TYPE:
case Binding.INTERSECTION_TYPE:
WildcardBinding wildcard = (WildcardBinding) originalType;
if (wildcard.boundKind != Wildcard.UNBOUND) {
TypeBinding originalBound = wildcard.bound;
TypeBinding substitutedBound = substitute(substitution, originalBound);
TypeBinding[] originalOtherBounds = wildcard.otherBounds;
TypeBinding[] substitutedOtherBounds = substitute(substitution, originalOtherBounds);
if (substitutedBound != originalBound || originalOtherBounds != substitutedOtherBounds) {
return wildcard.environment.createWildcard(wildcard.genericType, wildcard.rank, substitutedBound, substitutedOtherBounds, wildcard.boundKind);
}
}
break;
case Binding.TYPE:
if (!originalType.isMemberType()) break;
ReferenceBinding originalReferenceType = (ReferenceBinding) originalType;
originalEnclosing = originalType.enclosingType();
substitutedEnclosing = originalEnclosing;
if (originalEnclosing != null) {
substitutedEnclosing = (ReferenceBinding) substitute(substitution, originalEnclosing);
}
// treat as if parameterized with its type variables (non generic type gets 'null' arguments)
if (substitutedEnclosing != originalEnclosing) {
return substitution.isRawSubstitution()
? substitution.environment().createRawType(originalReferenceType, substitutedEnclosing)
: substitution.environment().createParameterizedType(originalReferenceType, null, substitutedEnclosing);
}
break;
case Binding.GENERIC_TYPE:
originalReferenceType = (ReferenceBinding) originalType;
originalEnclosing = originalType.enclosingType();
substitutedEnclosing = originalEnclosing;
if (originalEnclosing != null) {
substitutedEnclosing = (ReferenceBinding) substitute(substitution, originalEnclosing);
}
if (substitution.isRawSubstitution()) {
return substitution.environment().createRawType(originalReferenceType, substitutedEnclosing);
}
// treat as if parameterized with its type variables (non generic type gets 'null' arguments)
originalArguments = originalReferenceType.typeVariables();
substitutedArguments = substitute(substitution, originalArguments);
return substitution.environment().createParameterizedType(originalReferenceType, substitutedArguments, substitutedEnclosing);
}
return originalType;
}
/**
* 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) {
if (originalTypes == null) return null;
TypeBinding[] substitutedTypes = originalTypes;
for (int i = 0, length = originalTypes.length; i < length; i++) {
TypeBinding originalType = originalTypes[i];
TypeBinding substitutedParameter = substitute(substitution, 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;
}
/*
* Boxing primitive
*/
public TypeBinding boxing(TypeBinding type) {
if (type.isBaseType())
return environment().computeBoxingType(type);
return type;
}
public final ClassScope classScope() {
Scope scope = this;
do {
if (scope instanceof ClassScope)
return (ClassScope) scope;
scope = scope.parent;
} while (scope != null);
return null;
}
public final CompilationUnitScope compilationUnitScope() {
Scope lastScope = null;
Scope scope = this;
do {
lastScope = scope;
scope = scope.parent;
} while (scope != null);
return (CompilationUnitScope) lastScope;
}
/**
* Finds the most specific compiler options
*/
public final CompilerOptions compilerOptions() {
return compilationUnitScope().environment.globalOptions;
}
/**
* Internal use only
* Given a method, returns null if arguments cannot be converted to parameters.
* Will answer a subsituted method in case the method was generic and type inference got triggered;
* in case the method was originally compatible, then simply answer it back.
*/
protected final MethodBinding computeCompatibleMethod(MethodBinding method, TypeBinding[] arguments, InvocationSite invocationSite) {
TypeBinding[] genericTypeArguments = invocationSite.genericTypeArguments();
TypeBinding[] parameters = method.parameters;
TypeVariableBinding[] typeVariables = method.typeVariables;
if (parameters == arguments
&& (method.returnType.tagBits & TagBits.HasTypeVariable) == 0
&& genericTypeArguments == null
&& typeVariables == Binding.NO_TYPE_VARIABLES)
return method;
int argLength = arguments.length;
int paramLength = parameters.length;
boolean isVarArgs = method.isVarargs();
if (argLength != paramLength)
if (!isVarArgs || argLength < paramLength - 1)
return null; // incompatible
if (typeVariables != Binding.NO_TYPE_VARIABLES) { // generic method
TypeBinding[] newArgs = null;
for (int i = 0; i < argLength; i++) {
TypeBinding param = i < paramLength ? parameters[i] : parameters[paramLength - 1];
if (arguments[i].isBaseType() != param.isBaseType()) {
if (newArgs == null) {
newArgs = new TypeBinding[argLength];
System.arraycopy(arguments, 0, newArgs, 0, argLength);
}
newArgs[i] = environment().computeBoxingType(arguments[i]);
}
}
if (newArgs != null)
arguments = newArgs;
method = ParameterizedGenericMethodBinding.computeCompatibleMethod(method, arguments, this, invocationSite);
if (method == null) return null; // incompatible
if (!method.isValidBinding()) return method; // bound check issue is taking precedence
} else if (genericTypeArguments != null && compilerOptions().complianceLevel < ClassFileConstants.JDK1_7) {
if (method instanceof ParameterizedGenericMethodBinding) {
if (!((ParameterizedGenericMethodBinding) method).wasInferred)
// attempt to invoke generic method of raw type with type hints foo()
return new ProblemMethodBinding(method, method.selector, genericTypeArguments, ProblemReasons.TypeArgumentsForRawGenericMethod);
} else if (!method.isOverriding() || !isOverriddenMethodGeneric(method)) {
return new ProblemMethodBinding(method, method.selector, genericTypeArguments, ProblemReasons.TypeParameterArityMismatch);
}
}
if (parameterCompatibilityLevel(method, arguments) > NOT_COMPATIBLE)
return method;
if (genericTypeArguments != null)
return new ProblemMethodBinding(method, method.selector, arguments, ProblemReasons.ParameterizedMethodTypeMismatch);
return null; // incompatible
}
/**
* Connect type variable supertypes, and returns true if no problem was detected
* @param typeParameters
* @param checkForErasedCandidateCollisions
*/
protected boolean connectTypeVariables(TypeParameter[] typeParameters, boolean checkForErasedCandidateCollisions) {
if (typeParameters == null || compilerOptions().sourceLevel < ClassFileConstants.JDK1_5) return true;
Map invocations = new HashMap(2);
boolean noProblems = true;
// preinitializing each type variable
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 = Binding.NO_SUPERINTERFACES;
// set firstBound to the binding of the first explicit bound in parameter declaration
typeVariable.firstBound = null; // first bound used to compute erasure
}
nextVariable: for (int i = 0, paramLength = typeParameters.length; i < paramLength; i++) {
TypeParameter typeParameter = typeParameters[i];
TypeVariableBinding typeVariable = typeParameter.binding;
TypeReference typeRef = typeParameter.type;
if (typeRef == null)
continue nextVariable;
boolean isFirstBoundTypeVariable = false;
TypeBinding superType = this.kind == METHOD_SCOPE
? typeRef.resolveType((BlockScope)this, false/*no bound check*/)
: typeRef.resolveType((ClassScope)this);
if (superType == null) {
typeVariable.tagBits |= TagBits.HierarchyHasProblems;
} else {
typeRef.resolvedType = superType; // hold onto the problem type
firstBound: {
switch (superType.kind()) {
case Binding.ARRAY_TYPE :
problemReporter().boundCannotBeArray(typeRef, superType);
typeVariable.tagBits |= TagBits.HierarchyHasProblems;
break firstBound; // do not keep first bound
case Binding.TYPE_PARAMETER :
isFirstBoundTypeVariable = true;
TypeVariableBinding varSuperType = (TypeVariableBinding) superType;
if (varSuperType.rank >= typeVariable.rank && varSuperType.declaringElement == typeVariable.declaringElement) {
if (compilerOptions().complianceLevel <= ClassFileConstants.JDK1_6) {
problemReporter().forwardTypeVariableReference(typeParameter, varSuperType);
typeVariable.tagBits |= TagBits.HierarchyHasProblems;
break firstBound; // do not keep first bound
}
}
break;
default :
if (((ReferenceBinding) superType).isFinal()) {
problemReporter().finalVariableBound(typeVariable, typeRef);
}
break;
}
ReferenceBinding superRefType = (ReferenceBinding) superType;
if (!superType.isInterface()) {
typeVariable.superclass = superRefType;
} else {
typeVariable.superInterfaces = new ReferenceBinding[] {superRefType};
}
typeVariable.tagBits |= superType.tagBits & TagBits.ContainsNestedTypeReferences;
typeVariable.firstBound = superRefType; // first bound used to compute erasure
}
}
TypeReference[] boundRefs = typeParameter.bounds;
if (boundRefs != null) {
nextBound: for (int j = 0, boundLength = boundRefs.length; j < boundLength; j++) {
typeRef = boundRefs[j];
superType = this.kind == METHOD_SCOPE
? typeRef.resolveType((BlockScope)this, false)
: typeRef.resolveType((ClassScope)this);
if (superType == null) {
typeVariable.tagBits |= TagBits.HierarchyHasProblems;
continue nextBound;
} else {
typeVariable.tagBits |= superType.tagBits & TagBits.ContainsNestedTypeReferences;
boolean didAlreadyComplain = !typeRef.resolvedType.isValidBinding();
if (isFirstBoundTypeVariable && j == 0) {
problemReporter().noAdditionalBoundAfterTypeVariable(typeRef);
typeVariable.tagBits |= TagBits.HierarchyHasProblems;
didAlreadyComplain = true;
//continue nextBound; - keep these bounds to minimize secondary errors
} else if (superType.isArrayType()) {
if (!didAlreadyComplain) {
problemReporter().boundCannotBeArray(typeRef, superType);
typeVariable.tagBits |= TagBits.HierarchyHasProblems;
}
continue nextBound;
} else {
if (!superType.isInterface()) {
if (!didAlreadyComplain) {
problemReporter().boundMustBeAnInterface(typeRef, superType);
typeVariable.tagBits |= TagBits.HierarchyHasProblems;
}
continue nextBound;
}
}
// check against superclass
if (checkForErasedCandidateCollisions && typeVariable.firstBound == typeVariable.superclass) {
if (hasErasedCandidatesCollisions(superType, typeVariable.superclass, invocations, typeVariable, typeRef)) {
continue nextBound;
}
}
// check against superinterfaces
ReferenceBinding superRefType = (ReferenceBinding) superType;
for (int index = typeVariable.superInterfaces.length; --index >= 0;) {
ReferenceBinding previousInterface = typeVariable.superInterfaces[index];
if (previousInterface == superRefType) {
problemReporter().duplicateBounds(typeRef, superType);
typeVariable.tagBits |= TagBits.HierarchyHasProblems;
continue nextBound;
}
if (checkForErasedCandidateCollisions) {
if (hasErasedCandidatesCollisions(superType, previousInterface, invocations, typeVariable, typeRef)) {
continue nextBound;
}
}
}
int size = typeVariable.superInterfaces.length;
System.arraycopy(typeVariable.superInterfaces, 0, typeVariable.superInterfaces = new ReferenceBinding[size + 1], 0, size);
typeVariable.superInterfaces[size] = superRefType;
}
}
}
noProblems &= (typeVariable.tagBits & TagBits.HierarchyHasProblems) == 0;
}
return noProblems;
}
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 TypeVariableBinding[] createTypeVariables(TypeParameter[] typeParameters, Binding declaringElement) {
// do not construct type variables if source < 1.5
if (typeParameters == null || compilerOptions().sourceLevel < ClassFileConstants.JDK1_5)
return Binding.NO_TYPE_VARIABLES;
PackageBinding unitPackage = compilationUnitScope().fPackage;
int length = typeParameters.length;
TypeVariableBinding[] typeVariableBindings = new TypeVariableBinding[length];
int count = 0;
for (int i = 0; i < length; i++) {
TypeParameter typeParameter = typeParameters[i];
TypeVariableBinding parameterBinding = new TypeVariableBinding(typeParameter.name, declaringElement, i, environment());
parameterBinding.fPackage = unitPackage;
typeParameter.binding = parameterBinding;
// detect duplicates, but keep each variable to reduce secondary errors with instantiating this generic type (assume number of variables is correct)
for (int j = 0; j < count; j++) {
TypeVariableBinding knownVar = typeVariableBindings[j];
if (CharOperation.equals(knownVar.sourceName, typeParameter.name))
problemReporter().duplicateTypeParameterInType(typeParameter);
}
typeVariableBindings[count++] = parameterBinding;
// TODO should offer warnings to inform about hiding declaring, enclosing or member types
// ReferenceBinding type = sourceType;
// // check that the member does not conflict with an enclosing type
// do {
// if (CharOperation.equals(type.sourceName, memberContext.name)) {
// problemReporter().hidingEnclosingType(memberContext);
// continue nextParameter;
// }
// type = type.enclosingType();
// } while (type != null);
// // check that the member type does not conflict with another sibling member type
// for (int j = 0; j < i; j++) {
// if (CharOperation.equals(referenceContext.memberTypes[j].name, memberContext.name)) {
// problemReporter().duplicateNestedType(memberContext);
// continue nextParameter;
// }
// }
}
if (count != length)
System.arraycopy(typeVariableBindings, 0, typeVariableBindings = new TypeVariableBinding[count], 0, count);
return typeVariableBindings;
}
public final ClassScope enclosingClassScope() {
Scope scope = this;
while ((scope = scope.parent) != null) {
if (scope instanceof ClassScope) return (ClassScope) scope;
}
return null; // may answer null if no type around
}
public final MethodScope enclosingMethodScope() {
Scope scope = this;
while ((scope = scope.parent) != null) {
if (scope instanceof MethodScope) return (MethodScope) scope;
}
return null; // may answer null if no method around
}
/* Answer the scope receiver type (could be parameterized)
*/
public final ReferenceBinding enclosingReceiverType() {
Scope scope = this;
do {
if (scope instanceof ClassScope) {
return environment().convertToParameterizedType(((ClassScope) scope).referenceContext.binding);
}
scope = scope.parent;
} while (scope != null);
return null;
}
/**
* Returns the immediately enclosing reference context, starting from current scope parent.
* If starting on a class, it will skip current class. If starting on unitScope, returns null.
*/
public ReferenceContext enclosingReferenceContext() {
Scope current = this;
while ((current = current.parent) != null) {
switch(current.kind) {
case METHOD_SCOPE :
return ((MethodScope) current).referenceContext;
case CLASS_SCOPE :
return ((ClassScope) current).referenceContext;
case COMPILATION_UNIT_SCOPE :
return ((CompilationUnitScope) current).referenceContext;
}
}
return null;
}
/* Answer the scope enclosing source type (could be generic)
*/
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)
protected MethodBinding findDefaultAbstractMethod(
ReferenceBinding receiverType,
char[] selector,
TypeBinding[] argumentTypes,
InvocationSite invocationSite,
ReferenceBinding classHierarchyStart,
ObjectVector found,
MethodBinding concreteMatch) {
int startFoundSize = found.size;
ReferenceBinding currentType = classHierarchyStart;
while (currentType != null) {
findMethodInSuperInterfaces(currentType, selector, found, invocationSite);
currentType = currentType.superclass();
}
MethodBinding[] candidates = null;
int candidatesCount = 0;
MethodBinding problemMethod = null;
int foundSize = found.size;
if (foundSize > startFoundSize) {
// 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()) {
if (concreteMatch != null && concreteMatch.declaringClass.findSuperTypeOriginatingFrom(compatibleMethod.declaringClass) != null)
if (environment().methodVerifier().isParameterSubsignature(concreteMatch.original(), compatibleMethod.original()))
continue; // can skip this method since concreteMatch overrides it
if (candidatesCount == 0) {
candidates = new MethodBinding[foundSize - startFoundSize + 1];
if (concreteMatch != null)
candidates[candidatesCount++] = concreteMatch;
}
candidates[candidatesCount++] = compatibleMethod;
} else if (problemMethod == null) {
problemMethod = compatibleMethod;
}
}
}
}
if (candidatesCount < 2) {
if (concreteMatch == null) {
if (candidatesCount == 0)
return problemMethod; // can be null
concreteMatch = candidates[0];
}
compilationUnitScope().recordTypeReferences(concreteMatch.thrownExceptions);
return concreteMatch;
}
// no need to check for visibility - interface methods are public
if (compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4)
return mostSpecificMethodBinding(candidates, candidatesCount, argumentTypes, invocationSite, receiverType);
return mostSpecificInterfaceMethodBinding(candidates, candidatesCount, invocationSite);
}
// Internal use only
public ReferenceBinding findDirectMemberType(char[] typeName, ReferenceBinding enclosingType) {
if ((enclosingType.tagBits & TagBits.HasNoMemberTypes) != 0)
return null; // know it has no member types (nor inherited member types)
ReferenceBinding enclosingReceiverType = enclosingReceiverType();
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordReference(enclosingType, typeName);
ReferenceBinding memberType = enclosingType.getMemberType(typeName);
if (memberType != null) {
unitScope.recordTypeReference(memberType);
if (enclosingReceiverType == null
? memberType.canBeSeenBy(getCurrentPackage())
: memberType.canBeSeenBy(enclosingType, enclosingReceiverType))
return memberType;
return new ProblemReferenceBinding(new char[][]{typeName}, memberType, ProblemReasons.NotVisible);
}
return null;
}
// Internal use only
public MethodBinding findExactMethod(ReferenceBinding receiverType, char[] selector, TypeBinding[] argumentTypes, InvocationSite invocationSite) {
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordTypeReferences(argumentTypes);
MethodBinding exactMethod = receiverType.getExactMethod(selector, argumentTypes, unitScope);
if (exactMethod != null && exactMethod.typeVariables == Binding.NO_TYPE_VARIABLES && !exactMethod.isBridge()) {
// in >= 1.5 mode, ensure the exactMatch did not match raw types
if (compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5)
for (int i = argumentTypes.length; --i >= 0;)
if (isPossibleSubtypeOfRawType(argumentTypes[i]))
return null;
// must find both methods for this case: void foo() {} and N foo() { return null; }
// or find an inherited method when the exact match is to a bridge method
unitScope.recordTypeReferences(exactMethod.thrownExceptions);
if (exactMethod.isAbstract() && exactMethod.thrownExceptions != Binding.NO_EXCEPTIONS)
return null; // may need to merge exceptions with interface method
// special treatment for Object.getClass() in 1.5 mode (substitute parameterized return type)
if (receiverType.isInterface() || exactMethod.canBeSeenBy(receiverType, invocationSite, this)) {
if (argumentTypes == Binding.NO_PARAMETERS
&& CharOperation.equals(selector, TypeConstants.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 (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) {
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordTypeReference(receiverType);
checkArrayField: {
TypeBinding leafType;
switch (receiverType.kind()) {
case Binding.BASE_TYPE :
return null;
case Binding.WILDCARD_TYPE :
case Binding.INTERSECTION_TYPE:
case Binding.TYPE_PARAMETER : // capture
TypeBinding receiverErasure = receiverType.erasure();
if (!receiverErasure.isArrayType())
break checkArrayField;
leafType = receiverErasure.leafComponentType();
break;
case Binding.ARRAY_TYPE :
leafType = receiverType.leafComponentType();
break;
default:
break checkArrayField;
}
if (leafType instanceof ReferenceBinding)
if (!((ReferenceBinding) leafType).canBeSeenBy(this))
return new ProblemFieldBinding((ReferenceBinding)leafType, fieldName, ProblemReasons.ReceiverTypeNotVisible);
if (CharOperation.equals(fieldName, TypeConstants.LENGTH)) {
if ((leafType.tagBits & TagBits.HasMissingType) != 0) {
return new ProblemFieldBinding(ArrayBinding.ArrayLength, null, fieldName, ProblemReasons.NotFound);
}
return ArrayBinding.ArrayLength;
}
return null;
}
ReferenceBinding currentType = (ReferenceBinding) receiverType;
if (!currentType.canBeSeenBy(this))
return new ProblemFieldBinding(currentType, fieldName, ProblemReasons.ReceiverTypeNotVisible);
currentType.initializeForStaticImports();
FieldBinding field = currentType.getField(fieldName, needResolve);
if (field != null) {
if (invocationSite == null
? field.canBeSeenBy(getCurrentPackage())
: field.canBeSeenBy(currentType, invocationSite, this))
return field;
return new ProblemFieldBinding(field /* closest match*/, field.declaringClass, fieldName, ProblemReasons.NotVisible);
}
// collect all superinterfaces of receiverType until the field is found in a supertype
ReferenceBinding[] interfacesToVisit = null;
int nextPosition = 0;
FieldBinding visibleField = null;
boolean keepLooking = true;
FieldBinding notVisibleField = null;
// we could hold onto the not visible field for extra error reporting
while (keepLooking) {
ReferenceBinding[] itsInterfaces = currentType.superInterfaces();
if (itsInterfaces != null && itsInterfaces != Binding.NO_SUPERINTERFACES) {
if (interfacesToVisit == null) {
interfacesToVisit = itsInterfaces;
nextPosition = interfacesToVisit.length;
} else {
int itsLength = itsInterfaces.length;
if (nextPosition + itsLength >= interfacesToVisit.length)
System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition);
nextInterface : for (int a = 0; a < itsLength; a++) {
ReferenceBinding next = itsInterfaces[a];
for (int b = 0; b < nextPosition; b++)
if (next == interfacesToVisit[b]) continue nextInterface;
interfacesToVisit[nextPosition++] = next;
}
}
}
if ((currentType = currentType.superclass()) == null)
break;
unitScope.recordTypeReference(currentType);
currentType.initializeForStaticImports();
currentType = (ReferenceBinding) currentType.capture(this, invocationSite == null ? 0 : invocationSite.sourceEnd());
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, ProblemReasons.Ambiguous);
} else {
if (notVisibleField == null)
notVisibleField = field;
}
}
}
// walk all visible interfaces to find ambiguous references
if (interfacesToVisit != null) {
ProblemFieldBinding ambiguous = null;
done : for (int i = 0; i < nextPosition; i++) {
ReferenceBinding anInterface = interfacesToVisit[i];
unitScope.recordTypeReference(anInterface);
// no need to capture rcv interface, since member field is going to be static anyway
if ((field = anInterface.getField(fieldName, true /*resolve*/)) != null) {
if (visibleField == null) {
visibleField = field;
} else {
ambiguous = new ProblemFieldBinding(visibleField /* closest match*/, visibleField.declaringClass, fieldName, ProblemReasons.Ambiguous);
break done;
}
} else {
ReferenceBinding[] itsInterfaces = anInterface.superInterfaces();
if (itsInterfaces != null && itsInterfaces != Binding.NO_SUPERINTERFACES) {
int itsLength = itsInterfaces.length;
if (nextPosition + itsLength >= interfacesToVisit.length)
System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition);
nextInterface : for (int a = 0; a < itsLength; a++) {
ReferenceBinding next = itsInterfaces[a];
for (int b = 0; b < nextPosition; b++)
if (next == interfacesToVisit[b]) continue nextInterface;
interfacesToVisit[nextPosition++] = next;
}
}
}
}
if (ambiguous != null)
return ambiguous;
}
if (visibleField != null)
return visibleField;
if (notVisibleField != null) {
return new ProblemFieldBinding(notVisibleField, currentType, fieldName, ProblemReasons.NotVisible);
}
return null;
}
// Internal use only
public ReferenceBinding findMemberType(char[] typeName, ReferenceBinding enclosingType) {
if ((enclosingType.tagBits & TagBits.HasNoMemberTypes) != 0)
return null; // know it has no member types (nor inherited member types)
ReferenceBinding enclosingSourceType = enclosingSourceType();
PackageBinding currentPackage = getCurrentPackage();
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordReference(enclosingType, typeName);
ReferenceBinding memberType = enclosingType.getMemberType(typeName);
if (memberType != null) {
unitScope.recordTypeReference(memberType);
if (enclosingSourceType == null || (this.parent == unitScope && (enclosingSourceType.tagBits & TagBits.TypeVariablesAreConnected) == 0)
? memberType.canBeSeenBy(currentPackage)
: memberType.canBeSeenBy(enclosingType, enclosingSourceType))
return memberType;
return new ProblemReferenceBinding(new char[][]{typeName}, memberType, ProblemReasons.NotVisible);
}
// collect all superinterfaces of receiverType until the memberType is found in a supertype
ReferenceBinding currentType = enclosingType;
ReferenceBinding[] interfacesToVisit = null;
int nextPosition = 0;
ReferenceBinding visibleMemberType = null;
boolean keepLooking = true;
ReferenceBinding notVisible = null;
// we could hold onto the not visible field for extra error reporting
while (keepLooking) {
ReferenceBinding[] itsInterfaces = currentType.superInterfaces();
if (itsInterfaces == null) { // needed for statically imported types which don't know their hierarchy yet
ReferenceBinding sourceType = currentType.isParameterizedType()
? ((ParameterizedTypeBinding) currentType).genericType()
: currentType;
if (sourceType.isHierarchyBeingConnected())
return null; // looking for an undefined member type in its own superclass ref
((SourceTypeBinding) sourceType).scope.connectTypeHierarchy();
itsInterfaces = currentType.superInterfaces();
}
if (itsInterfaces != null && itsInterfaces != Binding.NO_SUPERINTERFACES) {
if (interfacesToVisit == null) {
interfacesToVisit = itsInterfaces;
nextPosition = interfacesToVisit.length;
} else {
int itsLength = itsInterfaces.length;
if (nextPosition + itsLength >= interfacesToVisit.length)
System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition);
nextInterface : for (int a = 0; a < itsLength; a++) {
ReferenceBinding next = itsInterfaces[a];
for (int b = 0; b < nextPosition; b++)
if (next == interfacesToVisit[b]) continue nextInterface;
interfacesToVisit[nextPosition++] = next;
}
}
}
if ((currentType = currentType.superclass()) == null)
break;
unitScope.recordReference(currentType, typeName);
if ((memberType = currentType.getMemberType(typeName)) != null) {
unitScope.recordTypeReference(memberType);
keepLooking = false;
if (enclosingSourceType == null
? memberType.canBeSeenBy(currentPackage)
: memberType.canBeSeenBy(enclosingType, enclosingSourceType)) {
if (visibleMemberType == null)
visibleMemberType = memberType;
else
return new ProblemReferenceBinding(new char[][]{typeName}, visibleMemberType, ProblemReasons.Ambiguous);
} else {
notVisible = memberType;
}
}
}
// walk all visible interfaces to find ambiguous references
if (interfacesToVisit != null) {
ProblemReferenceBinding ambiguous = null;
done : for (int i = 0; i < nextPosition; i++) {
ReferenceBinding anInterface = interfacesToVisit[i];
unitScope.recordReference(anInterface, typeName);
if ((memberType = anInterface.getMemberType(typeName)) != null) {
unitScope.recordTypeReference(memberType);
if (visibleMemberType == null) {
visibleMemberType = memberType;
} else {
ambiguous = new ProblemReferenceBinding(new char[][]{typeName}, visibleMemberType, ProblemReasons.Ambiguous);
break done;
}
} else {
ReferenceBinding[] itsInterfaces = anInterface.superInterfaces();
if (itsInterfaces != null && itsInterfaces != Binding.NO_SUPERINTERFACES) {
int itsLength = itsInterfaces.length;
if (nextPosition + itsLength >= interfacesToVisit.length)
System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition);
nextInterface : for (int a = 0; a < itsLength; a++) {
ReferenceBinding next = itsInterfaces[a];
for (int b = 0; b < nextPosition; b++)
if (next == interfacesToVisit[b]) continue nextInterface;
interfacesToVisit[nextPosition++] = next;
}
}
}
}
if (ambiguous != null)
return ambiguous;
}
if (visibleMemberType != null)
return visibleMemberType;
if (notVisible != null)
return new ProblemReferenceBinding(new char[][]{typeName}, notVisible, ProblemReasons.NotVisible);
return null;
}
// Internal use only - use findMethod()
public MethodBinding findMethod(ReferenceBinding receiverType, char[] selector, TypeBinding[] argumentTypes, InvocationSite invocationSite) {
return findMethod(receiverType, selector, argumentTypes, invocationSite, false);
}
// Internal use only - use findMethod()
public MethodBinding findMethod(ReferenceBinding receiverType, char[] selector, TypeBinding[] argumentTypes, InvocationSite invocationSite, boolean inStaticContext) {
ReferenceBinding currentType = receiverType;
boolean receiverTypeIsInterface = receiverType.isInterface();
ObjectVector found = new ObjectVector(3);
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordTypeReferences(argumentTypes);
if (receiverTypeIsInterface) {
unitScope.recordTypeReference(receiverType);
MethodBinding[] receiverMethods = receiverType.getMethods(selector, argumentTypes.length);
if (receiverMethods.length > 0)
found.addAll(receiverMethods);
findMethodInSuperInterfaces(receiverType, selector, found, invocationSite);
currentType = getJavaLangObject();
}
// superclass lookup
long complianceLevel = compilerOptions().complianceLevel;
boolean isCompliant14 = complianceLevel >= ClassFileConstants.JDK1_4;
boolean isCompliant15 = complianceLevel >= ClassFileConstants.JDK1_5;
ReferenceBinding classHierarchyStart = currentType;
MethodVerifier verifier = environment().methodVerifier();
while (currentType != null) {
unitScope.recordTypeReference(currentType);
currentType = (ReferenceBinding) currentType.capture(this, invocationSite == null ? 0 : invocationSite.sourceEnd());
MethodBinding[] currentMethods = currentType.getMethods(selector, argumentTypes.length);
int currentLength = currentMethods.length;
if (currentLength > 0) {
if (isCompliant14 && (receiverTypeIsInterface || found.size > 0)) {
nextMethod: for (int i = 0, l = currentLength; i < l; i++) { // currentLength can be modified inside the loop
MethodBinding currentMethod = currentMethods[i];
if (currentMethod == null) continue nextMethod;
if (receiverTypeIsInterface && !currentMethod.isPublic()) { // only public methods from Object are visible to interface receiverTypes
currentLength--;
currentMethods[i] = null;
continue nextMethod;
}
// if 1.4 compliant, must filter out redundant protected methods from superclasses
// protected method need to be checked only - default access is already dealt with in #canBeSeen implementation
// when checking that p.C -> q.B -> p.A cannot see default access members from A through B.
// if ((currentMethod.modifiers & AccProtected) == 0) continue nextMethod;
// BUT we can also ignore any overridden method since we already know the better match (fixes 80028)
for (int j = 0, max = found.size; j < max; j++) {
MethodBinding matchingMethod = (MethodBinding) found.elementAt(j);
if (verifier.isParameterSubsignature(matchingMethod.original(), currentMethod.original())) {
if (isCompliant15) {
if (matchingMethod.isBridge() && !currentMethod.isBridge())
continue nextMethod; // keep inherited methods to find concrete method over a bridge method
}
currentLength--;
currentMethods[i] = null;
continue nextMethod;
}
}
}
}
if (currentLength > 0) {
// append currentMethods, filtering out null entries
if (currentMethods.length == currentLength) {
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;
MethodBinding problemMethod = null;
boolean searchForDefaultAbstractMethod = isCompliant14 && ! receiverTypeIsInterface && (receiverType.isAbstract() || receiverType.isTypeVariable());
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()) {
if (foundSize == 1 && compatibleMethod.canBeSeenBy(receiverType, invocationSite, this)) {
// return the single visible match now
if (searchForDefaultAbstractMethod)
return findDefaultAbstractMethod(receiverType, selector, argumentTypes, invocationSite, classHierarchyStart, found, compatibleMethod);
unitScope.recordTypeReferences(compatibleMethod.thrownExceptions);
return compatibleMethod;
}
if (candidatesCount == 0)
candidates = new MethodBinding[foundSize];
candidates[candidatesCount++] = compatibleMethod;
} else if (problemMethod == null) {
problemMethod = compatibleMethod;
}
}
}
}
// no match was found
if (candidatesCount == 0) {
if (problemMethod != null) {
switch (problemMethod.problemId()) {
case ProblemReasons.TypeArgumentsForRawGenericMethod :
case ProblemReasons.TypeParameterArityMismatch :
return problemMethod;
}
}
// abstract classes may get a match in interfaces; for non abstract
// classes, reduces secondary errors since missing interface method
// error is already reported
MethodBinding interfaceMethod =
findDefaultAbstractMethod(receiverType, selector, argumentTypes, invocationSite, classHierarchyStart, found, null);
if (interfaceMethod != null) return interfaceMethod;
if (found.size == 0) return null;
if (problemMethod != null) return problemMethod;
// still no match; try to find a close match when the parameter
// order is wrong or missing some parameters
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=69471
// bad guesses are foo(), when argument types have been supplied
// and foo(X, Y), when the argument types are (int, float, Y)
// so answer the method with the most argType matches and least parameter type mismatches
int bestArgMatches = -1;
MethodBinding bestGuess = (MethodBinding) found.elementAt(0); // if no good match so just use the first one found
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;
int argMatches = 0;
next: for (int a = 0; a < argLength; a++) {
TypeBinding arg = argumentTypes[a];
for (int p = a == 0 ? 0 : a - 1; p < paramLength && p < a + 1; p++) { // look one slot before & after to see if the type matches
if (params[p] == arg) {
argMatches++;
continue next;
}
}
}
if (argMatches < bestArgMatches)
continue nextMethod;
if (argMatches == bestArgMatches) {
int diff1 = paramLength < argLength ? 2 * (argLength - paramLength) : paramLength - argLength;
int bestLength = bestGuess.parameters.length;
int diff2 = bestLength < argLength ? 2 * (argLength - bestLength) : bestLength - argLength;
if (diff1 >= diff2)
continue nextMethod;
}
bestArgMatches = argMatches;
bestGuess = methodBinding;
}
return new ProblemMethodBinding(bestGuess, bestGuess.selector, argumentTypes, ProblemReasons.NotFound);
}
// tiebreak using visibility check
int visiblesCount = 0;
if (receiverTypeIsInterface) {
if (candidatesCount == 1) {
unitScope.recordTypeReferences(candidates[0].thrownExceptions);
return candidates[0];
}
visiblesCount = candidatesCount;
} else {
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++;
}
}
switch (visiblesCount) {
case 0 :
MethodBinding interfaceMethod =
findDefaultAbstractMethod(receiverType, selector, argumentTypes, invocationSite, classHierarchyStart, found, null);
if (interfaceMethod != null) return interfaceMethod;
return new ProblemMethodBinding(candidates[0], candidates[0].selector, candidates[0].parameters, ProblemReasons.NotVisible);
case 1 :
if (searchForDefaultAbstractMethod)
return findDefaultAbstractMethod(receiverType, selector, argumentTypes, invocationSite, classHierarchyStart, found, candidates[0]);
unitScope.recordTypeReferences(candidates[0].thrownExceptions);
return candidates[0];
default :
break;
}
}
if (complianceLevel <= ClassFileConstants.JDK1_3) {
ReferenceBinding declaringClass = candidates[0].declaringClass;
return !declaringClass.isInterface()
? mostSpecificClassMethodBinding(candidates, visiblesCount, invocationSite)
: mostSpecificInterfaceMethodBinding(candidates, visiblesCount, invocationSite);
}
// check for duplicate parameterized methods
if (compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) {
for (int i = 0; i < visiblesCount; i++) {
MethodBinding candidate = candidates[i];
if (candidate instanceof ParameterizedGenericMethodBinding)
candidate = ((ParameterizedGenericMethodBinding) candidate).originalMethod;
if (candidate instanceof ParameterizedMethodBinding)
for (int j = i + 1; j < visiblesCount; j++) {
MethodBinding otherCandidate = candidates[j];
if (otherCandidate == candidate
|| (candidate.declaringClass == otherCandidate.declaringClass && candidate.areParametersEqual(otherCandidate))) {
return new ProblemMethodBinding(candidates[i], candidates[i].selector, candidates[i].parameters, ProblemReasons.Ambiguous);
}
}
}
}
if (inStaticContext) {
MethodBinding[] staticCandidates = new MethodBinding[visiblesCount];
int staticCount = 0;
for (int i = 0; i < visiblesCount; i++)
if (candidates[i].isStatic())
staticCandidates[staticCount++] = candidates[i];
if (staticCount == 1)
return staticCandidates[0];
if (staticCount > 1)
return mostSpecificMethodBinding(staticCandidates, staticCount, argumentTypes, invocationSite, receiverType);
}
MethodBinding mostSpecificMethod = mostSpecificMethodBinding(candidates, visiblesCount, argumentTypes, invocationSite, receiverType);
if (searchForDefaultAbstractMethod) { // search interfaces for a better match
if (mostSpecificMethod.isValidBinding())
// see if there is a better match in the interfaces - see AutoBoxingTest 99, LookupTest#81
return findDefaultAbstractMethod(receiverType, selector, argumentTypes, invocationSite, classHierarchyStart, found, mostSpecificMethod);
// see if there is a match in the interfaces - see LookupTest#84
MethodBinding interfaceMethod = findDefaultAbstractMethod(receiverType, selector, argumentTypes, invocationSite, classHierarchyStart, found, null);
if (interfaceMethod != null && interfaceMethod.isValidBinding() /* else return the same error as before */)
return interfaceMethod;
}
return mostSpecificMethod;
}
// 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, Binding.NO_PARAMETERS, (ReferenceBinding)leafType, ProblemReasons.ReceiverTypeNotVisible);
}
ReferenceBinding object = getJavaLangObject();
MethodBinding methodBinding = object.getExactMethod(selector, argumentTypes, null);
if (methodBinding != null) {
// handle the method clone() specially... cannot be protected or throw exceptions
if (argumentTypes == Binding.NO_PARAMETERS) {
switch (selector[0]) {
case 'c':
if (CharOperation.equals(selector, TypeConstants.CLONE)) {
return environment().computeArrayClone(methodBinding);
}
break;
case 'g':
if (CharOperation.equals(selector, TypeConstants.GETCLASS) && methodBinding.returnType.isParameterizedType()/*1.5*/) {
return ParameterizedMethodBinding.instantiateGetClass(receiverType, methodBinding, this);
}
break;
}
}
if (methodBinding.canBeSeenBy(receiverType, invocationSite, this))
return methodBinding;
}
methodBinding = findMethod(object, selector, argumentTypes, invocationSite);
if (methodBinding == null)
return new ProblemMethodBinding(selector, argumentTypes, ProblemReasons.NotFound);
return methodBinding;
}
protected void findMethodInSuperInterfaces(ReferenceBinding currentType, char[] selector, ObjectVector found, InvocationSite invocationSite) {
ReferenceBinding[] itsInterfaces = currentType.superInterfaces();
if (itsInterfaces != null && itsInterfaces != Binding.NO_SUPERINTERFACES) {
ReferenceBinding[] interfacesToVisit = itsInterfaces;
int nextPosition = interfacesToVisit.length;
for (int i = 0; i < nextPosition; i++) {
currentType = interfacesToVisit[i];
compilationUnitScope().recordTypeReference(currentType);
currentType = (ReferenceBinding) currentType.capture(this, invocationSite == null ? 0 : invocationSite.sourceEnd());
MethodBinding[] currentMethods = currentType.getMethods(selector);
if (currentMethods.length > 0) {
int foundSize = found.size;
if (foundSize > 0) {
// its possible to walk the same superinterface from different classes in the hierarchy
next : for (int c = 0, l = currentMethods.length; c < l; c++) {
MethodBinding current = currentMethods[c];
for (int f = 0; f < foundSize; f++)
if (current == found.elementAt(f)) continue next;
found.add(current);
}
} else {
found.addAll(currentMethods);
}
}
if ((itsInterfaces = currentType.superInterfaces()) != null && itsInterfaces != Binding.NO_SUPERINTERFACES) {
int itsLength = itsInterfaces.length;
if (nextPosition + itsLength >= interfacesToVisit.length)
System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition);
nextInterface : for (int a = 0; a < itsLength; a++) {
ReferenceBinding next = itsInterfaces[a];
for (int b = 0; b < nextPosition; b++)
if (next == interfacesToVisit[b]) continue nextInterface;
interfacesToVisit[nextPosition++] = next;
}
}
}
}
}
// 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(new char[][]{typeName}, typeBinding, ProblemReasons.NotVisible);
}
return typeBinding;
}
public LocalVariableBinding findVariable(char[] variable) {
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) {
CompilationUnitScope unitScope = compilationUnitScope();
LookupEnvironment env = unitScope.environment;
try {
env.missingClassFileLocation = invocationSite;
Binding binding = null;
FieldBinding problemField = null;
if ((mask & Binding.VARIABLE) != 0) {
boolean insideStaticContext = false;
boolean insideConstructorCall = false;
boolean insideTypeAnnotation = 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;
insideTypeAnnotation = methodScope.insideTypeAnnotation;
//$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,
ProblemReasons.InheritedNameHidesEnclosingName);
if (depth > 0)
invocationSite.setDepth(depth);
return variableBinding;
}
break;
case CLASS_SCOPE :
ClassScope classScope = (ClassScope) scope;
ReferenceBinding receiverType = classScope.enclosingReceiverType();
if (!insideTypeAnnotation) {
FieldBinding fieldBinding = classScope.findField(receiverType, 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() == ProblemReasons.Ambiguous) {
if (foundField == null || foundField.problemId() == ProblemReasons.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,
ProblemReasons.InheritedNameHidesEnclosingName);
}
ProblemFieldBinding insideProblem = null;
if (fieldBinding.isValidBinding()) {
if (!fieldBinding.isStatic()) {
if (insideConstructorCall) {
insideProblem =
new ProblemFieldBinding(
fieldBinding, // closest match
fieldBinding.declaringClass,
name,
ProblemReasons.NonStaticReferenceInConstructorInvocation);
} else if (insideStaticContext) {
insideProblem =
new ProblemFieldBinding(
fieldBinding, // closest match
fieldBinding.declaringClass,
name,
ProblemReasons.NonStaticReferenceInStaticContext);
}
}
if (receiverType == fieldBinding.declaringClass || compilerOptions().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(receiverType);
}
// 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,
ProblemReasons.InheritedNameHidesEnclosingName);
}
}
if (foundField == null || (foundField.problemId() == ProblemReasons.NotVisible && fieldBinding.problemId() != ProblemReasons.NotVisible)) {
// only remember the fieldBinding if its the first one found or the previous one was not visible & fieldBinding is...
foundDepth = depth;
foundActualReceiverType = receiverType;
foundInsideProblem = insideProblem;
foundField = fieldBinding;
}
}
}
insideTypeAnnotation = false;
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 (foundInsideProblem != null)
return foundInsideProblem;
if (foundField != null) {
if (foundField.isValidBinding()) {
if (foundDepth > 0) {
invocationSite.setDepth(foundDepth);
invocationSite.setActualReceiverType(foundActualReceiverType);
}
return foundField;
}
problemField = foundField;
foundField = null;
}
if (compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) {
// at this point the scope is a compilation unit scope & need to check for imported static fields
unitScope.faultInImports(); // ensure static imports are resolved
ImportBinding[] imports = unitScope.imports;
if (imports != null) {
// check single static imports
for (int i = 0, length = imports.length; i < length; i++) {
ImportBinding importBinding = imports[i];
if (importBinding.isStatic() && !importBinding.onDemand) {
if (CharOperation.equals(importBinding.compoundName[importBinding.compoundName.length - 1], name)) {
if (unitScope.resolveSingleImport(importBinding, Binding.TYPE | Binding.FIELD | Binding.METHOD) != null && importBinding.resolvedImport instanceof FieldBinding) {
foundField = (FieldBinding) importBinding.resolvedImport;
ImportReference importReference = importBinding.reference;
if (importReference != null && needResolve) {
importReference.bits |= ASTNode.Used;
}
invocationSite.setActualReceiverType(foundField.declaringClass);
if (foundField.isValidBinding()) {
return foundField;
}
if (problemField == null)
problemField = foundField;
}
}
}
}
// check on demand imports
boolean foundInImport = false;
for (int i = 0, length = imports.length; i < length; i++) {
ImportBinding importBinding = imports[i];
if (importBinding.isStatic() && importBinding.onDemand) {
Binding resolvedImport = importBinding.resolvedImport;
if (resolvedImport instanceof ReferenceBinding) {
FieldBinding temp = findField((ReferenceBinding) resolvedImport, name, invocationSite, needResolve);
if (temp != null) {
if (!temp.isValidBinding()) {
if (problemField == null)
problemField = temp;
} else if (temp.isStatic()) {
if (foundField == temp) continue;
ImportReference importReference = importBinding.reference;
if (importReference != null && needResolve) {
importReference.bits |= ASTNode.Used;
}
if (foundInImport)
// Answer error binding -- import on demand conflict; name found in two import on demand packages.
return new ProblemFieldBinding(
foundField, // closest match
foundField.declaringClass,
name,
ProblemReasons.Ambiguous);
foundField = temp;
foundInImport = true;
}
}
}
}
}
if (foundField != null) {
invocationSite.setActualReceiverType(foundField.declaringClass);
return foundField;
}
}
}
}
// We did not find a local or instance variable.
if ((mask & Binding.TYPE) != 0) {
if ((binding = getBaseType(name)) != null)
return binding;
binding = getTypeOrPackage(name, (mask & Binding.PACKAGE) == 0 ? Binding.TYPE : Binding.TYPE | Binding.PACKAGE, needResolve);
if (binding.isValidBinding() || mask == Binding.TYPE)
return binding;
// answer the problem type binding if we are only looking for a type
} else if ((mask & Binding.PACKAGE) != 0) {
unitScope.recordSimpleReference(name);
if ((binding = env.getTopLevelPackage(name)) != null)
return binding;
}
if (problemField != null) return problemField;
if (binding != null && binding.problemId() != ProblemReasons.NotFound)
return binding; // answer the better problem binding
return new ProblemBinding(name, enclosingSourceType(), ProblemReasons.NotFound);
} catch (AbortCompilation e) {
e.updateContext(invocationSite, referenceCompilationUnit().compilationResult);
throw e;
} finally {
env.missingClassFileLocation = null;
}
}
public MethodBinding getConstructor(ReferenceBinding receiverType, TypeBinding[] argumentTypes, InvocationSite invocationSite) {
CompilationUnitScope unitScope = compilationUnitScope();
LookupEnvironment env = unitScope.environment;
try {
env.missingClassFileLocation = invocationSite;
unitScope.recordTypeReference(receiverType);
unitScope.recordTypeReferences(argumentTypes);
MethodBinding methodBinding = receiverType.getExactConstructor(argumentTypes);
if (methodBinding != null && methodBinding.canBeSeenBy(invocationSite, this)) {
// targeting a non generic constructor with type arguments ?
if (invocationSite.genericTypeArguments() != null)
methodBinding = computeCompatibleMethod(methodBinding, argumentTypes, invocationSite);
return methodBinding;
}
MethodBinding[] methods = receiverType.getMethods(TypeConstants.INIT, argumentTypes.length);
if (methods == Binding.NO_METHODS)
return new ProblemMethodBinding(
TypeConstants.INIT,
argumentTypes,
ProblemReasons.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(methods[0], TypeConstants.INIT, argumentTypes, ProblemReasons.NotFound);
return problemMethod;
}
// need a more descriptive error... cannot convert from X to Y
MethodBinding[] visible = new MethodBinding[compatibleIndex];
int visibleIndex = 0;
for (int i = 0; i < compatibleIndex; i++) {
MethodBinding method = compatible[i];
if (method.canBeSeenBy(invocationSite, this))
visible[visibleIndex++] = method;
}
if (visibleIndex == 1) return visible[0];
if (visibleIndex == 0)
return new ProblemMethodBinding(
compatible[0],
TypeConstants.INIT,
compatible[0].parameters,
ProblemReasons.NotVisible);
// all of visible are from the same declaringClass, even before 1.4 we can call this method instead of mostSpecificClassMethodBinding
return mostSpecificMethodBinding(visible, visibleIndex, argumentTypes, invocationSite, receiverType);
} catch (AbortCompilation e) {
e.updateContext(invocationSite, referenceCompilationUnit().compilationResult);
throw e;
} finally {
env.missingClassFileLocation = null;
}
}
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) {
LookupEnvironment env = environment();
try {
env.missingClassFileLocation = invocationSite;
FieldBinding field = findField(receiverType, fieldName, invocationSite, true /*resolve*/);
if (field != null) return field;
return new ProblemFieldBinding(
receiverType instanceof ReferenceBinding ? (ReferenceBinding) receiverType : null,
fieldName,
ProblemReasons.NotFound);
} catch (AbortCompilation e) {
e.updateContext(invocationSite, referenceCompilationUnit().compilationResult);
throw e;
} finally {
env.missingClassFileLocation = null;
}
}
/* 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;
boolean insideTypeAnnotation = false;
MethodBinding foundMethod = null;
MethodBinding foundProblem = null;
boolean foundProblemVisible = false;
Scope scope = this;
int depth = 0;
// in 1.4 mode (inherited visible shadows enclosing)
CompilerOptions options;
boolean inheritedHasPrecedence = (options = compilerOptions()).complianceLevel >= ClassFileConstants.JDK1_4;
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;
insideTypeAnnotation = methodScope.insideTypeAnnotation;
break;
case CLASS_SCOPE :
ClassScope classScope = (ClassScope) scope;
ReferenceBinding receiverType = classScope.enclosingReceiverType();
if (!insideTypeAnnotation) {
// retrieve an exact visible match (if possible)
// compilationUnitScope().recordTypeReference(receiverType); not needed since receiver is the source type
MethodBinding methodBinding = classScope.findExactMethod(receiverType, selector, argumentTypes, invocationSite);
if (methodBinding == null)
methodBinding = classScope.findMethod(receiverType, selector, argumentTypes, invocationSite);
if (methodBinding != null) { // skip it if we did not find anything
if (foundMethod == null) {
if (methodBinding.isValidBinding()) {
if (!methodBinding.isStatic() && (insideConstructorCall || insideStaticContext)) {
if (foundProblem != null && foundProblem.problemId() != ProblemReasons.NotVisible)
return foundProblem; // takes precedence
return new ProblemMethodBinding(
methodBinding, // closest match
methodBinding.selector,
methodBinding.parameters,
insideConstructorCall
? ProblemReasons.NonStaticReferenceInConstructorInvocation
: ProblemReasons.NonStaticReferenceInStaticContext);
}
if (inheritedHasPrecedence
|| receiverType == methodBinding.declaringClass
|| (receiverType.getMethods(selector)) != Binding.NO_METHODS) {
// found a valid method in the 'immediate' scope (ie. not inherited)
// OR in 1.4 mode (inherited visible shadows enclosing)
// OR the receiverType implemented a method with the correct name
// return the methodBinding if it is not declared in a superclass of the scope's binding (that is, inherited)
if (foundProblemVisible) {
return foundProblem;
}
if (depth > 0) {
invocationSite.setDepth(depth);
invocationSite.setActualReceiverType(receiverType);
}
// special treatment for Object.getClass() in 1.5 mode (substitute parameterized return type)
if (argumentTypes == Binding.NO_PARAMETERS
&& CharOperation.equals(selector, TypeConstants.GETCLASS)
&& methodBinding.returnType.isParameterizedType()/*1.5*/) {
return ParameterizedMethodBinding.instantiateGetClass(receiverType, methodBinding, this);
}
return methodBinding;
}
if (foundProblem == null || foundProblem.problemId() == ProblemReasons.NotVisible) {
if (foundProblem != null) foundProblem = null;
// only remember the methodBinding if its the first one found
// remember that private methods are visible if defined directly by an enclosing class
if (depth > 0) {
invocationSite.setDepth(depth);
invocationSite.setActualReceiverType(receiverType);
}
foundMethod = methodBinding;
}
} else { // methodBinding is a problem method
if (methodBinding.problemId() != ProblemReasons.NotVisible && methodBinding.problemId() != ProblemReasons.NotFound)
return methodBinding; // return the error now
if (foundProblem == null) {
foundProblem = methodBinding; // hold onto the first not visible/found error and keep the second not found if first is not visible
}
if (! foundProblemVisible && methodBinding.problemId() == ProblemReasons.NotFound) {
MethodBinding closestMatch = ((ProblemMethodBinding) methodBinding).closestMatch;
if (closestMatch != null && closestMatch.canBeSeenBy(receiverType, invocationSite, this)) {
foundProblem = methodBinding; // hold onto the first not visible/found error and keep the second not found if first is not visible
foundProblemVisible = true;
}
}
}
} else { // found a valid method so check to see if this is a hiding case
if (methodBinding.problemId() == ProblemReasons.Ambiguous
|| (foundMethod.declaringClass != methodBinding.declaringClass
&& (receiverType == methodBinding.declaringClass || receiverType.getMethods(selector) != Binding.NO_METHODS)))
// ambiguous case -> must qualify the method (javac generates an ambiguous error instead)
// otherwise 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
return new ProblemMethodBinding(
methodBinding, // closest match
selector,
argumentTypes,
ProblemReasons.InheritedNameHidesEnclosingName);
}
}
}
insideTypeAnnotation = false;
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 (insideStaticContext && options.sourceLevel >= ClassFileConstants.JDK1_5) {
if (foundProblem != null) {
if (foundProblem.declaringClass != null && foundProblem.declaringClass.id == TypeIds.T_JavaLangObject)
return foundProblem; // static imports lose to methods from Object
if (foundProblem.problemId() == ProblemReasons.NotFound && foundProblemVisible) {
return foundProblem; // visible method selectors take precedence
}
}
// at this point the scope is a compilation unit scope & need to check for imported static methods
CompilationUnitScope unitScope = (CompilationUnitScope) scope;
unitScope.faultInImports(); // field constants can cause static imports to be accessed before they're resolved
ImportBinding[] imports = unitScope.imports;
if (imports != null) {
ObjectVector visible = null;
boolean skipOnDemand = false; // set to true when matched static import of method name so stop looking for on demand methods
for (int i = 0, length = imports.length; i < length; i++) {
ImportBinding importBinding = imports[i];
if (importBinding.isStatic()) {
Binding resolvedImport = importBinding.resolvedImport;
MethodBinding possible = null;
if (importBinding.onDemand) {
if (!skipOnDemand && resolvedImport instanceof ReferenceBinding)
// answers closest approximation, may not check argumentTypes or visibility
possible = findMethod((ReferenceBinding) resolvedImport, selector, argumentTypes, invocationSite, true);
} else {
if (resolvedImport instanceof MethodBinding) {
MethodBinding staticMethod = (MethodBinding) resolvedImport;
if (CharOperation.equals(staticMethod.selector, selector))
// answers closest approximation, may not check argumentTypes or visibility
possible = findMethod(staticMethod.declaringClass, selector, argumentTypes, invocationSite, true);
} else if (resolvedImport instanceof FieldBinding) {
// check to see if there are also methods with the same name
FieldBinding staticField = (FieldBinding) resolvedImport;
if (CharOperation.equals(staticField.name, selector)) {
// must find the importRef's type again since the field can be from an inherited type
char[][] importName = importBinding.reference.tokens;
TypeBinding referencedType = getType(importName, importName.length - 1);
if (referencedType != null)
// answers closest approximation, may not check argumentTypes or visibility
possible = findMethod((ReferenceBinding) referencedType, selector, argumentTypes, invocationSite, true);
}
}
}
if (possible != null && possible != foundProblem) {
if (!possible.isValidBinding()) {
if (foundProblem == null)
foundProblem = possible; // answer as error case match
} else if (possible.isStatic()) {
MethodBinding compatibleMethod = computeCompatibleMethod(possible, argumentTypes, invocationSite);
if (compatibleMethod != null) {
if (compatibleMethod.isValidBinding()) {
if (compatibleMethod.canBeSeenBy(unitScope.fPackage)) {
if (visible == null || !visible.contains(compatibleMethod)) {
ImportReference importReference = importBinding.reference;
if (importReference != null) {
importReference.bits |= ASTNode.Used;
}
if (!skipOnDemand && !importBinding.onDemand) {
visible = null; // forget previous matches from on demand imports
skipOnDemand = true;
}
if (visible == null)
visible = new ObjectVector(3);
visible.add(compatibleMethod);
}
} else if (foundProblem == null) {
foundProblem = new ProblemMethodBinding(compatibleMethod, selector, compatibleMethod.parameters, ProblemReasons.NotVisible);
}
} else if (foundProblem == null) {
foundProblem = compatibleMethod;
}
} else if (foundProblem == null) {
foundProblem = new ProblemMethodBinding(possible, selector, argumentTypes, ProblemReasons.NotFound);
}
}
}
}
}
if (visible != null) {
MethodBinding[] temp = new MethodBinding[visible.size];
visible.copyInto(temp);
foundMethod = mostSpecificMethodBinding(temp, temp.length, argumentTypes, invocationSite, null);
}
}
}
if (foundMethod != null) {
invocationSite.setActualReceiverType(foundMethod.declaringClass);
return foundMethod;
}
if (foundProblem != null)
return foundProblem;
return new ProblemMethodBinding(selector, argumentTypes, ProblemReasons.NotFound);
}
public final ReferenceBinding getJavaIoSerializable() {
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordQualifiedReference(TypeConstants.JAVA_IO_SERIALIZABLE);
return unitScope.environment.getResolvedType(TypeConstants.JAVA_IO_SERIALIZABLE, this);
}
public final ReferenceBinding getJavaLangAnnotationAnnotation() {
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordQualifiedReference(TypeConstants.JAVA_LANG_ANNOTATION_ANNOTATION);
return unitScope.environment.getResolvedType(TypeConstants.JAVA_LANG_ANNOTATION_ANNOTATION, this);
}
public final ReferenceBinding getJavaLangAssertionError() {
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordQualifiedReference(TypeConstants.JAVA_LANG_ASSERTIONERROR);
return unitScope.environment.getResolvedType(TypeConstants.JAVA_LANG_ASSERTIONERROR, this);
}
public final ReferenceBinding getJavaLangClass() {
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordQualifiedReference(TypeConstants.JAVA_LANG_CLASS);
return unitScope.environment.getResolvedType(TypeConstants.JAVA_LANG_CLASS, this);
}
public final ReferenceBinding getJavaLangCloneable() {
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordQualifiedReference(TypeConstants.JAVA_LANG_CLONEABLE);
return unitScope.environment.getResolvedType(TypeConstants.JAVA_LANG_CLONEABLE, this);
}
public final ReferenceBinding getJavaLangEnum() {
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordQualifiedReference(TypeConstants.JAVA_LANG_ENUM);
return unitScope.environment.getResolvedType(TypeConstants.JAVA_LANG_ENUM, this);
}
public final ReferenceBinding getJavaLangIterable() {
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordQualifiedReference(TypeConstants.JAVA_LANG_ITERABLE);
return unitScope.environment.getResolvedType(TypeConstants.JAVA_LANG_ITERABLE, this);
}
public final ReferenceBinding getJavaLangObject() {
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordQualifiedReference(TypeConstants.JAVA_LANG_OBJECT);
return unitScope.environment.getResolvedType(TypeConstants.JAVA_LANG_OBJECT, this);
}
public final ReferenceBinding getJavaLangString() {
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordQualifiedReference(TypeConstants.JAVA_LANG_STRING);
return unitScope.environment.getResolvedType(TypeConstants.JAVA_LANG_STRING, this);
}
public final ReferenceBinding getJavaLangThrowable() {
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordQualifiedReference(TypeConstants.JAVA_LANG_THROWABLE);
return unitScope.environment.getResolvedType(TypeConstants.JAVA_LANG_THROWABLE, this);
}
public final ReferenceBinding getJavaUtilIterator() {
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordQualifiedReference(TypeConstants.JAVA_UTIL_ITERATOR);
return unitScope.environment.getResolvedType(TypeConstants.JAVA_UTIL_ITERATOR, this);
}
/* 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;
char[][] compoundName = new char[][] { typeName };
return new ProblemReferenceBinding(compoundName, null, ProblemReasons.NotFound);
}
public MethodBinding getMethod(TypeBinding receiverType, char[] selector, TypeBinding[] argumentTypes, InvocationSite invocationSite) {
CompilationUnitScope unitScope = compilationUnitScope();
LookupEnvironment env = unitScope.environment;
try {
env.missingClassFileLocation = invocationSite;
switch (receiverType.kind()) {
case Binding.BASE_TYPE :
return new ProblemMethodBinding(selector, argumentTypes, ProblemReasons.NotFound);
case Binding.ARRAY_TYPE :
unitScope.recordTypeReference(receiverType);
return findMethodForArray((ArrayBinding) receiverType, selector, argumentTypes, invocationSite);
}
unitScope.recordTypeReference(receiverType);
ReferenceBinding currentType = (ReferenceBinding) receiverType;
if (!currentType.canBeSeenBy(this))
return new ProblemMethodBinding(selector, argumentTypes, ProblemReasons.ReceiverTypeNotVisible);
// retrieve an exact visible match (if possible)
MethodBinding methodBinding = findExactMethod(currentType, selector, argumentTypes, invocationSite);
if (methodBinding != null) return methodBinding;
methodBinding = findMethod(currentType, selector, argumentTypes, invocationSite);
if (methodBinding == null)
return new ProblemMethodBinding(selector, argumentTypes, ProblemReasons.NotFound);
if (!methodBinding.isValidBinding())
return methodBinding;
// special treatment for Object.getClass() in 1.5 mode (substitute parameterized return type)
if (argumentTypes == Binding.NO_PARAMETERS
&& CharOperation.equals(selector, TypeConstants.GETCLASS)
&& methodBinding.returnType.isParameterizedType()/*1.5*/) {
return ParameterizedMethodBinding.instantiateGetClass(receiverType, methodBinding, this);
}
return methodBinding;
} catch (AbortCompilation e) {
e.updateContext(invocationSite, referenceCompilationUnit().compilationResult);
throw e;
} finally {
env.missingClassFileLocation = null;
}
}
/* Answer the package from the compoundName or null if it begins with a type.
* Intended to be used while resolving a qualified type name.
*
* NOTE: If a problem binding is returned, senders should extract the compound name
* from the binding & not assume the problem applies to the entire compoundName.
*/
public final Binding getPackage(char[][] compoundName) {
compilationUnitScope().recordQualifiedReference(compoundName);
Binding binding = getTypeOrPackage(compoundName[0], Binding.TYPE | Binding.PACKAGE, true);
if (binding == null) {
char[][] qName = new char[][] { compoundName[0] };
return new ProblemReferenceBinding(qName, environment().createMissingType(null, compoundName), ProblemReasons.NotFound);
}
if (!binding.isValidBinding()) {
if (binding instanceof PackageBinding) { /* missing package */
char[][] qName = new char[][] { compoundName[0] };
return new ProblemReferenceBinding(qName, null /* no closest match since search for pkg*/, ProblemReasons.NotFound);
}
return binding;
}
if (!(binding instanceof PackageBinding)) return null; // compoundName does not start with a package
int currentIndex = 1, length = compoundName.length;
PackageBinding packageBinding = (PackageBinding) binding;
while (currentIndex < length) {
binding = packageBinding.getTypeOrPackage(compoundName[currentIndex++]);
if (binding == null) {
return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, currentIndex), null /* no closest match since search for pkg*/, ProblemReasons.NotFound);
}
if (!binding.isValidBinding())
return new ProblemReferenceBinding(
CharOperation.subarray(compoundName, 0, currentIndex),
binding instanceof ReferenceBinding ? (ReferenceBinding)((ReferenceBinding)binding).closestMatch() : null,
binding.problemId());
if (!(binding instanceof PackageBinding))
return packageBinding;
packageBinding = (PackageBinding) binding;
}
return new ProblemReferenceBinding(compoundName, null /* no closest match since search for pkg*/, ProblemReasons.NotFound);
}
/* Answer the type binding that corresponds the given name, starting the lookup in the receiver.
* The name provided is a simple source name (e.g., "Object" , "Point", ...)
*/
// The return type of this method could be ReferenceBinding if we did not answer base types.
// NOTE: We could support looking for Base Types last in the search, however any code using
// this feature would be extraordinarily slow. Therefore we don't do this
public final TypeBinding getType(char[] name) {
// Would like to remove this test and require senders to specially handle base types
TypeBinding binding = getBaseType(name);
if (binding != null) return binding;
return (ReferenceBinding) getTypeOrPackage(name, Binding.TYPE, true);
}
/* 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),
null,
ProblemReasons.NotFound);
}
if (!binding.isValidBinding()) {
return new ProblemReferenceBinding(
binding instanceof ReferenceBinding ? ((ReferenceBinding)binding).compoundName : CharOperation.arrayConcat(packageBinding.compoundName, name),
binding instanceof ReferenceBinding ? (ReferenceBinding)((ReferenceBinding)binding).closestMatch() : null,
binding.problemId());
}
ReferenceBinding typeBinding = (ReferenceBinding) binding;
if (!typeBinding.canBeSeenBy(this))
return new ProblemReferenceBinding(
typeBinding.compoundName,
typeBinding,
ProblemReasons.NotVisible);
return typeBinding;
}
/* Answer the type binding corresponding to the compoundName.
*
* NOTE: If a problem binding is returned, senders should extract the compound name
* from the binding & not assume the problem applies to the entire compoundName.
*/
public final TypeBinding getType(char[][] compoundName, int typeNameLength) {
if (typeNameLength == 1) {
// Would like to remove this test and require senders to specially handle base types
TypeBinding binding = getBaseType(compoundName[0]);
if (binding != null) return binding;
}
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordQualifiedReference(compoundName);
Binding binding = getTypeOrPackage(compoundName[0], typeNameLength == 1 ? Binding.TYPE : Binding.TYPE | Binding.PACKAGE, true);
if (binding == null) {
char[][] qName = new char[][] { compoundName[0] };
return new ProblemReferenceBinding(qName, environment().createMissingType(compilationUnitScope().getCurrentPackage(), qName), ProblemReasons.NotFound);
}
if (!binding.isValidBinding()) {
if (binding instanceof PackageBinding) {
char[][] qName = new char[][] { compoundName[0] };
return new ProblemReferenceBinding(
qName,
environment().createMissingType(null, qName),
ProblemReasons.NotFound);
}
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) {
char[][] qName = CharOperation.subarray(compoundName, 0, currentIndex);
return new ProblemReferenceBinding(
qName,
environment().createMissingType(packageBinding, qName),
ProblemReasons.NotFound);
}
if (!binding.isValidBinding())
return new ProblemReferenceBinding(
CharOperation.subarray(compoundName, 0, currentIndex),
binding instanceof ReferenceBinding ? (ReferenceBinding)((ReferenceBinding)binding).closestMatch() : null,
binding.problemId());
if (!(binding instanceof PackageBinding))
break;
packageBinding = (PackageBinding) binding;
}
if (binding instanceof PackageBinding) {
char[][] qName = CharOperation.subarray(compoundName, 0, currentIndex);
return new ProblemReferenceBinding(
qName,
environment().createMissingType(null, qName),
ProblemReasons.NotFound);
}
checkVisibility = true;
}
// binding is now a ReferenceBinding
ReferenceBinding typeBinding = (ReferenceBinding) binding;
unitScope.recordTypeReference(typeBinding);
if (checkVisibility) // handles the fall through case
if (!typeBinding.canBeSeenBy(this))
return new ProblemReferenceBinding(
CharOperation.subarray(compoundName, 0, currentIndex),
typeBinding,
ProblemReasons.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.closestReferenceMatch(),
typeBinding.problemId());
}
return new ProblemReferenceBinding(
CharOperation.subarray(compoundName, 0, currentIndex),
(ReferenceBinding)((ReferenceBinding)binding).closestMatch(),
typeBinding.problemId());
}
}
return typeBinding;
}
/* Internal use only
*/
final Binding getTypeOrPackage(char[] name, int mask, boolean needResolve) {
Scope scope = this;
ReferenceBinding foundType = null;
boolean insideStaticContext = false;
boolean insideTypeAnnotation = false;
if ((mask & Binding.TYPE) == 0) {
Scope next = scope;
while ((next = scope.parent) != null)
scope = next;
} else {
boolean inheritedHasPrecedence = compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4;
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) {
if (methodDecl.binding != null) {
TypeVariableBinding typeVariable = methodDecl.binding.getTypeVariable(name);
if (typeVariable != null)
return typeVariable;
} else {
// use the methodDecl's typeParameters to handle problem cases when the method binding doesn't exist
TypeParameter[] params = methodDecl.typeParameters();
for (int i = params == null ? 0 : params.length; --i >= 0;)
if (CharOperation.equals(params[i].name, name))
if (params[i].binding != null && params[i].binding.isValidBinding())
return params[i].binding;
}
}
insideStaticContext |= methodScope.isStatic;
insideTypeAnnotation = methodScope.insideTypeAnnotation;
//$FALL-THROUGH$
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(new char[][]{name}, foundType, ProblemReasons.InheritedNameHidesEnclosingName);
return localType;
}
break;
case CLASS_SCOPE :
SourceTypeBinding sourceType = ((ClassScope) scope).referenceContext.binding;
if (scope == this && (sourceType.tagBits & TagBits.TypeVariablesAreConnected) == 0) {
// type variables take precedence over the source type, ex. class X extends X == class X extends Y
// but not when we step out to the enclosing type
TypeVariableBinding typeVariable = sourceType.getTypeVariable(name);
if (typeVariable != null)
return typeVariable;
if (CharOperation.equals(name, sourceType.sourceName))
return sourceType;
insideStaticContext |= sourceType.isStatic();
break;
}
// member types take precedence over type variables
if (!insideTypeAnnotation) {
// 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() == ProblemReasons.Ambiguous) {
if (foundType == null || foundType.problemId() == ProblemReasons.NotVisible)
// supercedes any potential InheritedNameHidesEnclosingName problem
return memberType;
// make the user qualify the type, likely wants the first inherited type
return new ProblemReferenceBinding(new char[][]{name}, foundType, ProblemReasons.InheritedNameHidesEnclosingName);
}
if (memberType.isValidBinding()) {
if (sourceType == memberType.enclosingType() || inheritedHasPrecedence) {
if (insideStaticContext && !memberType.isStatic() && sourceType.isGenericType())
return new ProblemReferenceBinding(new char[][]{name}, memberType, ProblemReasons.NonStaticReferenceInStaticContext);
// found a valid type in the 'immediate' scope (ie. not inherited)
// OR in 1.4 mode (inherited visible shadows enclosing)
if (foundType == null || (inheritedHasPrecedence && foundType.problemId() == ProblemReasons.NotVisible))
return memberType;
// if a valid type was found, complain when another is found in an 'immediate' enclosing type (ie. not inherited)
if (foundType.isValidBinding() && foundType != memberType)
return new ProblemReferenceBinding(new char[][]{name}, foundType, ProblemReasons.InheritedNameHidesEnclosingName);
}
}
if (foundType == null || (foundType.problemId() == ProblemReasons.NotVisible && memberType.problemId() != ProblemReasons.NotVisible))
// only remember the memberType if its the first one found or the previous one was not visible & memberType is...
foundType = memberType;
}
}
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(new char[][]{name}, typeVariable, ProblemReasons.NonStaticReferenceInStaticContext);
return typeVariable;
}
insideStaticContext |= sourceType.isStatic();
insideTypeAnnotation = false;
if (CharOperation.equals(sourceType.sourceName, name)) {
if (foundType != null && foundType != sourceType && foundType.problemId() != ProblemReasons.NotVisible)
return new ProblemReferenceBinding(new char[][]{name}, foundType, ProblemReasons.InheritedNameHidesEnclosingName);
return sourceType;
}
break;
case COMPILATION_UNIT_SCOPE :
break done;
}
scope = scope.parent;
}
if (foundType != null && foundType.problemId() != ProblemReasons.NotVisible)
return foundType;
}
// at this point the scope is a compilation unit scope
CompilationUnitScope unitScope = (CompilationUnitScope) scope;
HashtableOfObject typeOrPackageCache = unitScope.typeOrPackageCache;
if (typeOrPackageCache != null) {
Binding cachedBinding = (Binding) typeOrPackageCache.get(name);
if (cachedBinding != null) { // can also include NotFound ProblemReferenceBindings if we already know this name is not found
if (cachedBinding instanceof ImportBinding) { // single type import cached in faultInImports(), replace it in the cache with the type
ImportReference importReference = ((ImportBinding) cachedBinding).reference;
if (importReference != null) {
importReference.bits |= ASTNode.Used;
}
if (cachedBinding instanceof ImportConflictBinding)
typeOrPackageCache.put(name, cachedBinding = ((ImportConflictBinding) cachedBinding).conflictingTypeBinding); // already know its visible
else
typeOrPackageCache.put(name, cachedBinding = ((ImportBinding) cachedBinding).resolvedImport); // already know its visible
}
if ((mask & Binding.TYPE) != 0) {
if (foundType != null && foundType.problemId() != ProblemReasons.NotVisible && cachedBinding.problemId() != ProblemReasons.Ambiguous)
return foundType; // problem type from above supercedes NotFound type but not Ambiguous import case
if (cachedBinding instanceof ReferenceBinding)
return cachedBinding; // cached type found in previous walk below
}
if ((mask & Binding.PACKAGE) != 0 && cachedBinding instanceof PackageBinding)
return cachedBinding; // cached package found in previous walk below
}
}
// ask for the imports + name
if ((mask & Binding.TYPE) != 0) {
ImportBinding[] imports = unitScope.imports;
if (imports != null && typeOrPackageCache == null) { // walk single type imports since faultInImports() has not run yet
nextImport : for (int i = 0, length = imports.length; i < length; i++) {
ImportBinding importBinding = imports[i];
if (!importBinding.onDemand) {
if (CharOperation.equals(importBinding.compoundName[importBinding.compoundName.length - 1], name)) {
Binding resolvedImport = unitScope.resolveSingleImport(importBinding, Binding.TYPE);
if (resolvedImport == null) continue nextImport;
if (resolvedImport instanceof TypeBinding) {
ImportReference importReference = importBinding.reference;
if (importReference != null)
importReference.bits |= ASTNode.Used;
return resolvedImport; // already know its visible
}
}
}
}
}
// check if the name is in the current package, skip it if its a sub-package
PackageBinding currentPackage = unitScope.fPackage;
unitScope.recordReference(currentPackage.compoundName, name);
Binding binding = currentPackage.getTypeOrPackage(name);
if (binding instanceof ReferenceBinding) {
ReferenceBinding referenceType = (ReferenceBinding) binding;
if ((referenceType.tagBits & TagBits.HasMissingType) == 0) {
if (typeOrPackageCache != null)
typeOrPackageCache.put(name, referenceType);
return referenceType; // 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 = null;
if (resolvedImport instanceof PackageBinding) {
temp = findType(name, (PackageBinding) resolvedImport, currentPackage);
} else if (someImport.isStatic()) {
temp = findMemberType(name, (ReferenceBinding) resolvedImport); // static imports are allowed to see inherited member types
if (temp != null && !temp.isStatic())
temp = null;
} else {
temp = findDirectMemberType(name, (ReferenceBinding) resolvedImport);
}
if (temp != type && temp != null) {
if (temp.isValidBinding()) {
ImportReference importReference = someImport.reference;
if (importReference != null) {
importReference.bits |= ASTNode.Used;
}
if (foundInImport) {
// Answer error binding -- import on demand conflict; name found in two import on demand packages.
temp = new ProblemReferenceBinding(new char[][]{name}, type, ProblemReasons.Ambiguous);
if (typeOrPackageCache != null)
typeOrPackageCache.put(name, temp);
return temp;
}
type = temp;
foundInImport = true;
} else if (foundType == null) {
foundType = temp;
}
}
}
}
if (type != null) {
if (typeOrPackageCache != null)
typeOrPackageCache.put(name, type);
return type;
}
}
}
unitScope.recordSimpleReference(name);
if ((mask & Binding.PACKAGE) != 0) {
PackageBinding packageBinding = unitScope.environment.getTopLevelPackage(name);
if (packageBinding != null) {
if (typeOrPackageCache != null)
typeOrPackageCache.put(name, packageBinding);
return packageBinding;
}
}
// Answer error binding -- could not find name
if (foundType == null) {
char[][] qName = new char[][] { name };
ReferenceBinding closestMatch = null;
if ((mask & Binding.PACKAGE) != 0 || unitScope.environment.getTopLevelPackage(name) == null) {
if (needResolve) {
closestMatch = environment().createMissingType(unitScope.fPackage, qName);
}
}
foundType = new ProblemReferenceBinding(qName, closestMatch, ProblemReasons.NotFound);
if (typeOrPackageCache != null && (mask & Binding.PACKAGE) != 0) { // only put NotFound type in cache if you know its not a package
typeOrPackageCache.put(name, foundType);
}
} else if ((foundType.tagBits & TagBits.HasMissingType) != 0) {
char[][] qName = new char[][] { name };
foundType = new ProblemReferenceBinding(qName, foundType, ProblemReasons.NotFound);
if (typeOrPackageCache != null && (mask & Binding.PACKAGE) != 0) // only put NotFound type in cache if you know its not a package
typeOrPackageCache.put(name, foundType);
}
return foundType;
}
// Added for code assist... NOT Public API
// DO NOT USE to resolve import references since this method assumes 'A.B' is relative to a single type import of 'p1.A'
// when it may actually mean the type B in the package A
// use CompilationUnitScope.getImport(char[][]) instead
public final Binding getTypeOrPackage(char[][] compoundName) {
int nameLength = compoundName.length;
if (nameLength == 1) {
TypeBinding binding = getBaseType(compoundName[0]);
if (binding != null) return binding;
}
Binding binding = getTypeOrPackage(compoundName[0], Binding.TYPE | Binding.PACKAGE, true);
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),
null,
ProblemReasons.NotFound);
if (!binding.isValidBinding())
return new ProblemReferenceBinding(
CharOperation.subarray(compoundName, 0, currentIndex),
binding instanceof ReferenceBinding ? (ReferenceBinding)((ReferenceBinding)binding).closestMatch() : null,
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;
ReferenceBinding qualifiedType = (ReferenceBinding) environment().convertToRawType(typeBinding, false /*do not force conversion of enclosing types*/);
if (checkVisibility) // handles the fall through case
if (!typeBinding.canBeSeenBy(this))
return new ProblemReferenceBinding(
CharOperation.subarray(compoundName, 0, currentIndex),
typeBinding,
ProblemReasons.NotVisible);
while (currentIndex < nameLength) {
typeBinding = getMemberType(compoundName[currentIndex++], typeBinding);
// checks visibility
if (!typeBinding.isValidBinding())
return new ProblemReferenceBinding(
CharOperation.subarray(compoundName, 0, currentIndex),
(ReferenceBinding)typeBinding.closestMatch(),
typeBinding.problemId());
if (typeBinding.isGenericType()) {
qualifiedType = environment().createRawType(typeBinding, qualifiedType);
} else {
qualifiedType = (qualifiedType != null && (qualifiedType.isRawType() || qualifiedType.isParameterizedType()))
? environment().createParameterizedType(typeBinding, null, qualifiedType)
: typeBinding;
}
}
return qualifiedType;
}
protected boolean hasErasedCandidatesCollisions(TypeBinding one, TypeBinding two, Map invocations, ReferenceBinding type, ASTNode typeRef) {
invocations.clear();
TypeBinding[] mecs = minimalErasedCandidates(new TypeBinding[] {one, two}, invocations);
if (mecs != null) {
nextCandidate: for (int k = 0, max = mecs.length; k < max; k++) {
TypeBinding mec = mecs[k];
if (mec == null) continue nextCandidate;
Object value = invocations.get(mec);
if (value instanceof TypeBinding[]) {
TypeBinding[] invalidInvocations = (TypeBinding[]) value;
problemReporter().superinterfacesCollide(invalidInvocations[0].erasure(), typeRef, invalidInvocations[0], invalidInvocations[1]);
type.tagBits |= TagBits.HierarchyHasProblems;
return true;
}
}
}
return false;
}
/**
* Returns the immediately enclosing switchCase statement (carried by closest blockScope),
*/
public CaseStatement innermostSwitchCase() {
Scope scope = this;
do {
if (scope instanceof BlockScope)
return ((BlockScope) scope).enclosingCase;
scope = scope.parent;
} while (scope != null);
return null;
}
protected boolean isAcceptableMethod(MethodBinding one, MethodBinding two) {
TypeBinding[] oneParams = one.parameters;
TypeBinding[] twoParams = two.parameters;
int oneParamsLength = oneParams.length;
int twoParamsLength = twoParams.length;
if (oneParamsLength == twoParamsLength) {
next : for (int i = 0; i < oneParamsLength; i++) {
TypeBinding oneParam = oneParams[i];
TypeBinding twoParam = twoParams[i];
if (oneParam == twoParam || oneParam.isCompatibleWith(twoParam)) {
if (two.declaringClass.isRawType()) continue next;
TypeBinding originalTwoParam = two.original().parameters[i].leafComponentType();
switch (originalTwoParam.kind()) {
case Binding.TYPE_PARAMETER :
if (((TypeVariableBinding) originalTwoParam).hasOnlyRawBounds())
continue next;
//$FALL-THROUGH$
case Binding.WILDCARD_TYPE :
case Binding.INTERSECTION_TYPE:
case Binding.PARAMETERIZED_TYPE :
TypeBinding originalOneParam = one.original().parameters[i].leafComponentType();
switch (originalOneParam.kind()) {
case Binding.TYPE :
case Binding.GENERIC_TYPE :
TypeBinding inheritedTwoParam = oneParam.findSuperTypeOriginatingFrom(twoParam);
if (inheritedTwoParam == null || !inheritedTwoParam.leafComponentType().isRawType()) break;
return false;
case Binding.TYPE_PARAMETER :
if (!((TypeVariableBinding) originalOneParam).upperBound().isRawType()) break;
return false;
case Binding.RAW_TYPE:
// originalOneParam is RAW so it cannot be more specific than a wildcard or parameterized type
return false;
}
}
} else {
if (i == oneParamsLength - 1 && one.isVarargs() && two.isVarargs()) {
TypeBinding eType = ((ArrayBinding) twoParam).elementsType();
if (oneParam == eType || oneParam.isCompatibleWith(eType))
return true; // special case to choose between 2 varargs methods when the last arg is Object[]
}
return false;
}
}
return true;
}
if (one.isVarargs() && two.isVarargs()) {
if (oneParamsLength > twoParamsLength) {
// special case when autoboxing makes (int, int...) better than (Object...) but not (int...) or (Integer, int...)
if (((ArrayBinding) twoParams[twoParamsLength - 1]).elementsType().id != TypeIds.T_JavaLangObject)
return false;
}
// check that each parameter before the vararg parameters are compatible (no autoboxing allowed here)
for (int i = (oneParamsLength > twoParamsLength ? twoParamsLength : oneParamsLength) - 2; i >= 0; i--)
if (oneParams[i] != twoParams[i] && !oneParams[i].isCompatibleWith(twoParams[i]))
return false;
if (parameterCompatibilityLevel(one, twoParams) == NOT_COMPATIBLE
&& parameterCompatibilityLevel(two, oneParams) == VARARGS_COMPATIBLE)
return true;
}
return false;
}
public boolean isBoxingCompatibleWith(TypeBinding expressionType, TypeBinding targetType) {
LookupEnvironment environment = environment();
if (environment.globalOptions.sourceLevel < ClassFileConstants.JDK1_5 || expressionType.isBaseType() == targetType.isBaseType())
return false;
// check if autoboxed type is compatible
TypeBinding convertedType = environment.computeBoxingType(expressionType);
return convertedType == targetType || convertedType.isCompatibleWith(targetType);
}
/* Answer true if the scope is nested inside a given field declaration.
* Note: it works as long as the scope.fieldDeclarationIndex is reflecting the field being traversed
* e.g. during name resolution.
*/
public final boolean isDefinedInField(FieldBinding field) {
Scope scope = this;
do {
if (scope instanceof MethodScope) {
MethodScope methodScope = (MethodScope) scope;
if (methodScope.initializedField == field) return true;
}
scope = scope.parent;
} while (scope != null);
return false;
}
/* Answer true if the scope is nested inside a given method declaration
*/
public final boolean isDefinedInMethod(MethodBinding method) {
Scope scope = this;
do {
if (scope instanceof MethodScope) {
ReferenceContext refContext = ((MethodScope) scope).referenceContext;
if (refContext instanceof AbstractMethodDeclaration)
if (((AbstractMethodDeclaration) refContext).binding == method)
return true;
}
scope = scope.parent;
} while (scope != null);
return false;
}
/* Answer whether the type is defined in the same compilation unit as the receiver
*/
public final boolean isDefinedInSameUnit(ReferenceBinding type) {
// find the outer most enclosing type
ReferenceBinding enclosingType = type;
while ((type = enclosingType.enclosingType()) != null)
enclosingType = type;
// find the compilation unit scope
Scope scope, unitScope = this;
while ((scope = unitScope.parent) != null)
unitScope = scope;
// test that the enclosingType is not part of the compilation unit
SourceTypeBinding[] topLevelTypes = ((CompilationUnitScope) unitScope).topLevelTypes;
for (int i = topLevelTypes.length; --i >= 0;)
if (topLevelTypes[i] == enclosingType)
return true;
return false;
}
/* Answer true if the scope is nested inside a given type declaration
*/
public final boolean isDefinedInType(ReferenceBinding type) {
Scope scope = this;
do {
if (scope instanceof ClassScope)
if (((ClassScope) scope).referenceContext.binding == type)
return true;
scope = scope.parent;
} while (scope != null);
return false;
}
/**
* Returns true if the scope or one of its parent is associated to a given caseStatement, denoting
* being part of a given switch case statement.
*/
public boolean isInsideCase(CaseStatement caseStatement) {
Scope scope = this;
do {
switch (scope.kind) {
case Scope.BLOCK_SCOPE :
if (((BlockScope) scope).enclosingCase == caseStatement) {
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 if (methodScope.initializedField != null && methodScope.initializedField.isViewedAsDeprecated()) {
// inside field declaration ? check field modifier to see if deprecated
return true;
}
SourceTypeBinding declaringType = ((BlockScope)this).referenceType().binding;
if (declaringType != null) {
declaringType.initializeDeprecatedAnnotationTagBits(); // may not have been resolved until then
if (declaringType.isViewedAsDeprecated())
return true;
}
break;
case Scope.CLASS_SCOPE :
ReferenceBinding context = ((ClassScope)this).referenceType().binding;
if (context != null) {
context.initializeDeprecatedAnnotationTagBits(); // may not have been resolved until then
if (context.isViewedAsDeprecated())
return true;
}
break;
case Scope.COMPILATION_UNIT_SCOPE :
// consider import as being deprecated if first type is itself deprecated (123522)
CompilationUnitDeclaration unit = referenceCompilationUnit();
if (unit.types != null && unit.types.length > 0) {
SourceTypeBinding type = unit.types[0].binding;
if (type != null) {
type.initializeDeprecatedAnnotationTagBits(); // may not have been resolved until then
if (type.isViewedAsDeprecated())
return true;
}
}
}
return false;
}
private boolean isOverriddenMethodGeneric(MethodBinding method) {
MethodVerifier verifier = environment().methodVerifier();
ReferenceBinding currentType = method.declaringClass.superclass();
while (currentType != null) {
MethodBinding[] currentMethods = currentType.getMethods(method.selector);
for (int i = 0, l = currentMethods.length; i < l; i++) {
MethodBinding currentMethod = currentMethods[i];
if (currentMethod != null && currentMethod.original().typeVariables != Binding.NO_TYPE_VARIABLES)
if (verifier.doesMethodOverride(method, currentMethod))
return true;
}
currentType = currentType.superclass();
}
return false;
}
public boolean isPossibleSubtypeOfRawType(TypeBinding paramType) {
TypeBinding t = paramType.leafComponentType();
if (t.isBaseType()) return false;
ReferenceBinding currentType = (ReferenceBinding) t;
ReferenceBinding[] interfacesToVisit = null;
int nextPosition = 0;
do {
if (currentType.isRawType()) return true;
if (!currentType.isHierarchyConnected()) return true; // do not fault in super types right now, so assume one is a raw type
ReferenceBinding[] itsInterfaces = currentType.superInterfaces();
if (itsInterfaces != null && itsInterfaces != Binding.NO_SUPERINTERFACES) {
if (interfacesToVisit == null) {
interfacesToVisit = itsInterfaces;
nextPosition = interfacesToVisit.length;
} else {
int itsLength = itsInterfaces.length;
if (nextPosition + itsLength >= interfacesToVisit.length)
System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition);
nextInterface : for (int a = 0; a < itsLength; a++) {
ReferenceBinding next = itsInterfaces[a];
for (int b = 0; b < nextPosition; b++)
if (next == interfacesToVisit[b]) continue nextInterface;
interfacesToVisit[nextPosition++] = next;
}
}
}
} while ((currentType = currentType.superclass()) != null);
for (int i = 0; i < nextPosition; i++) {
currentType = interfacesToVisit[i];
if (currentType.isRawType()) return true;
ReferenceBinding[] itsInterfaces = currentType.superInterfaces();
if (itsInterfaces != null && itsInterfaces != Binding.NO_SUPERINTERFACES) {
int itsLength = itsInterfaces.length;
if (nextPosition + itsLength >= interfacesToVisit.length)
System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition);
nextInterface : for (int a = 0; a < itsLength; a++) {
ReferenceBinding next = itsInterfaces[a];
for (int b = 0; b < nextPosition; b++)
if (next == interfacesToVisit[b]) continue nextInterface;
interfacesToVisit[nextPosition++] = next;
}
}
}
return false;
}
private TypeBinding leastContainingInvocation(TypeBinding mec, Object invocationData, List lubStack) {
if (invocationData == null) return mec; // no alternate invocation
if (invocationData instanceof TypeBinding) { // only one invocation, simply return it (array only allocated if more than one)
return (TypeBinding) invocationData;
}
TypeBinding[] invocations = (TypeBinding[]) invocationData;
// if mec is an array type, intersect invocation leaf component types, then promote back to array
int dim = mec.dimensions();
mec = mec.leafComponentType();
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, length = invocations.length; i < length; i++) {
TypeBinding invocation = invocations[i].leafComponentType();
switch (invocation.kind()) {
case Binding.GENERIC_TYPE :
TypeVariableBinding[] invocationVariables = invocation.typeVariables();
for (int j = 0; j < argLength; j++) {
TypeBinding bestArgument = leastContainingTypeArgument(bestArguments[j], invocationVariables[j], (ReferenceBinding) mec, j, lubStack);
if (bestArgument == null) return null;
bestArguments[j] = bestArgument;
}
break;
case Binding.PARAMETERIZED_TYPE :
ParameterizedTypeBinding parameterizedType = (ParameterizedTypeBinding)invocation;
for (int j = 0; j < argLength; j++) {
TypeBinding bestArgument = leastContainingTypeArgument(bestArguments[j], parameterizedType.arguments[j], (ReferenceBinding) mec, j, lubStack);
if (bestArgument == null) return null;
bestArguments[j] = bestArgument;
}
break;
case Binding.RAW_TYPE :
return dim == 0 ? invocation : environment().createArrayType(invocation, dim); // raw type is taking precedence
}
}
TypeBinding least = environment().createParameterizedType((ReferenceBinding) mec.erasure(), bestArguments, mec.enclosingType());
return dim == 0 ? least : environment().createArrayType(least, dim);
}
// JLS 15.12.2
private TypeBinding leastContainingTypeArgument(TypeBinding u, TypeBinding v, ReferenceBinding genericType, int rank, List lubStack) {
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.boundKind) {
// ? extends U
case Wildcard.EXTENDS :
switch(wildV.boundKind) {
// ? extends U, ? extends V
case Wildcard.EXTENDS :
TypeBinding lub = lowerUpperBound(new TypeBinding[]{wildU.bound,wildV.bound}, lubStack);
if (lub == null) return null;
// int is returned to denote cycle detected in lub computation - stop recursion by answering unbound wildcard
if (lub == TypeBinding.INT) return environment().createWildcard(genericType, rank, null, null /*no extra bound*/, Wildcard.UNBOUND);
return environment().createWildcard(genericType, rank, lub, null /*no extra bound*/, Wildcard.EXTENDS);
// ? extends U, ? SUPER V
case Wildcard.SUPER :
if (wildU.bound == wildV.bound) return wildU.bound;
return environment().createWildcard(genericType, rank, null, null /*no extra bound*/, Wildcard.UNBOUND);
}
break;
// ? super U
case Wildcard.SUPER :
// ? super U, ? super V
if (wildU.boundKind == Wildcard.SUPER) {
TypeBinding[] glb = greaterLowerBound(new TypeBinding[]{wildU.bound,wildV.bound});
if (glb == null) return null;
return environment().createWildcard(genericType, rank, glb[0], null /*no extra bound*/, Wildcard.SUPER); // TODO (philippe) need to capture entire bounds
}
}
} else {
switch (wildV.boundKind) {
// U, ? extends V
case Wildcard.EXTENDS :
TypeBinding lub = lowerUpperBound(new TypeBinding[]{u,wildV.bound}, lubStack);
if (lub == null) return null;
// int is returned to denote cycle detected in lub computation - stop recursion by answering unbound wildcard
if (lub == TypeBinding.INT) return environment().createWildcard(genericType, rank, null, null /*no extra bound*/, Wildcard.UNBOUND);
return environment().createWildcard(genericType, rank, lub, null /*no extra bound*/, 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], null /*no extra bound*/, Wildcard.SUPER); // TODO (philippe) need to capture entire bounds
case Wildcard.UNBOUND :
}
}
} else if (u.isWildcard()) {
WildcardBinding wildU = (WildcardBinding) u;
switch (wildU.boundKind) {
// U, ? extends V
case Wildcard.EXTENDS :
TypeBinding lub = lowerUpperBound(new TypeBinding[]{wildU.bound, v}, lubStack);
if (lub == null) return null;
// int is returned to denote cycle detected in lub computation - stop recursion by answering unbound wildcard
if (lub == TypeBinding.INT) return environment().createWildcard(genericType, rank, null, null /*no extra bound*/, Wildcard.UNBOUND);
return environment().createWildcard(genericType, rank, lub, null /*no extra bound*/, 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], null /*no extra bound*/, Wildcard.SUPER); // TODO (philippe) need to capture entire bounds
case Wildcard.UNBOUND :
}
}
TypeBinding lub = lowerUpperBound(new TypeBinding[]{u,v}, lubStack);
if (lub == null) return null;
// int is returned to denote cycle detected in lub computation - stop recursion by answering unbound wildcard
if (lub == TypeBinding.INT) return environment().createWildcard(genericType, rank, null, null /*no extra bound*/, Wildcard.UNBOUND);
return environment().createWildcard(genericType, rank, lub, null /*no extra bound*/, Wildcard.EXTENDS);
}
// 15.12.2
/**
* Returns VoidBinding if types have no intersection (e.g. 2 unrelated interfaces), or null if
* no common supertype (e.g. List and List), or the intersection type if possible
*/
public TypeBinding lowerUpperBound(TypeBinding[] types) {
int typeLength = types.length;
if (typeLength == 1) {
TypeBinding type = types[0];
return type == null ? TypeBinding.VOID : type;
}
return lowerUpperBound(types, new ArrayList(1));
}
// 15.12.2
private TypeBinding lowerUpperBound(TypeBinding[] types, List lubStack) {
int typeLength = types.length;
if (typeLength == 1) {
TypeBinding type = types[0];
return type == null ? TypeBinding.VOID : type;
}
// cycle detection
int stackLength = lubStack.size();
nextLubCheck: for (int i = 0; i < stackLength; i++) {
TypeBinding[] lubTypes = (TypeBinding[])lubStack.get(i);
int lubTypeLength = lubTypes.length;
if (lubTypeLength < typeLength) continue nextLubCheck;
nextTypeCheck: for (int j = 0; j < typeLength; j++) {
TypeBinding type = types[j];
if (type == null) continue nextTypeCheck; // ignore
for (int k = 0; k < lubTypeLength; k++) {
TypeBinding lubType = lubTypes[k];
if (lubType == null) continue; // ignore
if (lubType == type || lubType.isEquivalentTo(type)) continue nextTypeCheck; // type found, jump to next one
}
continue nextLubCheck; // type not found in current lubTypes
}
// all types are included in some lub, cycle detected - stop recursion by answering special value (int)
return TypeBinding.INT;
}
lubStack.add(types);
Map invocations = new HashMap(1);
TypeBinding[] mecs = minimalErasedCandidates(types, invocations);
if (mecs == null) return null;
int length = mecs.length;
if (length == 0) return TypeBinding.VOID;
int count = 0;
TypeBinding firstBound = null;
int commonDim = -1;
for (int i = 0; i < length; i++) {
TypeBinding mec = mecs[i];
if (mec == null) continue;
mec = leastContainingInvocation(mec, invocations.get(mec), lubStack);
if (mec == null) return null;
int dim = mec.dimensions();
if (commonDim == -1) {
commonDim = dim;
} else if (dim != commonDim) { // not all types have same dimension
return null;
}
if (firstBound == null && !mec.leafComponentType().isInterface()) firstBound = mec.leafComponentType();
mecs[count++] = mec; // recompact them to the front
}
switch (count) {
case 0 : return TypeBinding.VOID;
case 1 : return mecs[0];
case 2 :
if ((commonDim == 0 ? mecs[1].id : mecs[1].leafComponentType().id) == TypeIds.T_JavaLangObject) return mecs[0];
if ((commonDim == 0 ? mecs[0].id : mecs[0].leafComponentType().id) == TypeIds.T_JavaLangObject) return mecs[1];
}
TypeBinding[] otherBounds = new TypeBinding[count - 1];
int rank = 0;
for (int i = 0; i < count; i++) {
TypeBinding mec = commonDim == 0 ? mecs[i] : mecs[i].leafComponentType();
if (mec.isInterface()) {
otherBounds[rank++] = mec;
}
}
TypeBinding intersectionType = environment().createWildcard(null, 0, firstBound, otherBounds, Wildcard.EXTENDS);
return commonDim == 0 ? intersectionType : environment().createArrayType(intersectionType, commonDim);
}
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 set of types compatible with all given types.
* (i.e. most specific common super types)
* If no types is given, will return an empty array. If not compatible
* reference type is found, returns null. In other cases, will return an array
* of minimal erased types, where some nulls may appear (and must simply be
* ignored).
*/
protected TypeBinding[] minimalErasedCandidates(TypeBinding[] types, Map allInvocations) {
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 Binding.NO_TYPES;
case 1: return types;
}
TypeBinding firstType = types[indexOfFirst];
if (firstType.isBaseType()) return null;
// record all supertypes of type
// intersect with all supertypes of otherType
ArrayList typesToVisit = new ArrayList(5);
int dim = firstType.dimensions();
TypeBinding leafType = firstType.leafComponentType();
// do not allow type variables/intersection types to match with erasures for free
TypeBinding firstErasure;
switch(leafType.kind()) {
case Binding.PARAMETERIZED_TYPE :
case Binding.RAW_TYPE :
case Binding.ARRAY_TYPE :
firstErasure = firstType.erasure();
break;
default :
firstErasure = firstType;
break;
}
if (firstErasure != firstType) {
allInvocations.put(firstErasure, firstType);
}
typesToVisit.add(firstType);
int max = 1;
ReferenceBinding currentType;
for (int i = 0; i < max; i++) {
TypeBinding typeToVisit = (TypeBinding) typesToVisit.get(i);
dim = typeToVisit.dimensions();
if (dim > 0) {
leafType = typeToVisit.leafComponentType();
switch(leafType.id) {
case TypeIds.T_JavaLangObject:
if (dim > 1) { // Object[][] supertype is Object[]
TypeBinding elementType = ((ArrayBinding)typeToVisit).elementsType();
if (!typesToVisit.contains(elementType)) {
typesToVisit.add(elementType);
max++;
}
continue;
}
//$FALL-THROUGH$
case TypeIds.T_byte:
case TypeIds.T_short:
case TypeIds.T_char:
case TypeIds.T_boolean:
case TypeIds.T_int:
case TypeIds.T_long:
case TypeIds.T_float:
case TypeIds.T_double:
TypeBinding superType = getJavaIoSerializable();
if (!typesToVisit.contains(superType)) {
typesToVisit.add(superType);
max++;
}
superType = getJavaLangCloneable();
if (!typesToVisit.contains(superType)) {
typesToVisit.add(superType);
max++;
}
superType = getJavaLangObject();
if (!typesToVisit.contains(superType)) {
typesToVisit.add(superType);
max++;
}
continue;
default:
}
typeToVisit = leafType;
}
currentType = (ReferenceBinding) typeToVisit;
if (currentType.isCapture()) {
TypeBinding firstBound = ((CaptureBinding) currentType).firstBound;
if (firstBound != null && firstBound.isArrayType()) {
TypeBinding superType = dim == 0 ? firstBound : (TypeBinding)environment().createArrayType(firstBound, dim); // recreate array if needed
if (!typesToVisit.contains(superType)) {
typesToVisit.add(superType);
max++;
TypeBinding superTypeErasure = (firstBound.isTypeVariable() || firstBound.isWildcard() /*&& !itsInterface.isCapture()*/) ? superType : superType.erasure();
if (superTypeErasure != superType) {
allInvocations.put(superTypeErasure, superType);
}
}
continue;
}
}
// inject super interfaces prior to superclass
ReferenceBinding[] itsInterfaces = currentType.superInterfaces();
if (itsInterfaces != null) { // can be null during code assist operations that use LookupEnvironment.completeTypeBindings(parsedUnit, buildFieldsAndMethods)
for (int j = 0, count = itsInterfaces.length; j < count; j++) {
TypeBinding itsInterface = itsInterfaces[j];
TypeBinding superType = dim == 0 ? itsInterface : (TypeBinding)environment().createArrayType(itsInterface, dim); // recreate array if needed
if (!typesToVisit.contains(superType)) {
typesToVisit.add(superType);
max++;
TypeBinding superTypeErasure = (itsInterface.isTypeVariable() || itsInterface.isWildcard() /*&& !itsInterface.isCapture()*/) ? superType : superType.erasure();
if (superTypeErasure != superType) {
allInvocations.put(superTypeErasure, superType);
}
}
}
}
TypeBinding itsSuperclass = currentType.superclass();
if (itsSuperclass != null) {
TypeBinding superType = dim == 0 ? itsSuperclass : (TypeBinding)environment().createArrayType(itsSuperclass, dim); // recreate array if needed
if (!typesToVisit.contains(superType)) {
typesToVisit.add(superType);
max++;
TypeBinding superTypeErasure = (itsSuperclass.isTypeVariable() || itsSuperclass.isWildcard() /*&& !itsSuperclass.isCapture()*/) ? superType : superType.erasure();
if (superTypeErasure != superType) {
allInvocations.put(superTypeErasure, superType);
}
}
}
}
int superLength = typesToVisit.size();
TypeBinding[] erasedSuperTypes = new TypeBinding[superLength];
int rank = 0;
for (Iterator iter = typesToVisit.iterator(); iter.hasNext();) {
TypeBinding type = (TypeBinding)iter.next();
leafType = type.leafComponentType();
erasedSuperTypes[rank++] = (leafType.isTypeVariable() || leafType.isWildcard() /*&& !leafType.isCapture()*/) ? type : type.erasure();
}
// intersecting first type supertypes with other types' ones, nullifying non matching supertypes
int remaining = superLength;
nextOtherType: for (int i = indexOfFirst+1; i < length; i++) {
TypeBinding otherType = types[i];
if (otherType == null) continue nextOtherType;
if (otherType.isArrayType()) {
nextSuperType: for (int j = 0; j < superLength; j++) {
TypeBinding erasedSuperType = erasedSuperTypes[j];
if (erasedSuperType == null || erasedSuperType == otherType) continue nextSuperType;
TypeBinding match;
if ((match = otherType.findSuperTypeOriginatingFrom(erasedSuperType)) == null) {
erasedSuperTypes[j] = null;
if (--remaining == 0) return null;
continue nextSuperType;
}
// record invocation
Object invocationData = allInvocations.get(erasedSuperType);
if (invocationData == null) {
allInvocations.put(erasedSuperType, match); // no array for singleton
} else if (invocationData instanceof TypeBinding) {
if (match != invocationData) {
// using an array to record invocations in order (188103)
TypeBinding[] someInvocations = { (TypeBinding) invocationData, match, };
allInvocations.put(erasedSuperType, someInvocations);
}
} else { // using an array to record invocations in order (188103)
TypeBinding[] someInvocations = (TypeBinding[]) invocationData;
checkExisting: {
int invocLength = someInvocations.length;
for (int k = 0; k < invocLength; k++) {
if (someInvocations[k] == match) break checkExisting;
}
System.arraycopy(someInvocations, 0, someInvocations = new TypeBinding[invocLength+1], 0, invocLength);
allInvocations.put(erasedSuperType, someInvocations);
someInvocations[invocLength] = match;
}
}
}
continue nextOtherType;
}
nextSuperType: for (int j = 0; j < superLength; j++) {
TypeBinding erasedSuperType = erasedSuperTypes[j];
if (erasedSuperType == null) continue nextSuperType;
TypeBinding match;
if (erasedSuperType == otherType || erasedSuperType.id == TypeIds.T_JavaLangObject && otherType.isInterface()) {
match = erasedSuperType;
} else {
if (erasedSuperType.isArrayType()) {
match = null;
} else {
match = otherType.findSuperTypeOriginatingFrom(erasedSuperType);
}
if (match == null) { // incompatible super type
erasedSuperTypes[j] = null;
if (--remaining == 0) return null;
continue nextSuperType;
}
}
// record invocation
Object invocationData = allInvocations.get(erasedSuperType);
if (invocationData == null) {
allInvocations.put(erasedSuperType, match); // no array for singleton
} else if (invocationData instanceof TypeBinding) {
if (match != invocationData) {
// using an array to record invocations in order (188103)
TypeBinding[] someInvocations = { (TypeBinding) invocationData, match, };
allInvocations.put(erasedSuperType, someInvocations);
}
} else { // using an array to record invocations in order (188103)
TypeBinding[] someInvocations = (TypeBinding[]) invocationData;
checkExisting: {
int invocLength = someInvocations.length;
for (int k = 0; k < invocLength; k++) {
if (someInvocations[k] == match) break checkExisting;
}
System.arraycopy(someInvocations, 0, someInvocations = new TypeBinding[invocLength+1], 0, invocLength);
allInvocations.put(erasedSuperType, someInvocations);
someInvocations[invocLength] = match;
}
}
}
}
// eliminate non minimal super types
if (remaining > 1) {
nextType: for (int i = 0; i < superLength; i++) {
TypeBinding erasedSuperType = erasedSuperTypes[i];
if (erasedSuperType == null) continue nextType;
nextOtherType: for (int j = 0; j < superLength; j++) {
if (i == j) continue nextOtherType;
TypeBinding otherType = erasedSuperTypes[j];
if (otherType == null) continue nextOtherType;
if (erasedSuperType instanceof ReferenceBinding) {
if (otherType.id == TypeIds.T_JavaLangObject && erasedSuperType.isInterface()) continue nextOtherType; // keep Object for an interface
if (erasedSuperType.findSuperTypeOriginatingFrom(otherType) != null) {
erasedSuperTypes[j] = null; // discard non minimal supertype
remaining--;
}
} else if (erasedSuperType.isArrayType()) {
if (otherType.isArrayType() // keep Object[...] for an interface array (same dimensions)
&& otherType.leafComponentType().id == TypeIds.T_JavaLangObject
&& otherType.dimensions() == erasedSuperType.dimensions()
&& erasedSuperType.leafComponentType().isInterface()) continue nextOtherType;
if (erasedSuperType.findSuperTypeOriginatingFrom(otherType) != null) {
erasedSuperTypes[j] = null; // discard non minimal supertype
remaining--;
}
}
}
}
}
return erasedSuperTypes;
}
// 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 previous = null;
nextVisible : for (int i = 0; i < visibleSize; i++) {
MethodBinding method = visible[i];
if (previous != null && method.declaringClass != previous.declaringClass)
break; // cannot answer a method farther up the hierarchy than the first method found
if (!method.isStatic()) previous = method; // no ambiguity for static methods
for (int j = 0; j < visibleSize; j++) {
if (i == j) continue;
if (!visible[j].areParametersCompatibleWith(method.parameters))
continue nextVisible;
}
compilationUnitScope().recordTypeReferences(method.thrownExceptions);
return method;
}
return new ProblemMethodBinding(visible[0], visible[0].selector, visible[0].parameters, ProblemReasons.Ambiguous);
}
// 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) {
nextVisible : for (int i = 0; i < visibleSize; i++) {
MethodBinding method = visible[i];
for (int j = 0; j < visibleSize; j++) {
if (i == j) continue;
if (!visible[j].areParametersCompatibleWith(method.parameters))
continue nextVisible;
}
compilationUnitScope().recordTypeReferences(method.thrownExceptions);
return method;
}
return new ProblemMethodBinding(visible[0], visible[0].selector, visible[0].parameters, ProblemReasons.Ambiguous);
}
// caveat: this is not a direct implementation of JLS
protected final MethodBinding mostSpecificMethodBinding(MethodBinding[] visible, int visibleSize, TypeBinding[] argumentTypes, InvocationSite invocationSite, ReferenceBinding receiverType) {
int[] compatibilityLevels = new int[visibleSize];
for (int i = 0; i < visibleSize; i++)
compatibilityLevels[i] = parameterCompatibilityLevel(visible[i], argumentTypes);
boolean useTiebreakMethod = invocationSite.genericTypeArguments() == null;
MethodBinding[] moreSpecific = new MethodBinding[visibleSize];
int count = 0;
for (int level = 0, max = VARARGS_COMPATIBLE; level <= max; level++) {
nextVisible : for (int i = 0; i < visibleSize; i++) {
if (compatibilityLevels[i] != level) continue nextVisible;
max = level; // do not examine further categories, will either return mostSpecific or report ambiguous case
MethodBinding current = visible[i];
MethodBinding original = current.original();
MethodBinding tiebreakMethod = useTiebreakMethod ? current.tiebreakMethod() : current;
for (int j = 0; j < visibleSize; j++) {
if (i == j || compatibilityLevels[j] != level) continue;
MethodBinding next = visible[j];
if (original == next.original()) {
// parameterized superclasses & interfaces may be walked twice from different paths so skip next from now on
compatibilityLevels[j] = -1;
continue;
}
MethodBinding methodToTest = next;
if (next instanceof ParameterizedGenericMethodBinding) {
ParameterizedGenericMethodBinding pNext = (ParameterizedGenericMethodBinding) next;
if (pNext.isRaw && !pNext.isStatic()) {
// hold onto the raw substituted method
} else {
methodToTest = pNext.originalMethod;
}
}
MethodBinding acceptable = computeCompatibleMethod(methodToTest, tiebreakMethod.parameters, invocationSite);
/* There are 4 choices to consider with current & next :
foo(B) & foo(A) where B extends A
1. the 2 methods are equal (both accept each others parameters) -> want to continue
2. current has more specific parameters than next (so acceptable is a valid method) -> want to continue
3. current has less specific parameters than next (so acceptable is null) -> go on to next
4. current and next are not compatible with each other (so acceptable is null) -> go on to next
*/
if (acceptable == null || !acceptable.isValidBinding())
continue nextVisible;
if (!isAcceptableMethod(tiebreakMethod, acceptable))
continue nextVisible;
// pick a concrete method over a bridge method when parameters are equal since the return type of the concrete method is more specific
if (current.isBridge() && !next.isBridge())
if (tiebreakMethod.areParametersEqual(acceptable))
continue nextVisible; // skip current so acceptable wins over this bridge method
}
moreSpecific[i] = current;
count++;
}
}
if (count == 1) {
for (int i = 0; i < visibleSize; i++) {
if (moreSpecific[i] != null) {
compilationUnitScope().recordTypeReferences(visible[i].thrownExceptions);
return visible[i];
}
}
} else if (count == 0) {
return new ProblemMethodBinding(visible[0], visible[0].selector, visible[0].parameters, ProblemReasons.Ambiguous);
}
// found several methods that are mutually acceptable -> must be equal
// so now with the first acceptable method, find the 'correct' inherited method for each other acceptable method AND
// see if they are equal after substitution of type variables (do the type variables have to be equal to be considered an override???)
if (receiverType != null)
receiverType = receiverType instanceof CaptureBinding ? receiverType : (ReferenceBinding) receiverType.erasure();
nextSpecific : for (int i = 0; i < visibleSize; i++) {
MethodBinding current = moreSpecific[i];
if (current != null) {
ReferenceBinding[] mostSpecificExceptions = null;
MethodBinding original = current.original();
boolean shouldIntersectExceptions = original.declaringClass.isAbstract() && original.thrownExceptions != Binding.NO_EXCEPTIONS; // only needed when selecting from interface methods
for (int j = 0; j < visibleSize; j++) {
MethodBinding next = moreSpecific[j];
if (next == null || i == j) continue;
MethodBinding original2 = next.original();
if (original.declaringClass == original2.declaringClass)
break nextSpecific; // duplicates thru substitution
if (!original.isAbstract()) {
if (original2.isAbstract())
continue; // only compare current against other concrete methods
TypeBinding superType = original.declaringClass.findSuperTypeOriginatingFrom(original2.declaringClass.erasure());
if (superType == null)
continue nextSpecific; // current's declaringClass is not a subtype of next's declaringClass
if (current.hasSubstitutedParameters() || original.typeVariables != Binding.NO_TYPE_VARIABLES) {
if (original2.declaringClass != superType) {
// must find inherited method with the same substituted variables
MethodBinding[] superMethods = ((ReferenceBinding) superType).getMethods(original2.selector, argumentTypes.length);
for (int m = 0, l = superMethods.length; m < l; m++) {
if (superMethods[m].original() == original2) {
original2 = superMethods[m];
break;
}
}
}
if (!environment().methodVerifier().isParameterSubsignature(original, original2))
continue nextSpecific; // current does not override next
}
} else if (receiverType != null) { // should not be null if original isAbstract, but be safe
TypeBinding superType = receiverType.findSuperTypeOriginatingFrom(original.declaringClass.erasure());
if (original.declaringClass == superType || !(superType instanceof ReferenceBinding)) {
// keep original
} else {
// must find inherited method with the same substituted variables
MethodBinding[] superMethods = ((ReferenceBinding) superType).getMethods(original.selector, argumentTypes.length);
for (int m = 0, l = superMethods.length; m < l; m++) {
if (superMethods[m].original() == original) {
original = superMethods[m];
break;
}
}
}
superType = receiverType.findSuperTypeOriginatingFrom(original2.declaringClass.erasure());
if (original2.declaringClass == superType || !(superType instanceof ReferenceBinding)) {
// keep original2
} else {
// must find inherited method with the same substituted variables
MethodBinding[] superMethods = ((ReferenceBinding) superType).getMethods(original2.selector, argumentTypes.length);
for (int m = 0, l = superMethods.length; m < l; m++) {
if (superMethods[m].original() == original2) {
original2 = superMethods[m];
break;
}
}
}
if (original.typeVariables != Binding.NO_TYPE_VARIABLES)
original2 = original.computeSubstitutedMethod(original2, environment());
if (original2 == null || !original.areParameterErasuresEqual(original2))
continue nextSpecific; // current does not override next
if (original.returnType != original2.returnType) {
if (current instanceof ParameterizedGenericMethodBinding) {
if (original.returnType.erasure().findSuperTypeOriginatingFrom(original2.returnType.erasure()) == null)
continue nextSpecific;
} else if (!current.returnType.isCompatibleWith(next.returnType)) {
continue nextSpecific;
}
// continue with original 15.12.2.5
}
if (shouldIntersectExceptions && original2.declaringClass.isInterface()) {
if (current.thrownExceptions != next.thrownExceptions) {
if (next.thrownExceptions == Binding.NO_EXCEPTIONS) {
mostSpecificExceptions = Binding.NO_EXCEPTIONS;
} else {
if (mostSpecificExceptions == null) {
mostSpecificExceptions = current.thrownExceptions;
}
int mostSpecificLength = mostSpecificExceptions.length;
int nextLength = next.thrownExceptions.length;
SimpleSet temp = new SimpleSet(mostSpecificLength);
boolean changed = false;
nextException : for (int t = 0; t < mostSpecificLength; t++) {
ReferenceBinding exception = mostSpecificExceptions[t];
for (int s = 0; s < nextLength; s++) {
ReferenceBinding nextException = next.thrownExceptions[s];
if (exception.isCompatibleWith(nextException)) {
temp.add(exception);
continue nextException;
} else if (nextException.isCompatibleWith(exception)) {
temp.add(nextException);
changed = true;
continue nextException;
} else {
changed = true;
}
}
}
if (changed) {
mostSpecificExceptions = temp.elementSize == 0 ? Binding.NO_EXCEPTIONS : new ReferenceBinding[temp.elementSize];
temp.asArray(mostSpecificExceptions);
}
}
}
}
}
}
if (mostSpecificExceptions != null && mostSpecificExceptions != current.thrownExceptions) {
return new MostSpecificExceptionMethodBinding(current, mostSpecificExceptions);
}
return current;
}
}
// if all moreSpecific methods are equal then see if duplicates exist because of substitution
return new ProblemMethodBinding(visible[0], visible[0].selector, visible[0].parameters, ProblemReasons.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 int parameterCompatibilityLevel(MethodBinding method, TypeBinding[] arguments) {
TypeBinding[] parameters = method.parameters;
int paramLength = parameters.length;
int argLength = arguments.length;
if (compilerOptions().sourceLevel < ClassFileConstants.JDK1_5) {
if (paramLength != argLength)
return NOT_COMPATIBLE;
for (int i = 0; i < argLength; i++) {
TypeBinding param = parameters[i];
TypeBinding arg = arguments[i];
if (arg != param && !arg.isCompatibleWith(param))
return NOT_COMPATIBLE;
}
return COMPATIBLE;
}
int level = COMPATIBLE; // no autoboxing or varargs support needed
int lastIndex = argLength;
LookupEnvironment env = environment();
if (method.isVarargs()) {
lastIndex = paramLength - 1;
if (paramLength == argLength) { // accept X or X[] but not X[][]
TypeBinding param = parameters[lastIndex]; // is an ArrayBinding by definition
TypeBinding arg = arguments[lastIndex];
if (param != arg) {
level = parameterCompatibilityLevel(arg, param, env);
if (level == NOT_COMPATIBLE) {
// expect X[], is it called with X
param = ((ArrayBinding) param).elementsType();
if (parameterCompatibilityLevel(arg, param, env) == NOT_COMPATIBLE)
return NOT_COMPATIBLE;
level = VARARGS_COMPATIBLE; // varargs support needed
}
}
} else {
if (paramLength < argLength) { // all remaining argument types must be compatible with the elementsType of varArgType
TypeBinding param = ((ArrayBinding) parameters[lastIndex]).elementsType();
for (int i = lastIndex; i < argLength; i++) {
TypeBinding arg = arguments[i];
if (param != arg && parameterCompatibilityLevel(arg, param, env) == NOT_COMPATIBLE)
return NOT_COMPATIBLE;
}
} else if (lastIndex != argLength) { // can call foo(int i, X ... x) with foo(1) but NOT foo();
return NOT_COMPATIBLE;
}
level = VARARGS_COMPATIBLE; // varargs support needed
}
} else if (paramLength != argLength) {
return NOT_COMPATIBLE;
}
// now compare standard arguments from 0 to lastIndex
for (int i = 0; i < lastIndex; i++) {
TypeBinding param = parameters[i];
TypeBinding arg = arguments[i];
if (arg != param) {
int newLevel = parameterCompatibilityLevel(arg, param, env);
if (newLevel == NOT_COMPATIBLE)
return NOT_COMPATIBLE;
if (newLevel > level)
level = newLevel;
}
}
return level;
}
private int parameterCompatibilityLevel(TypeBinding arg, TypeBinding param, LookupEnvironment env) {
// only called if env.options.sourceLevel >= ClassFileConstants.JDK1_5
if (arg.isCompatibleWith(param))
return COMPATIBLE;
if (arg.isBaseType() != param.isBaseType()) {
TypeBinding convertedType = env.computeBoxingType(arg);
if (convertedType == param || convertedType.isCompatibleWith(param))
return AUTOBOX_COMPATIBLE;
}
return NOT_COMPATIBLE;
}
public abstract ProblemReporter problemReporter();
public final CompilationUnitDeclaration referenceCompilationUnit() {
Scope scope, unitScope = this;
while ((scope = unitScope.parent) != null)
unitScope = scope;
return ((CompilationUnitScope) unitScope).referenceContext;
}
/**
* Returns the nearest reference context, starting from current scope.
* If starting on a class, it will return current class. If starting on unitScope, returns unit.
*/
public ReferenceContext referenceContext() {
Scope current = this;
do {
switch(current.kind) {
case METHOD_SCOPE :
return ((MethodScope) current).referenceContext;
case CLASS_SCOPE :
return ((ClassScope) current).referenceContext;
case COMPILATION_UNIT_SCOPE :
return ((CompilationUnitScope) current).referenceContext;
}
} while ((current = current.parent) != null);
return null;
}
public void deferBoundCheck(TypeReference typeRef) {
if (this.kind == CLASS_SCOPE) {
ClassScope classScope = (ClassScope) this;
if (classScope.deferredBoundChecks == null) {
classScope.deferredBoundChecks = new ArrayList(3);
classScope.deferredBoundChecks.add(typeRef);
} else if (!classScope.deferredBoundChecks.contains(typeRef)) {
classScope.deferredBoundChecks.add(typeRef);
}
}
}
// start position in this scope - for ordering scopes vs. variables
int startIndex() {
return 0;
}
}