blob: 7682fd48b9c4bb20a13156d09588df915ab10d7c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2014 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
* Fraunhofer FIRST - extended API and implementation
* Technical University Berlin - extended API and implementation
* Stephan Herrmann - Contributions for
* bug 186342 - [compiler][null] Using annotations for null checking
* bug 387612 - Unreachable catch block...exception is never thrown from the try
* bug 395002 - Self bound generic class doesn't resolve bounds properly for wildcards for certain parametrisation.
* bug 401456 - Code compiles from javac/intellij, but fails from eclipse
* bug 401271 - StackOverflowError when searching for a methods references
* bug 405706 - Eclipse compiler fails to give compiler error when return type is a inferred generic
* Bug 408441 - Type mismatch using Arrays.asList with 3 or more implementations of an interface with the interface type as the last parameter
* Bug 413958 - Function override returning inherited Generic Type
* Bug 392238 - [1.8][compiler][null] Detect semantically invalid null type annotations
* Bug 416183 - [1.8][compiler][null] Overload resolution fails with null annotations
* Bug 416176 - [1.8][compiler][null] null type annotations cause grief on type variables
* Bug 400874 - [1.8][compiler] Inference infrastructure should evolve to meet JLS8 18.x (Part G of JSR335 spec)
* Bug 424710 - [1.8][compiler] CCE in SingleNameReference.localVariableBinding
* Bug 424205 - [1.8] Cannot infer type for diamond type with lambda on method invocation
* Bug 424415 - [1.8][compiler] Eventual resolution of ReferenceExpression is not seen to be happening.
* Bug 426366 - [1.8][compiler] Type inference doesn't handle multiple candidate target types in outer overload context
* Bug 426290 - [1.8][compiler] Inference + overloading => wrong method resolution ?
* Bug 426589 - [1.8][compiler] Compiler error with generic method/constructor invocation as vargs argument
* Bug 426590 - [1.8][compiler] Compiler error with tenary operator
* Bug 426764 - [1.8] Presence of conditional expression as method argument confuses compiler
* Bug 426998 - [1.8][compiler] method(java.lang.Class, java.lang.String) not applicable for the arguments (java.lang.Class, java.lang.String)
* Bug 423505 - [1.8] Implement "18.5.4 More Specific Method Inference"
* Bug 427196 - [1.8][compiler] Compiler error for method reference to overloaded method
* Bug 427483 - [Java 8] Variables in lambdas sometimes can't be resolved
* Bug 427728 - [1.8] Type Inference rejects calls requiring boxing/unboxing
* Bug 427218 - [1.8][compiler] Verify error varargs + inference
* Bug 426836 - [1.8] special handling for return type in references to method getClass()?
* Bug 427628 - [1.8] regression : The method * is ambiguous for the type *
* Bug 428352 - [1.8][compiler] Resolution errors don't always surface
* Bug 428366 - [1.8] [compiler] The method valueAt(ObservableList<Object>, int) is ambiguous for the type Bindings
* Bug 424728 - [1.8][null] Unexpected error: The nullness annotation 'XXXX' is not applicable at this location
* Bug 428811 - [1.8][compiler] Type witness unnecessarily required
* Bug 429424 - [1.8][inference] Problem inferring type of method's parameter
* Jesper S Moller - Contributions for
* Bug 378674 - "The method can be declared as static" is wrong
* Bug 405066 - [1.8][compiler][codegen] Implement code generation infrastructure for JSR335
* Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for
* Bug 405104 - [1.8][compiler][codegen] Implement support for serializeable lambdas
*******************************************************************************/
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.ast.AbstractMethodDeclaration.WrapperKind;
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.SimpleLookupTable;
import org.eclipse.jdt.internal.compiler.util.SimpleSet;
import org.eclipse.objectteams.otdt.core.compiler.IOTConstants;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.TypeAnchorReference;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.TypeValueParameter;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.Config;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.Dependencies;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.AnchorMapping;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.CallinCalloutScope;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.DependentTypeBinding;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.ITeamAnchor;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.OTClassScope;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.RoleTypeBinding;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.MethodModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.TypeModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.MethodModel.FakeKind;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.RoleTypeCreator;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.TypeAnalyzer;
/**
* OTDT changes:
*
* What: New kind: BINDING_SCOPE for new subclass CallinCalloutScope
*
* What: respect anchor mappings for parameters
* (findExactMethod->parameterCompatibilityLevel)
*
* What: differentiate isInterface() vs. isRegularInterface()
* (findExactMethod)
*
* What: search member types in a role's class part.
* (findMemberType)
*
* What: for confined interfaces don't search methods in java.lang.Object
* (findMethod)
*
* What: for generated _OT$getTeam() methods allow access to synthetic field this$n
* (getField)
*
* What: record referenced teams.
* (getType,getMemberType,getTypeOrPackage)
*
* What: support anchored types
* How: call RoleTypeCreator.resolveAnchoredType()
* (getType(char[][],int)
*
* What: respect import scope of role files.
* (internalGetTypeOrPackage)
*
* What: respect overriding method OTClassScope.referenceCompilationUnit()
*
* What: additional convenience methods:
* + access/check org.objectteams.Team:
* getOrgObjectteamsTeam(), isOrgObjectteamsTeam()
* + referenceType() // search inside->out
* inGeneratedMethod() // within generated methods some checks are relaxed
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
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;
//{ObjectTeams
public final static int BINDING_SCOPE = 5;
//Markus Witte}
/* 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;
/*
* Flags for PGMB.computeCompatibleMethod() and its callers, to request
* which of two levels of inference should be performed (or both).
*/
/** Request Invocation Applicability Inference (18.5.1). */
public static final int APPLICABILITY = 1;
/** Request Invocation Type Inference (18.5.2). */
public static final int INVOCATION_TYPE = 2;
/** Request both {@link #APPLICABILITY} and {@link #INVOCATION_TYPE} inference. */
public static final int FULL_INFERENCE = APPLICABILITY | INVOCATION_TYPE;
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<E> (discarding <E extends Enum<E>) 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 (TypeBinding.notEquals(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 (TypeBinding.notEquals(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 (TypeBinding.notEquals(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 (TypeBinding.notEquals(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 (TypeBinding.notEquals(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 (TypeBinding.notEquals(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 (TypeBinding.notEquals(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 (TypeBinding.notEquals(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, /*@Nullable*/ Scope scope, LookupEnvironment environment) {
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, scope)) { // 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 ++;
} else if (!jType.isCompatibleWith(iType, scope)) {
// avoid creating unsatisfiable intersection types (see https://bugs.eclipse.org/405706):
if (iType.isParameterizedType() && jType.isParameterizedType()) {
// if the wider of the two types (judged by originals) has type variables
// substitute those with their upper bounds and re-check (see https://bugs.eclipse.org/413958):
ParameterizedTypeBinding wideType, narrowType;
if (iType.original().isCompatibleWith(jType.original(), scope)) {
wideType = (ParameterizedTypeBinding) jType;
narrowType = (ParameterizedTypeBinding) iType;
} else if (jType.original().isCompatibleWith(iType.original(), scope)) {
wideType = (ParameterizedTypeBinding) iType;
narrowType = (ParameterizedTypeBinding) jType;
} else {
continue;
}
if (wideType.arguments == null)
continue; // assume we already have an error here
// Skip the following check if inference variables or CaptureBinding18 are involved,
// hopefully during inference a contradictory glb will simply not produce a solution
// (should essentially be detected beforehand in CaptureBinding18.setUpperBounds()):
if (!narrowType.isProperType(false) || !wideType.isProperType(false))
continue;
int numTypeArgs = wideType.arguments.length;
TypeBinding[] bounds = new TypeBinding[numTypeArgs];
for (int k = 0; k < numTypeArgs; k++) {
TypeBinding argument = wideType.arguments[k];
bounds[k] = argument.isTypeVariable() ? ((TypeVariableBinding)argument).upperBound() : argument;
}
ReferenceBinding wideOriginal = (ReferenceBinding) wideType.original();
TypeBinding substitutedWideType =
environment.createParameterizedType(wideOriginal, bounds, wideOriginal.enclosingType());
// if the narrow type is compatible with the substituted wide type, we keep silent,
// substituting type variables with proper types can still satisfy all constraints,
// otherwise ...
if (!narrowType.isCompatibleWith(substitutedWideType, scope)) {
// ... parameterized types are incompatible due to incompatible type arguments => unsatisfiable
return null;
}
}
}
}
}
if (removed == 0) return result;
if (length == removed) return null; // how is this possible ???
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) {
return defaultSubstitutor.substitute(substitution, originalTypes);
}
/**
* Returns a type, where original type was substituted using the receiver
* parameterized type.
* In raw mode (see {@link Substitution#isRawSubstitution()}),
* all parameterized types are converted to raw types.
* Cf. 4.8: "The type of a constructor (8.8), instance method (8.4, 9.4),
* or non-static field (8.3) M of a raw type C that is not inherited from its
* superclasses or superinterfaces is the raw type that corresponds to the erasure
* of its type in the generic declaration corresponding to C."
*/
public static TypeBinding substitute(Substitution substitution, TypeBinding originalType) {
return defaultSubstitutor.substitute(substitution, 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) {
return defaultSubstitutor.substitute(substitution, originalTypes);
}
/** Bridge to non-static implementation in {@link Substitutor}, to make methods overridable. */
private static Substitutor defaultSubstitutor = new Substitutor();
public static class Substitutor {
/**
* Returns an array of types, where original types got substituted given a substitution.
* Only allocate an array if anything is different.
*/
public 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) { //$IDENTITY-COMPARISON$
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 (see {@link Substitution#isRawSubstitution()}),
* all parameterized types are converted to raw types.
* Cf. 4.8: "The type of a constructor (8.8), instance method (8.4, 9.4),
* or non-static field (8.3) M of a raw type C that is not inherited from its
* superclasses or superinterfaces is the raw type that corresponds to the erasure
* of its type in the generic declaration corresponding to C."
*/
public TypeBinding substitute(Substitution substitution, TypeBinding originalType) {
if (originalType == null) return null;
//{ObjectTeams: unwrap type variable from dependent type?
if (DependentTypeBinding.isDependentTypeVariable(originalType))
originalType = ((DependentTypeBinding)originalType).type;
// SH}
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);
if (isMemberTypeOfRaw(originalType, substitutedEnclosing))
return originalParameterizedType.environment.createRawType(
originalParameterizedType.genericType(), substitutedEnclosing, originalType.getTypeAnnotations());
}
TypeBinding[] originalArguments = originalParameterizedType.arguments;
TypeBinding[] substitutedArguments = originalArguments;
if (originalArguments != null) {
if (substitution.isRawSubstitution()) {
return originalParameterizedType.environment.createRawType(originalParameterizedType.genericType(), substitutedEnclosing, originalType.getTypeAnnotations());
}
substitutedArguments = substitute(substitution, originalArguments);
}
if (substitutedArguments != originalArguments || substitutedEnclosing != originalEnclosing) { //$IDENTITY-COMPARISON$
//{ObjectTeams: combination role & parameterized:
if (originalParameterizedType.isRoleType()) {
DependentTypeBinding originalRole = (DependentTypeBinding) originalParameterizedType;
return originalParameterizedType.environment.createParameterizedType(
originalParameterizedType.genericType(), substitutedArguments, originalRole._teamAnchor, originalRole._valueParamPosition, substitutedEnclosing, Binding.NO_ANNOTATIONS);
}
// SH}
return originalParameterizedType.environment.createParameterizedType(
originalParameterizedType.genericType(), substitutedArguments, substitutedEnclosing, originalType.getTypeAnnotations());
}
break;
case Binding.ARRAY_TYPE:
ArrayBinding originalArrayType = (ArrayBinding) originalType;
TypeBinding originalLeafComponentType = originalArrayType.leafComponentType;
TypeBinding substitute = substitute(substitution, originalLeafComponentType); // substitute could itself be array type, TODO(Srikanth): need a test case.
if (substitute != originalLeafComponentType) { //$IDENTITY-COMPARISON$
return originalArrayType.environment.createArrayType(substitute.leafComponentType(), substitute.dimensions() + originalType.dimensions(), originalType.getTypeAnnotations());
}
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) { //$IDENTITY-COMPARISON$
if (originalOtherBounds != null) {
/* https://bugs.eclipse.org/bugs/show_bug.cgi?id=347145: the constituent intersecting types have changed
in the last round of substitution. Reevaluate the composite intersection type, as there is a possibility
of the intersection collapsing into one of the constituents, the other being fully subsumed.
*/
TypeBinding [] bounds = new TypeBinding[1 + substitutedOtherBounds.length];
bounds[0] = substitutedBound;
System.arraycopy(substitutedOtherBounds, 0, bounds, 1, substitutedOtherBounds.length);
TypeBinding[] glb = Scope.greaterLowerBound(bounds, null, substitution.environment()); // re-evaluate
if (glb != null && glb != bounds) {
substitutedBound = glb[0];
if (glb.length == 1) {
substitutedOtherBounds = null;
} else {
System.arraycopy(glb, 1, substitutedOtherBounds = new TypeBinding[glb.length - 1], 0, glb.length - 1);
}
}
}
return wildcard.environment.createWildcard(wildcard.genericType, wildcard.rank, substitutedBound, substitutedOtherBounds, wildcard.boundKind, wildcard.getTypeAnnotations());
}
}
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);
if (isMemberTypeOfRaw(originalType, substitutedEnclosing))
return substitution.environment().createRawType(originalReferenceType, substitutedEnclosing, originalType.getTypeAnnotations());
}
// treat as if parameterized with its type variables (non generic type gets 'null' arguments)
if (substitutedEnclosing != originalEnclosing) { //$IDENTITY-COMPARISON$
return substitution.isRawSubstitution()
? substitution.environment().createRawType(originalReferenceType, substitutedEnclosing, originalType.getTypeAnnotations())
: substitution.environment().createParameterizedType(originalReferenceType, null, substitutedEnclosing, originalType.getTypeAnnotations());
}
break;
case Binding.GENERIC_TYPE:
originalReferenceType = (ReferenceBinding) originalType;
originalEnclosing = originalType.enclosingType();
substitutedEnclosing = originalEnclosing;
if (originalEnclosing != null) {
substitutedEnclosing = (ReferenceBinding) (originalType.isStatic() ? substitution.environment().convertToRawType(originalEnclosing, true) :
(ReferenceBinding) substitute(substitution, originalEnclosing));
if (isMemberTypeOfRaw(originalType, substitutedEnclosing))
return substitution.environment().createRawType(originalReferenceType, substitutedEnclosing, originalType.getTypeAnnotations());
}
if (substitution.isRawSubstitution()) {
return substitution.environment().createRawType(originalReferenceType, substitutedEnclosing, originalType.getTypeAnnotations());
}
// 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, originalType.getTypeAnnotations());
}
return originalType;
}
private static boolean isMemberTypeOfRaw(TypeBinding originalType, ReferenceBinding substitutedEnclosing) {
// 4.8:
// "a raw type is defined to be one of:
// ...
// * A non-static member type of a raw type R that is not
// inherited from a superclass or superinterface of R."
// Due to staticness, e.g., Map.Entry<String,Object> is *not* considered as a raw type
return (substitutedEnclosing != null && substitutedEnclosing.isRawType())
&& ((originalType instanceof ReferenceBinding) && !((ReferenceBinding)originalType).isStatic());
}
/**
* Returns an array of types, where original types got substituted given a substitution.
* Only allocate an array if anything is different.
*/
public 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) { //$IDENTITY-COMPARISON$
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() || type.kind() == Binding.POLY_TYPE)
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;
}
public boolean isLambdaScope() {
return false;
}
public boolean isLambdaSubscope() {
for (Scope scope = this; scope != null; scope = scope.parent) {
switch (scope.kind) {
case BLOCK_SCOPE:
continue;
case METHOD_SCOPE:
return scope.isLambdaScope();
default:
return false;
}
}
return false;
}
/**
* 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 substituted 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, int inferenceLevel) {
return computeCompatibleMethod(method, arguments, invocationSite, inferenceLevel, false);
}
/**
* Internal use only
* Given a method, returns null if arguments cannot be converted to parameters.
* Will answer a substituted 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, int inferenceLevel, boolean tiebreakingVarargsMethods)
{
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
CompilerOptions compilerOptions = this.compilerOptions();
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=330435, inference should kick in only at source 1.5+
if (typeVariables != Binding.NO_TYPE_VARIABLES && compilerOptions.sourceLevel >= ClassFileConstants.JDK1_5) { // 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;
else // ensure that computeCompatibleMethod() below can update arguments without harming our caller: (TODO: always copy before the loop? only in 1.8?)
System.arraycopy(arguments, 0, arguments=new TypeBinding[argLength], 0, argLength);
method = ParameterizedGenericMethodBinding.computeCompatibleMethod(method, arguments, this, invocationSite, inferenceLevel);
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 <String>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 (tiebreakingVarargsMethods) {
if (CompilerOptions.tolerateIllegalAmbiguousVarargsInvocation && compilerOptions.complianceLevel < ClassFileConstants.JDK1_7)
tiebreakingVarargsMethods = false;
}
if ((parameterCompatibilityLevel18(method, arguments, tiebreakingVarargsMethods, invocationSite)) > NOT_COMPATIBLE) {
if ((method.tagBits & TagBits.AnnotationPolymorphicSignature) != 0) {
// generate polymorphic method
return this.environment().createPolymorphicMethod(method, arguments);
}
return method;
}
// if method is generic and type arguments have been supplied, only then answer a problem
// of ParameterizedMethodTypeMismatch, else a non-generic method was invoked using type arguments
// in which case this problem category will be bogus
if (genericTypeArguments != null && typeVariables != Binding.NO_TYPE_VARIABLES)
return new ProblemMethodBinding(method, method.selector, arguments, ProblemReasons.ParameterizedMethodTypeMismatch);
return null; // incompatible
}
private int parameterCompatibilityLevel18(MethodBinding method, TypeBinding[] arguments, boolean tiebreakingVarargsMethods, InvocationSite site) {
jdk18checks:
if (site instanceof Invocation && compilerOptions().complianceLevel >= ClassFileConstants.JDK1_8) {
Invocation invocation = (Invocation) site;
if (method instanceof ParameterizedGenericMethodBinding) {
InferenceContext18 infCtx = invocation.getInferenceContext((ParameterizedGenericMethodBinding) method);
if (infCtx != null) {
// inference is responsible, no need to recheck
if (infCtx.isVarArgs())
return VARARGS_COMPATIBLE;
return COMPATIBLE;
}
}
// collect inner invocations where the outer did not involve any inference:
Expression[] invocationArguments = invocation.arguments();
if (invocationArguments != null) {
InnerInferenceHelper innerInferenceHelper = invocation.innerInferenceHelper();
int argLen = invocationArguments.length;
boolean isVarArgs[] = new boolean[1]; // emulate an in-out parameter for compatibilityLevel18FromInner(..)
isVarArgs[0] = method.isVarargs() && argLen != method.parameters.length; // if same lengths, isVarArgs can still be updated below
int level = COMPATIBLE;
//{ObjectTeams: integrate anchor mapping
TypeBinding[] parameters = AnchorMapping.instantiateParameters(this, method.parameters, method);
// SH}
for (int i = 0; i < argLen; i++) {
TypeBinding argumentType = i < arguments.length ? arguments[i] : null; // length mismatch may happen from CodeSnippetMessageSend.resolveType() in the if (argHasError) block.
//{ObjectTeams: new arg:
/* orig:
int nextLevel = compatibilityLevel18FromInner(method, innerInferenceHelper, invocationArguments[i], argumentType, argLen, i, isVarArgs);
:giro */
int nextLevel = compatibilityLevel18FromInner(method, parameters, innerInferenceHelper, invocationArguments[i], argumentType, argLen, i, isVarArgs);
// SH}
if (nextLevel == NOT_COMPATIBLE)
return nextLevel;
if (nextLevel == -2)
break jdk18checks;
level = Math.max(level, nextLevel);
}
//{ObjectTeams:
AnchorMapping.storeInstantiatedParameters(method, parameters);
// SH}
return level; // neither NOT_COMPATIBLE nor unknown(-2) seen
}
}
// fall back to old method:
return parameterCompatibilityLevel(method, arguments, tiebreakingVarargsMethods);
}
//{ObjectTeams: new arg:
/* orig:
private int compatibilityLevel18FromInner(MethodBinding method, InnerInferenceHelper innerInferenceHelper, Expression invocArg, TypeBinding argType, int argLen, int i, boolean[] isVarArgs)
:giro */
private int compatibilityLevel18FromInner(MethodBinding method, TypeBinding[] parameters, InnerInferenceHelper innerInferenceHelper, Expression invocArg, TypeBinding argType, int argLen, int i, boolean[] isVarArgs)
// SH}
{
int compatible = isVarArgs[0] ? VARARGS_COMPATIBLE : COMPATIBLE;
TypeBinding resolvedType = invocArg.resolvedType;
//{ObjectTeams: parameters have been extracted to arg:
/* orig:
TypeBinding targetType = InferenceContext18.getParameter(method.parameters, i, isVarArgs[0]);
:giro */
TypeBinding targetType = InferenceContext18.getParameter(parameters, i, isVarArgs[0]);
// orig:
if (!isVarArgs[0] && shouldTryVarargs(method, resolvedType, targetType)) {
isVarArgs[0] = true;
/*
targetType = InferenceContext18.getParameter(method.parameters, i, true);
:giro */
targetType = InferenceContext18.getParameter(parameters, i, true);
// SH}
}
if (targetType == null)
return NOT_COMPATIBLE; // mismatching number of args or other severe problem inside method binding
int level = -2; // don't know
if (invocArg instanceof Invocation && resolvedType != null) {
Invocation innerPoly = (Invocation) invocArg;
level = parameterCompatibilityLevel(resolvedType, targetType);
if (level != NOT_COMPATIBLE) {
if (TypeBinding.notEquals(argType, resolvedType) && innerInferenceHelper != null)
innerInferenceHelper.registerInnerResult(method, resolvedType, argLen, i);
return Math.max(compatible, level);
} else {
MethodBinding innerBinding = innerPoly.binding(null, false, null); // 1. try without update
if (innerBinding instanceof ParameterizedGenericMethodBinding) {
ParameterizedGenericMethodBinding innerParameterized = (ParameterizedGenericMethodBinding) innerBinding;
InferenceContext18 infCtx18 = innerPoly.getInferenceContext(innerParameterized);
if (infCtx18 != null && !infCtx18.hasResultFor(targetType)) {
// not detected as compatible, because inference still needs to complete?
invocArg.setExpectedType(targetType);
MethodBinding solution = infCtx18.inferInvocationType(innerPoly, innerParameterized);
if (solution != null && solution.isValidBinding()) {
if (innerPoly.updateBindings(solution, targetType)) {
if (innerInferenceHelper != null)
innerInferenceHelper.registerInnerResult(method, invocArg.resolvedType, argLen, i);
}
if (solution.returnType != null) {
level = parameterCompatibilityLevel(solution.returnType, targetType);
if (level != NOT_COMPATIBLE)
return Math.max(compatible, level);
}
}
invocArg.setExpectedType(null);
return NOT_COMPATIBLE;
} else if (innerPoly instanceof AllocationExpression) {
// not detected as compatible, because its a diamond whose type hasn't yet been inferred?
TypeBinding[] typeArguments = resolvedType.typeArguments();
if (typeArguments != null && typeArguments.length == 0) {
AllocationExpression alloc = (AllocationExpression) innerPoly;
if ((alloc.type.bits & ASTNode.IsDiamond) != 0) {
// not-yet-inferred diamond: erasure compatibility should suffice, detail will be checked using inference
if (resolvedType.isCompatibleWith(targetType.erasure(), this))
return compatible;
return NOT_COMPATIBLE;
}
}
}
} else if (innerPoly instanceof AllocationExpression) {
MethodBinding updatedMethod = innerPoly.binding(targetType, false, null); // 2. try with updating
if (updatedMethod != innerBinding && updatedMethod != null) {
if (updatedMethod.isValidBinding()) {
if (updatedMethod.declaringClass.isCompatibleWith(targetType))
return compatible;
return NOT_COMPATIBLE;
} else if (updatedMethod.problemId() == ProblemReasons.Ambiguous) {
level = -2; // neither good nor bad, answer "unknown"
}
}
}
}
} else if (invocArg.isPolyExpression()) {
if (invocArg instanceof ConditionalExpression) {
ConditionalExpression ce = (ConditionalExpression) invocArg;
//{ObjectTeams: new arg:
/* orig:
int level1 = compatibilityLevel18FromInner(method, innerInferenceHelper, ce.valueIfTrue, argType, argLen, i, isVarArgs);
:giro */
int level1 = compatibilityLevel18FromInner(method, parameters, innerInferenceHelper, ce.valueIfTrue, argType, argLen, i, isVarArgs);
// orig:
if (level1 == NOT_COMPATIBLE)
return NOT_COMPATIBLE;
/*
int level2 = compatibilityLevel18FromInner(method, innerInferenceHelper, ce.valueIfFalse, argType, argLen, i, isVarArgs);
:giro */
int level2 = compatibilityLevel18FromInner(method, parameters, innerInferenceHelper, ce.valueIfFalse, argType, argLen, i, isVarArgs);
// SH}
if (level2 == NOT_COMPATIBLE)
return NOT_COMPATIBLE;
return Math.max(level1, level2);
}
// LE or RE:
if (invocArg.isCompatibleWith(targetType, this))
return COMPATIBLE;
if (!isVarArgs[0] && method.isVarargs()) { // can't use shouldTryVarargs without a resolvedType, so just try it:
isVarArgs[0] = true;
//{ObjectTeams: parameters have been extracted to new arg:
/* orig:
targetType = InferenceContext18.getParameter(method.parameters, i, true);
:giro */
targetType = InferenceContext18.getParameter(parameters, i, true);
// SH}
if (targetType != null && invocArg.isCompatibleWith(targetType, this))
return VARARGS_COMPATIBLE;
}
return NOT_COMPATIBLE;
} else if (resolvedType != null && resolvedType.isValidBinding()) {
// need to handle "normal" expressions too, since mixed poly/standalone argument lists must be fully analyzed.
return parameterCompatibilityLevel(resolvedType, targetType);
}
return level;
}
private boolean shouldTryVarargs(MethodBinding method, TypeBinding resolvedType, TypeBinding targetType) {
if (!method.isVarargs())
return false;
if (targetType == null)
return true; // off range
if (targetType.isArrayType() && resolvedType != null && !resolvedType.isCompatibleWith(targetType, this))
return true; // not a direct match but hope to improve
return false;
}
/**
* Connect type variable supertypes, and returns true if no problem was detected
* @param typeParameters
* @param checkForErasedCandidateCollisions
*/
protected boolean connectTypeVariables(TypeParameter[] typeParameters, boolean checkForErasedCandidateCollisions) {
/* https://bugs.eclipse.org/bugs/show_bug.cgi?id=305259 - We used to not bother with connecting
type variables if source level is < 1.5. This creates problems in the reconciler if a 1.4
project references the generified API of a 1.5 project. The "current" project's source
level cannot decide this question for some other project. Now, if we see type parameters
at all, we assume that the concerned java element has some legitimate business with them.
*/
if (typeParameters == null || typeParameters.length == 0) return true;
Map invocations = new HashMap(2);
boolean noProblems = true;
// preinitializing each type variable
int paramLength = typeParameters.length;
for (int i = 0; i < paramLength; i++) {
TypeParameter typeParameter = typeParameters[i];
//{ObjectTeams: don't expect a TypeVariableBinding for a TypeValueParameter:
if (typeParameter instanceof TypeValueParameter)
continue;
// SH}
TypeVariableBinding typeVariable = typeParameter.binding;
if (typeVariable == null) return false;
typeVariable.setSuperClass(getJavaLangObject());
typeVariable.setSuperInterfaces(Binding.NO_SUPERINTERFACES);
// set firstBound to the binding of the first explicit bound in parameter declaration
typeVariable.setFirstBound(null); // first bound used to compute erasure
}
nextVariable: for (int i = 0; i < paramLength; i++) {
TypeParameter typeParameter = typeParameters[i];
//{ObjectTeams: don't expect a TypeVariableBinding for a TypeValueParameter:
if (typeParameter instanceof TypeValueParameter)
continue; // FIXME(SH): what checking is needed here?
if (typeParameter.type instanceof TypeAnchorReference)
continue;
// SH}
TypeVariableBinding typeVariable = typeParameter.binding;
TypeReference typeRef = typeParameter.type;
if (typeRef == null)
continue nextVariable;
boolean isFirstBoundTypeVariable = false;
TypeBinding superType = this.kind == METHOD_SCOPE
//{ObjectTeams: mapping scope:
||this.kind == BINDING_SCOPE
// SH}
? 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
}
}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=335751
if (compilerOptions().complianceLevel > ClassFileConstants.JDK1_6) {
if (typeVariable.rank >= varSuperType.rank && varSuperType.declaringElement == typeVariable.declaringElement) {
SimpleSet set = new SimpleSet(typeParameters.length);
set.add(typeVariable);
ReferenceBinding superBinding = varSuperType;
while (superBinding instanceof TypeVariableBinding) {
if (set.includes(superBinding)) {
problemReporter().hierarchyCircularity(typeVariable, varSuperType, typeRef);
typeVariable.tagBits |= TagBits.HierarchyHasProblems;
break firstBound; // do not keep first bound
} else {
set.add(superBinding);
superBinding = ((TypeVariableBinding)superBinding).superclass;
}
}
}
}
break;
default :
if (((ReferenceBinding) superType).isFinal()) {
//{ObjectTeams: role type may indeed be final:
if (!typeParameter.hasBaseBound())
// SH}
problemReporter().finalVariableBound(typeVariable, typeRef);
}
break;
}
ReferenceBinding superRefType = (ReferenceBinding) superType;
//{ObjectTeams: <B base R>:
if ((typeRef.bits & ASTNode.IsRoleType) != 0) {
if (superRefType.isRole()) {
typeVariable.roletype = superRefType;
} else {
problemReporter().roleBoundIsNotRole(typeRef, superRefType);
typeVariable.tagBits |= TagBits.HierarchyHasProblems;
}
superRefType = getJavaLangObject(); // no assumption about base type if constrained by role
} else
// SH}
if (!superType.isInterface()) {
typeVariable.setSuperClass(superRefType);
} else {
typeVariable.setSuperInterfaces(new ReferenceBinding[] {superRefType});
}
typeVariable.tagBits |= superType.tagBits & TagBits.ContainsNestedTypeReferences;
typeVariable.setFirstBound(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 && TypeBinding.equalsEquals(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 (TypeBinding.equalsEquals(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.setSuperInterfaces(new ReferenceBinding[size + 1]), 0, size);
typeVariable.superInterfaces[size] = superRefType;
}
}
}
noProblems &= (typeVariable.tagBits & TagBits.HierarchyHasProblems) == 0;
}
// after bounds have been resolved we're ready for resolving the type parameter itself,
// which includes resolving/evaluating type annotations and checking for inconsistencies
for (int i = 0; i < paramLength; i++)
resolveTypeParameter(typeParameters[i]);
return noProblems;
}
public ArrayBinding createArrayType(TypeBinding type, int dimension) {
return createArrayType(type, dimension, Binding.NO_ANNOTATIONS);
}
public ArrayBinding createArrayType(TypeBinding type, int dimension, AnnotationBinding[] annotations) {
if (type.isValidBinding())
return environment().createArrayType(type, dimension, annotations);
// do not cache obvious invalid types
return new ArrayBinding(type, dimension, environment());
}
public TypeVariableBinding[] createTypeVariables(TypeParameter[] typeParameters, Binding declaringElement) {
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=324850, If they exist at all, process type parameters irrespective of source level.
if (typeParameters == null || typeParameters.length == 0)
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;
if ((typeParameter.bits & ASTNode.HasTypeAnnotations) != 0) {
switch(declaringElement.kind()) {
case Binding.METHOD :
MethodBinding methodBinding = (MethodBinding) declaringElement;
AbstractMethodDeclaration sourceMethod = methodBinding.sourceMethod();
if (sourceMethod != null) {
sourceMethod.bits |= ASTNode.HasTypeAnnotations;
}
break;
case Binding.TYPE :
if (declaringElement instanceof SourceTypeBinding) {
SourceTypeBinding sourceTypeBinding = (SourceTypeBinding) declaringElement;
TypeDeclaration typeDeclaration = sourceTypeBinding.scope.referenceContext;
if (typeDeclaration != null) {
typeDeclaration.bits |= ASTNode.HasTypeAnnotations;
}
}
}
}
// 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;
}
void resolveTypeParameter(TypeParameter typeParameter) {
// valid only for ClassScope and MethodScope
}
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
}
public final MethodScope enclosingLambdaScope() {
Scope scope = this;
while ((scope = scope.parent) != null) {
if (scope instanceof MethodScope) {
MethodScope methodScope = (MethodScope) scope;
if (methodScope.referenceContext instanceof LambdaExpression)
return methodScope;
}
}
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 (since maybe missing default abstract methods). "Default abstract methods" are methods that used to be emitted into
abstract classes for unimplemented interface methods at JDK 1.1 time frame. See SourceTypeBinding.addDefaultAbstractMethods()
See also https://bugs.eclipse.org/bugs/show_bug.cgi?id=174588 for details of problem addressed here. Problem was in the method call in the
*abstract* class. Unless the interface methods are looked up, we will emit code that results in infinite recursion.
*/
protected MethodBinding findDefaultAbstractMethod(
ReferenceBinding receiverType,
char[] selector,
TypeBinding[] argumentTypes,
InvocationSite invocationSite,
ReferenceBinding classHierarchyStart,
ObjectVector found,
MethodBinding concreteMatch) {
int startFoundSize = found.size;
final boolean sourceLevel18 = this.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_8;
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
final MethodVerifier methodVerifier = environment().methodVerifier();
next:
for (int i = startFoundSize; i < foundSize; i++) {
MethodBinding methodBinding = (MethodBinding) found.elementAt(i);
MethodBinding compatibleMethod = computeCompatibleMethod(methodBinding, argumentTypes, invocationSite, APPLICABILITY);
if (compatibleMethod != null) {
if (compatibleMethod.isValidBinding()) {
if (concreteMatch != null) {
if (methodVerifier.areMethodsCompatible(concreteMatch, compatibleMethod))
continue; // can skip this method since concreteMatch overrides it
}
if (sourceLevel18) {
for (int j = 0; j < startFoundSize; j++) {
MethodBinding classMethod = (MethodBinding) found.elementAt(j);
if (classMethod != null && methodVerifier.areMethodsCompatible(classMethod, compatibleMethod))
continue next; // can skip this method since classMethod 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];
}
// 1.8: Give inference a chance to perform outstanding tasks (18.5.2):
concreteMatch = inferInvocationType(invocationSite, concreteMatch, argumentTypes);
compilationUnitScope().recordTypeReferences(concreteMatch.thrownExceptions);
//{ObjectTeams: visibility of role ifc methods:
if (concreteMatch.isValidBinding() && concreteMatch.declaringClass.isSynthInterface())
if (!concreteMatch.canBeSeenBy(receiverType, invocationSite, this))
concreteMatch = new ProblemMethodBinding(concreteMatch, selector, argumentTypes, ProblemReasons.NotVisible);
// SH}
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) {
if (memberType.canBeSeenBy(getCurrentPackage())) {
return memberType;
}
// maybe some type in the compilation unit is extending some class in some package
// and the selection is for some protected inner class of that superclass
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=235658
if (this instanceof CompilationUnitScope) {
TypeDeclaration[] types = ((CompilationUnitScope)this).referenceContext.types;
if (types != null) {
for (int i = 0, max = types.length; i < max; i++) {
if (memberType.canBeSeenBy(enclosingType, types[i].binding)) {
return memberType;
}
}
}
}
} else if (memberType.canBeSeenBy(enclosingType, enclosingReceiverType)) {
return memberType;
}
return new ProblemReferenceBinding(new char[][]{typeName}, memberType, ProblemReasons.NotVisible);
}
return null;
}
//{ObjectTeams: field used only by MethodBinding.canBeSeen() variant invoked from findExactMethod:
Scope origImplicitScope = null;
// SH}
// 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: <S extends A> void foo() {} and <N extends B> N foo() { return null; }
// or find an inherited method when the exact match is to a bridge method
//{ObjectTeams: the following check respects an AnchorMapping if set:
if (exactMethod.isValidBinding() && AnchorMapping.isDefined())
if (parameterCompatibilityLevel(exactMethod, argumentTypes) == NOT_COMPATIBLE)
return null; // parameter instantiation might produce incompatibility
// SH}
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 (exactMethod.canBeSeenBy(receiverType, invocationSite, this)) {
if (argumentTypes == Binding.NO_PARAMETERS
&& CharOperation.equals(selector, TypeConstants.GETCLASS)
&& exactMethod.returnType.isParameterizedType()/*1.5*/) {
return environment().createGetClassMethod(receiverType, exactMethod, this);
}
// targeting a generic method could find an exact match with variable return type
if (invocationSite.genericTypeArguments() != null) {
// computeCompatibleMethod(..) will return a PolymorphicMethodBinding if needed
exactMethod = computeCompatibleMethod(exactMethod, argumentTypes, invocationSite, FULL_INFERENCE);
} else if ((exactMethod.tagBits & TagBits.AnnotationPolymorphicSignature) != 0) {
// generate polymorphic method
return this.environment().createPolymorphicMethod(exactMethod, argumentTypes);
}
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) {
return findField(receiverType, fieldName, invocationSite, needResolve, false);
}
// 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 the parameter invisibleFieldsOk is true, visibility checks have not been run on
any returned fields. The caller needs to apply these checks as needed. Otherwise,
If no visible field is discovered, null is answered.
*/
public FieldBinding findField(TypeBinding receiverType, char[] fieldName, InvocationSite invocationSite, boolean needResolve, boolean invisibleFieldsOk) {
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))
//{ObjectTeams: base class decapsulation?
if (!allowsBaseclassDecapsulation(invocationSite, (ReferenceBinding)leafType))
// SH}
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))
//{ObjectTeams: base class decapsulation?
if (!allowsBaseclassDecapsulation(invocationSite, currentType))
//SH}
return new ProblemFieldBinding(currentType, fieldName, ProblemReasons.ReceiverTypeNotVisible);
currentType.initializeForStaticImports();
FieldBinding field = currentType.getField(fieldName, needResolve);
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=316456
boolean insideTypeAnnotations = this instanceof MethodScope && ((MethodScope) this).insideTypeAnnotation;
if (field != null) {
if (invisibleFieldsOk) {
return field;
}
if (invocationSite == null || insideTypeAnnotations
? 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 (TypeBinding.equalsEquals(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) {
if (invisibleFieldsOk) {
return field;
}
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 (invisibleFieldsOk) {
return field;
}
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 (TypeBinding.equalsEquals(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) {
//{ObjectTeams: need class part if role
if (enclosingType.isRole()) {
ReferenceBinding classPart = enclosingType.roleModel.getClassPartBinding();
if (classPart != null)
enclosingType = classPart;
}
// SH}
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 (TypeBinding.equalsEquals(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 (TypeBinding.equalsEquals(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, boolean inStaticContext) {
MethodBinding method = findMethod0(receiverType, selector, argumentTypes, invocationSite, inStaticContext);
if (method != null && method.isValidBinding() && method.isVarargs()) {
TypeBinding elementType = method.parameters[method.parameters.length - 1].leafComponentType();
if (elementType instanceof ReferenceBinding) {
if (!((ReferenceBinding) elementType).canBeSeenBy(this)) {
return new ProblemMethodBinding(method, method.selector, invocationSite.genericTypeArguments(), ProblemReasons.VarargsElementTypeNotVisible);
}
}
}
return method;
}
public MethodBinding findMethod0(ReferenceBinding receiverType, char[] selector, TypeBinding[] argumentTypes, InvocationSite invocationSite, boolean inStaticContext) {
//{ObjectTeams: access to private method from "within" (need class part)?
if ( RoleTypeBinding.isRoleWithoutExplicitAnchor(receiverType)
|| (receiverType.isRole() && invocationSite.isTypeAccess()))
{
ReferenceBinding enclosingType = this.enclosingSourceType();
if (isMethodMappingWrapper()) {
// retrieve the actual enclosing type of the mapping declaration:
for (ReferenceBinding declaringRole : MethodModel.getRoleHandledByThisWrapperMethod(methodScope().referenceMethod())) {
if (declaringRole != null)
enclosingType = declaringRole;
receiverType = transferTypeArguments(receiverType, enclosingType, receiverType.getRealClass());
MethodBinding method = internalFindMethod(receiverType, selector, argumentTypes, invocationSite, inStaticContext);
if (method != null && method.isValidBinding())
return method;
}
}
receiverType = transferTypeArguments(receiverType, enclosingType, receiverType.getRealClass());
}
return internalFindMethod(receiverType, selector, argumentTypes, invocationSite, inStaticContext);
}
private ReferenceBinding transferTypeArguments(ReferenceBinding receiverType, ReferenceBinding enclosingType, ReferenceBinding other) {
ReferenceBinding currentIfcType = receiverType.getRealType();
while (enclosingType != null) {
if (TypeBinding.equalsEquals(enclosingType.getRealType(), currentIfcType)) {
return receiverType.transferTypeArguments(receiverType.getRealClass());
}
enclosingType = enclosingType.enclosingType();
}
return receiverType;
}
private MethodBinding internalFindMethod(ReferenceBinding receiverType, char[] selector, TypeBinding[] argumentTypes, InvocationSite invocationSite, boolean inStaticContext) {
//SH}
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);
//{ObjectTeams: confined types don't proceed to java.lang.Object:
if (!(TypeAnalyzer.isConfined(receiverType)))
// SH}
currentType = getJavaLangObject();
}
// superclass lookup
long complianceLevel = compilerOptions().complianceLevel;
boolean isCompliant14 = complianceLevel >= ClassFileConstants.JDK1_4;
boolean isCompliant15 = complianceLevel >= ClassFileConstants.JDK1_5;
boolean soureLevel18 = compilerOptions().sourceLevel >= ClassFileConstants.JDK1_8;
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);
MethodBinding matchingOriginal = matchingMethod.original();
MethodBinding currentOriginal = matchingOriginal.findOriginalInheritedMethod(currentMethod);
if (currentOriginal != null && verifier.isParameterSubsignature(matchingOriginal, currentOriginal)) {
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 = soureLevel18 || (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, APPLICABILITY);
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);
// ==== 1.8: Finalize type inference of generic methods: ====
MethodBinding improved = inferInvocationType(invocationSite, compatibleMethod, argumentTypes);
if (improved.isValidBinding()) {
compatibleMethod = improved;
} else {
problemMethod = improved;
continue;
}
// ==== 1.8 ====
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) {
if (soureLevel18 && foundSize > 0 && interfaceMethod.isVarargs() && interfaceMethod instanceof ParameterizedGenericMethodBinding) {
MethodBinding original = interfaceMethod.original();
for (int i = 0; i < foundSize; i++) {
MethodBinding classMethod = (MethodBinding) found.elementAt(i);
if (!classMethod.isAbstract()) { // this check shouldn't matter, but to compatible with javac...
MethodBinding substitute = verifier.computeSubstituteMethod(original, classMethod);
if (substitute != null && verifier.isSubstituteParameterSubsignature(classMethod, substitute))
return new ProblemMethodBinding(interfaceMethod, selector, argumentTypes, ProblemReasons.ApplicableMethodOverriddenByInapplicable);
}
}
}
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 (TypeBinding.equalsEquals(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;
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++;
}
//{ObjectTeams: check if decapsulation is allowed:
else if (invocationSite instanceof Expression) {
if (((Expression)invocationSite).getBaseclassDecapsulation().isAllowed()) {
methodBinding = new ProblemMethodBinding(methodBinding, selector, argumentTypes, ProblemReasons.NotVisible);
if (visiblesCount != i) {
candidates[i] = null;
candidates[visiblesCount] = methodBinding;
} else {
candidates[i] = methodBinding;
}
visiblesCount++;
}
}
// SH}
}
switch (visiblesCount) {
case 0 :
MethodBinding interfaceMethod =
findDefaultAbstractMethod(receiverType, selector, argumentTypes, invocationSite, classHierarchyStart, found, null);
if (interfaceMethod != null) return interfaceMethod;
MethodBinding candidate = candidates[0];
return new ProblemMethodBinding(candidates[0], candidates[0].selector, candidates[0].parameters,
candidate.isStatic() && candidate.declaringClass.isInterface() ? ProblemReasons.NonStaticOrAlienTypeReceiver : ProblemReasons.NotVisible);
case 1 :
if (searchForDefaultAbstractMethod)
return findDefaultAbstractMethod(receiverType, selector, argumentTypes, invocationSite, classHierarchyStart, found, candidates[0]);
// 1.8: Give inference a chance to perform outstanding tasks (18.5.2):
candidate = inferInvocationType(invocationSite, candidates[0], argumentTypes);
unitScope.recordTypeReferences(candidate.thrownExceptions);
return candidate;
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.hasSubstitutedParameters()) {
for (int j = i + 1; j < visiblesCount; j++) {
MethodBinding otherCandidate = candidates[j];
if (otherCandidate.hasSubstitutedParameters()) {
if (otherCandidate == candidate
|| (TypeBinding.equalsEquals(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 environment().createGetClassMethod(receiverType, methodBinding, this);
}
break;
}
}
if (methodBinding.canBeSeenBy(receiverType, invocationSite, this))
return methodBinding;
}
methodBinding = findMethod(object, selector, argumentTypes, invocationSite, false);
if (methodBinding == null)
return new ProblemMethodBinding(selector, argumentTypes, ProblemReasons.NotFound);
return methodBinding;
}
protected void findMethodInSuperInterfaces(ReferenceBinding receiverType, char[] selector, ObjectVector found, InvocationSite invocationSite) {
ReferenceBinding currentType = receiverType;
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;
next : for (int c = 0, l = currentMethods.length; c < l; c++) {
MethodBinding current = currentMethods[c];
//{ObjectTeams: during this search decapsulation trumps inaccessibility:
boolean decapsulationAllowed = invocationSite instanceof Expression && ((Expression)invocationSite).getBaseclassDecapsulation().isAllowed();
if (!decapsulationAllowed && !current.declaringClass.isSynthInterface())
// SH}
if (!current.canBeSeenBy(receiverType, invocationSite, this)) continue next;
if (foundSize > 0) {
// its possible to walk the same superinterface from different classes in the hierarchy
for (int f = 0; f < foundSize; f++)
if (current == found.elementAt(f)) continue next;
//{ObjectTeams: some fake ifc methods are not relevant here:
if (MethodModel.isRoleMethodInheritedFromNonPublicRegular(current))
continue next;
// SH}
}
found.add(current);
}
}
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 (TypeBinding.equalsEquals(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;
//{ObjectTeams: support non static team fields to be accessed from static role context:
boolean insideRoleType = false;
// SH}
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;
MethodScope methodScope = null;
int depth = 0;
int foundDepth = 0;
boolean shouldTrackOuterLocals = false;
ReferenceBinding foundActualReceiverType = null;
done : while (true) { // done when a COMPILATION_UNIT_SCOPE is found
switch (scope.kind) {
case METHOD_SCOPE :
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);
if (shouldTrackOuterLocals) {
if (invocationSite instanceof NameReference) {
NameReference nameReference = (NameReference) invocationSite;
nameReference.bits |= ASTNode.IsCapturedOuterLocal;
} else if (invocationSite instanceof AbstractVariableDeclaration) {
AbstractVariableDeclaration variableDeclaration = (AbstractVariableDeclaration) invocationSite;
variableDeclaration.bits |= ASTNode.ShadowsOuterLocal;
}
}
return variableBinding;
}
break;
case CLASS_SCOPE :
ClassScope classScope = (ClassScope) scope;
ReferenceBinding receiverType = classScope.enclosingReceiverType();
//{ObjectTeams: record when we travel out of a role
if (insideRoleType)
insideStaticContext = false; // forget about it, we already traveled out of the role
if (insideStaticContext && receiverType.isRole())
insideRoleType = true;
// SH}
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 (TypeBinding.equalsEquals(receiverType, fieldBinding.declaringClass) || compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4) {
// found a valid field in the 'immediate' scope (i.e. 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)
// but only if "valid field" was inherited in the first place.
if (TypeBinding.notEquals(foundField.declaringClass, fieldBinding.declaringClass) &&
TypeBinding.notEquals(foundField.declaringClass, foundActualReceiverType)) // https://bugs.eclipse.org/bugs/show_bug.cgi?id=316956
// i.e. 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++;
shouldTrackOuterLocals = true;
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;
}
if (scope.isLambdaScope()) // Not in Kansas anymore ...
shouldTrackOuterLocals = true;
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;
//{ObjectTeams: ROFI use role import scope if present:
imports = checkGetRoleImports(imports);
// SH}
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;
}
}
//{ObjectTeams: if we have a role imports scope, replace the given import bindings
// with those from the role import scope:
private ImportBinding[] checkGetRoleImports(ImportBinding[] imports) {
ClassScope classScope= classScope();
if (classScope instanceof OTClassScope) {
OTClassScope otClassScope= (OTClassScope)classScope;
ImportBinding[] rofiImports= otClassScope.getRoleUnitImports();
if (rofiImports != null)
imports= rofiImports;
}
return imports;
}
// SH}
class MethodClashException extends RuntimeException {
private static final long serialVersionUID = -7996779527641476028L;
}
// For exact method references. 15.28.1
private MethodBinding getExactMethod(TypeBinding receiverType, TypeBinding type, char[] selector, InvocationSite invocationSite, MethodBinding candidate) {
if (type == null)
return null;
TypeBinding [] superInterfaces = type.superInterfaces();
TypeBinding [] typePlusSupertypes = new TypeBinding[2 + superInterfaces.length];
typePlusSupertypes[0] = type;
typePlusSupertypes[1] = type.superclass();
if (superInterfaces.length != 0)
System.arraycopy(superInterfaces, 0, typePlusSupertypes, 2, superInterfaces.length);
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordTypeReference(type);
type = type.capture(this, invocationSite.sourceEnd());
for (int i = 0, typesLength = typePlusSupertypes.length; i < typesLength; i++) {
MethodBinding[] methods = i == 0 ? type.getMethods(selector) : new MethodBinding [] { getExactMethod(receiverType, typePlusSupertypes[i], selector, invocationSite, candidate) };
for (int j = 0, length = methods.length; j < length; j++) {
MethodBinding currentMethod = methods[j];
if (currentMethod == null || candidate == currentMethod)
continue;
if (i == 0 && (!currentMethod.canBeSeenBy(receiverType, invocationSite, this) || currentMethod.isSynthetic() || currentMethod.isBridge()))
continue;
if (candidate != null) {
if (!candidate.areParameterErasuresEqual(currentMethod))
throw new MethodClashException();
} else {
candidate = currentMethod;
}
}
}
return candidate;
}
// For exact method references. 15.28.1
public MethodBinding getExactMethod(TypeBinding receiverType, char[] selector, InvocationSite invocationSite) {
if (receiverType == null || !receiverType.isValidBinding() || receiverType.isBaseType())
return null;
TypeBinding currentType = receiverType;
if (currentType.isArrayType()) {
if (!currentType.leafComponentType().canBeSeenBy(this))
return null;
currentType = getJavaLangObject();
}
MethodBinding exactMethod = null;
try {
exactMethod = getExactMethod(receiverType, currentType, selector, invocationSite, null);
} catch (MethodClashException e) {
return null;
}
if (exactMethod == null || !exactMethod.canBeSeenBy(invocationSite, this))
return null;
if (exactMethod.isVarargs() || exactMethod.typeVariables() != Binding.NO_TYPE_VARIABLES && invocationSite.genericTypeArguments() == null)
return null;
if (receiverType.isArrayType()) {
if (CharOperation.equals(selector, TypeConstants.CLONE))
return environment().computeArrayClone(exactMethod);
if (CharOperation.equals(selector, TypeConstants.GETCLASS))
return environment().createGetClassMethod(receiverType, exactMethod, this);
}
if (exactMethod.declaringClass.id == TypeIds.T_JavaLangObject
&& CharOperation.equals(selector, TypeConstants.GETCLASS)
&& exactMethod.returnType.isParameterizedType())
{
return environment().createGetClassMethod(receiverType, exactMethod, this);
}
return exactMethod;
}
// For exact constructor references. 15.28.1
public MethodBinding getExactConstructor(TypeBinding receiverType, InvocationSite invocationSite) {
if (receiverType == null || !receiverType.isValidBinding() || !receiverType.canBeInstantiated() || receiverType.isBaseType())
return null;
if (receiverType.isArrayType()) {
TypeBinding leafType = receiverType.leafComponentType();
if (!leafType.canBeSeenBy(this) || !leafType.isReifiable())
return null;
return new MethodBinding(ClassFileConstants.AccPublic | ClassFileConstants.AccSynthetic, TypeConstants.INIT,
receiverType,
new TypeBinding[] { TypeBinding.INT },
Binding.NO_EXCEPTIONS,
getJavaLangObject()); // just lie.
}
CompilationUnitScope unitScope = compilationUnitScope();
MethodBinding exactConstructor = null;
unitScope.recordTypeReference(receiverType);
MethodBinding[] methods = receiverType.getMethods(TypeConstants.INIT);
for (int i = 0, length = methods.length; i < length; i++) {
MethodBinding constructor = methods[i];
if (!constructor.canBeSeenBy(invocationSite, this))
continue;
if (constructor.isVarargs())
return null;
if (constructor.typeVariables() != Binding.NO_TYPE_VARIABLES && invocationSite.genericTypeArguments() == null)
return null;
if (exactConstructor == null) {
exactConstructor = constructor;
} else {
return null;
}
}
return exactConstructor;
}
public MethodBinding getConstructor(ReferenceBinding receiverType, TypeBinding[] argumentTypes, InvocationSite invocationSite) {
MethodBinding method = getConstructor0(receiverType, argumentTypes, invocationSite);
if (method != null && method.isValidBinding() && method.isVarargs()) {
TypeBinding elementType = method.parameters[method.parameters.length - 1].leafComponentType();
if (elementType instanceof ReferenceBinding) {
if (!((ReferenceBinding) elementType).canBeSeenBy(this)) {
return new ProblemMethodBinding(method, method.selector, invocationSite.genericTypeArguments(), ProblemReasons.VarargsElementTypeNotVisible);
}
}
}
return method;
}
public MethodBinding getConstructor0(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, FULL_INFERENCE);
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, APPLICABILITY);
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) {
// 1.8: Give inference a chance to perform outstanding tasks (18.5.2):
return inferInvocationType(invocationSite, visible[0], argumentTypes);
}
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;
//{ObjectTeams generated _OT$getTeam() methods need access to this$n:
if (TypeAnalyzer.isSearchingForSyntheticField(methodScope(), receiverType, fieldName))
{
ReferenceBinding role = (ReferenceBinding) ((ReferenceBinding)receiverType).getRealClass().erasure();
if (role instanceof MemberTypeBinding) {
SyntheticArgumentBinding[] synthetics = ((MemberTypeBinding)role).enclosingInstances;
if (synthetics != null)
for (int i=0; i<synthetics.length; i++)
if (CharOperation.equals(synthetics[i].name, fieldName))
return synthetics[i].matchingField;
} // for BinaryTypeBinding find the synthetic field in "fields".
}
// SH}
FieldBinding field = findField(receiverType, fieldName, invocationSite, true /*resolve*/);
if (field != null) return field;
//{ObjectTeams: role class has a visible field? (perhaps inherited from regular super class?)
if (receiverType.isRole()) {
ReferenceBinding roleClass = ((ReferenceBinding)receiverType).getRealClass();
if (TypeBinding.notEquals(roleClass, receiverType)) {
// try again with role class:
field = findField(roleClass, fieldName, invocationSite, true /*resolve*/);
if (field != null && field.isValidBinding()) {
field = new FieldBinding(field, roleClass); // make a copy which we can mark specifically...
field.tagBits |= TagBits.IsRoleClassField; // ...tell CodeStream to add a cast.
return field;
}
}
}
// SH}
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;
//{ObjectTeams: support non static team methods to be accessed from static role context:
boolean insideRoleType = false;
// SH}
MethodBinding foundMethod = null;
MethodBinding foundProblem = null;
boolean foundProblemVisible = false;
Scope scope = this;
MethodScope methodScope = null;
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) scope;
insideStaticContext |= methodScope.isStatic;
insideConstructorCall |= methodScope.isConstructorCall;
insideTypeAnnotation = methodScope.insideTypeAnnotation;
break;
case CLASS_SCOPE :
ClassScope classScope = (ClassScope) scope;
ReferenceBinding receiverType = classScope.enclosingReceiverType();
//{ObjectTeams: record when we travel out of a role
if (insideRoleType)
insideStaticContext = false; // forget about it, we already travelled out of the role
if (insideStaticContext && receiverType.isRole())
insideRoleType = true;
// SH}
if (!insideTypeAnnotation) {
// retrieve an exact visible match (if possible)
// compilationUnitScope().recordTypeReference(receiverType); not needed since receiver is the source type
//{ObjectTeams: for use by MethodBinding.canBeSeenBy downstream:
classScope.origImplicitScope = this;
// orig:
MethodBinding methodBinding = classScope.findExactMethod(receiverType, selector, argumentTypes, invocationSite);
// :giro
classScope.origImplicitScope = null;
// SH}
if (methodBinding == null)
methodBinding = classScope.findMethod(receiverType, selector, argumentTypes, invocationSite, false);
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);
} else if (!methodBinding.isStatic() && methodScope != null) {
tagAsAccessingEnclosingInstanceStateOf(receiverType, false /* type variable access */);
}
if (inheritedHasPrecedence
|| TypeBinding.equalsEquals(receiverType, methodBinding.declaringClass)
|| (receiverType.getMethods(selector)) != Binding.NO_METHODS) {
// found a valid method in the 'immediate' scope (i.e. 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 environment().createGetClassMethod(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
|| (TypeBinding.notEquals(foundMethod.declaringClass, methodBinding.declaringClass)
&& (TypeBinding.equalsEquals(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;
//{ObjectTeams: ROFI use role import scope if present:
imports = checkGetRoleImports(imports);
//SH}
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, APPLICABILITY);
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) {
if (visible.size == 1) {
foundMethod = (MethodBinding) visible.elementAt(0);
} else {
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 getJavaLangInvokeLambdaMetafactory() {
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordQualifiedReference(TypeConstants.JAVA_LANG_INVOKE_LAMBDAMETAFACTORY);
return unitScope.environment.getResolvedType(TypeConstants.JAVA_LANG_INVOKE_LAMBDAMETAFACTORY, this);
}
public final ReferenceBinding getJavaLangInvokeSerializedLambda() {
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordQualifiedReference(TypeConstants.JAVA_LANG_INVOKE_SERIALIZEDLAMBDA);
return unitScope.environment.getResolvedType(TypeConstants.JAVA_LANG_INVOKE_SERIALIZEDLAMBDA, this);
}
public final ReferenceBinding getJavaLangInvokeMethodHandlesLookup() {
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordQualifiedReference(TypeConstants.JAVA_LANG_INVOKE_METHODHANDLES);
ReferenceBinding outerType = unitScope.environment.getResolvedType(TypeConstants.JAVA_LANG_INVOKE_METHODHANDLES, this);
return findDirectMemberType("Lookup".toCharArray(), outerType); //$NON-NLS-1$
}
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 getJavaLangIllegalArgumentException() {
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordQualifiedReference(TypeConstants.JAVA_LANG_ILLEGALARGUMENTEXCEPTION);
return unitScope.environment.getResolvedType(TypeConstants.JAVA_LANG_ILLEGALARGUMENTEXCEPTION, this);
}
public final ReferenceBinding getJavaUtilIterator() {
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordQualifiedReference(TypeConstants.JAVA_UTIL_ITERATOR);
return unitScope.environment.getResolvedType(TypeConstants.JAVA_UTIL_ITERATOR, this);
}
//{ObjectTeams
public final ReferenceBinding getOrgObjectteamsITeam() {
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordQualifiedReference(IOTConstants.ORG_OBJECTTEAMS_ITEAM);
return unitScope.environment.getResolvedType(IOTConstants.ORG_OBJECTTEAMS_ITEAM, this);
}
public final ReferenceBinding getOrgObjectteamsTeam() {
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordQualifiedReference(IOTConstants.ORG_OBJECTTEAMS_TEAM);
return unitScope.environment.getResolvedType(IOTConstants.ORG_OBJECTTEAMS_TEAM, this);
}
public final boolean isOrgObjectteamsTeam(TypeBinding tb) {
// if org.objectteams.Team is not resolvable, don't report
// an error, otherwise we can't compile plain-java programs
return TypeBinding.equalsEquals(tb, environment().getType(IOTConstants.ORG_OBJECTTEAMS_TEAM));
}
public final ReferenceBinding getOrgObjectteamsITeamMigratable() {
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordQualifiedReference(IOTConstants.ORG_OBJECTTEAMS_ITEAMMIGRATABLE);
return unitScope.environment.getResolvedType(IOTConstants.ORG_OBJECTTEAMS_ITEAMMIGRATABLE, this);
}
public final ReferenceBinding getOrgObjectteamsIBaseMigratable() {
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordQualifiedReference(IOTConstants.ORG_OBJECTTEAMS_IBASEMIGRATABLE);
return unitScope.environment.getResolvedType(IOTConstants.ORG_OBJECTTEAMS_IBASEMIGRATABLE, this);
}
// Markus Witte}
/* 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);
//{ObjectTeams: need to record referenced teams even if they are member of some other class:
/* @original
if (memberType != null) return memberType;
*/
if (memberType != null) {
TypeModel.checkReferencedTeam(memberType, this);
return memberType;
}
// SH}
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))
//{ObjectTeams: check if receiver allows baseclass decapsultion:
if (!allowsBaseclassDecapsulation(invocationSite, currentType))
// SH}
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, false);
//{ObjectTeams: callout to private role method goes through the role class-part:
// (will later be replaced by synthetic accessor)
if ( methodBinding == null
&& invocationSite instanceof MessageSend
&& ((MessageSend)invocationSite).isDecapsulationAllowed(this)
&& currentType.isRole())
{
currentType = currentType.getRealClass();
methodBinding = findMethod(currentType, selector, argumentTypes, invocationSite, false);
}
// SH}
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 environment().createGetClassMethod(receiverType, methodBinding, this);
}
return methodBinding;
} catch (AbortCompilation e) {
e.updateContext(invocationSite, referenceCompilationUnit().compilationResult);
throw e;
} finally {
env.missingClassFileLocation = null;
}
}
//{ObjectTeams: check if a receiver allows baseclass decapsultion:
private boolean allowsBaseclassDecapsulation(InvocationSite invocationSite, ReferenceBinding type) {
Expression receiver = null;
if (invocationSite instanceof MessageSend)
receiver = ((MessageSend) invocationSite).receiver;
else if (invocationSite instanceof FieldReference)
receiver = ((FieldReference) invocationSite).receiver;
else if (invocationSite instanceof QualifiedAllocationExpression)
receiver = ((QualifiedAllocationExpression)invocationSite).enclosingInstance;
else if (invocationSite instanceof QualifiedNameReference)
receiver = (QualifiedNameReference)invocationSite;
if (receiver != null && receiver.getBaseclassDecapsulation(type).isAllowed())
return true;
if (invocationSite instanceof Expression)
return ((Expression)invocationSite).getBaseclassDecapsulation(type).isAllowed();
return false;
}
// SH}
/* 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 package from the compoundName or null if it begins with a type.
* Intended to be used while resolving a package name only.
*
* Internal use only
*/
public final Binding getOnlyPackage(char[][] compoundName) {
compilationUnitScope().recordQualifiedReference(compoundName);
Binding binding = getTypeOrPackage(compoundName[0], Binding.PACKAGE, true);
if (binding == null || !binding.isValidBinding()) {
char[][] qName = new char[][] { compoundName[0] };
return new ProblemReferenceBinding(qName, null /* no closest match since search for pkg*/, ProblemReasons.NotFound);
}
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.getPackage(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());
}
packageBinding = (PackageBinding) binding;
}
return packageBinding;
}
/* 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.
//{ObjectTeams: ROFI changed modifiers, was final
public TypeBinding getType(char[] name) {
// SH}
// 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", ...)
*/
//{ObjectTeams: ROFI changed modifiers, was final
public TypeBinding getType(char[] name, PackageBinding packageBinding) {
// SH}
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);
//{ObjectTeams: record found type:
TypeModel.checkReferencedTeam(typeBinding, this);
// SH}
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.
*/
//{ObjectTeams: ROFI changed modifiers, was final
public TypeBinding getType(char[][] compoundName, int typeNameLength) {
// SH}
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;
}
//{ObjectTeams: handle the case of externalized roles "prefix.Type"
if (typeNameLength >= 2) {
TypeBinding typeBinding = RoleTypeCreator.resolveAnchoredType(this, null, compoundName, 0);
// If this result is reasonable we are done.
// Any non-null result signals that anchored type was indeed plausible,
// although it might have an error.
if (typeBinding != null) {
// copied from below:
compilationUnitScope().recordTypeReference(typeBinding); // to record supertypes
TypeModel.checkReferencedTeam(typeBinding, this);
return typeBinding;
}
}
// SH}
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());
}
}
//{ObjectTeams: record found type
TypeModel.checkReferencedTeam(typeBinding, this);
// SH}
return typeBinding;
}
/* Internal use only
*/
//{ObjectTeams: split method into two methods in order to intercept all reference types, public for OTClassScope:
/* orig:
final Binding getTypeOrPackage(char[] name, int mask, boolean needResolve) {
:giro */
public final Binding getTypeOrPackage(char[] name, int mask, boolean needResolve) {
Binding result = internalGetTypeOrPackage(name, mask, needResolve);
TypeModel.checkReferencedTeam(result, this);
return result;
}
private final Binding internalGetTypeOrPackage(char[] name, int mask, boolean needResolve) {
// SH}
Scope scope = this;
MethodScope methodScope = null;
ReferenceBinding foundType = null;
boolean insideStaticContext = false;
boolean insideTypeAnnotation = false;
//{ObjectTeams: support non static team fields to be accessed from static role context:
boolean insideRoleType = false;
// SH}
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) 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 && TypeBinding.notEquals(foundType, localType))
return new ProblemReferenceBinding(new char[][]{name}, foundType, ProblemReasons.InheritedNameHidesEnclosingName);
return localType;
}
break;
case CLASS_SCOPE :
SourceTypeBinding sourceType = ((ClassScope) scope).referenceContext.binding;
//{ObjectTeams: record when we travel out of a role
if (insideRoleType)
insideStaticContext = false; // forget about it, we already traveled out of the role
if (insideStaticContext && sourceType.isRole())
insideRoleType = true;
// SH}
if (scope == this && (sourceType.tagBits & TagBits.TypeVariablesAreConnected) == 0) {
// type variables take precedence over the source type, ex. class X <X> extends X == class X <Y> 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);
//{ObjectTeams: role files have member types AND imported types!
if (scope instanceof OTClassScope) {
TypeBinding imported = ((OTClassScope)scope).findImportedType(name, null, memberType);
if (imported != null && imported.isValidBinding())
return imported;
}
// SH}
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 (TypeBinding.equalsEquals(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 (i.e. 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 (i.e. not inherited)
if (foundType.isValidBinding() && TypeBinding.notEquals(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 && TypeBinding.notEquals(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
}
}
}
}
}
// In this location we had a fix for
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=318401
// However, as of today (4.3M6 candidate) this fix seems unnecessary, while causing StackOverflowError in
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=401271
// 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 (TypeBinding.notEquals(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) {
if (needResolve) {
closestMatch = environment().createMissingType(unitScope.fPackage, qName);
}
} else {
PackageBinding packageBinding = unitScope.environment.getTopLevelPackage(name);
if (packageBinding == null || !packageBinding.isValidBinding()) {
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) {
//{ObjectTeams: this method may be called in a Config-unaware context:
boolean createdConfig = false;
if (!Config.hasLookupEnvironment()) {
createdConfig = true;
Dependencies.setup(this, null, environment(), false, true);
}
try {
// SH}
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;
//{ObjectTeams: cleanup:
} finally {
if (createdConfig)
Dependencies.release(this);
}
// SH}
}
public 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;
}
// Tie break IS running to determine the most specific method binding.
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) {
/* Below 1.5, discard any generics we have left in for the method verifier's benefit, (so it
can detect method overriding properly in the presence of generic super types.) This is so
as to allow us to determine whether we have been handed an acceptable method in 1.4 terms
without all the 1.5isms below kicking in and spoiling the party.
See https://bugs.eclipse.org/bugs/show_bug.cgi?id=331446
*/
boolean applyErasure = environment().globalOptions.sourceLevel < ClassFileConstants.JDK1_5;
next : for (int i = 0; i < oneParamsLength; i++) {
TypeBinding oneParam = applyErasure ? oneParams[i].erasure() : oneParams[i];
TypeBinding twoParam = applyErasure ? twoParams[i].erasure() : twoParams[i];
if (TypeBinding.equalsEquals(oneParam, twoParam) || oneParam.isCompatibleWith(twoParam)) {
if (two.declaringClass.isRawType()) continue next;
TypeBinding leafComponentType = two.original().parameters[i].leafComponentType();
TypeBinding originalTwoParam = applyErasure ? leafComponentType.erasure() : 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 oType = ((ArrayBinding) oneParam).elementsType();
TypeBinding eType = ((ArrayBinding) twoParam).elementsType();
if (CompilerOptions.tolerateIllegalAmbiguousVarargsInvocation && this.compilerOptions().complianceLevel < ClassFileConstants.JDK1_7) {
if (TypeBinding.equalsEquals(oneParam, eType) || oneParam.isCompatibleWith(eType))
return true; // special case to choose between 2 varargs methods when the last arg is Object[]
} else {
if (TypeBinding.equalsEquals(oType, eType) || oType.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 (CompilerOptions.tolerateIllegalAmbiguousVarargsInvocation && this.compilerOptions().complianceLevel < ClassFileConstants.JDK1_7 &&
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 (TypeBinding.notEquals(oneParams[i], twoParams[i]) && !oneParams[i].isCompatibleWith(twoParams[i]))
return false;
if (parameterCompatibilityLevel(one, twoParams, true) == NOT_COMPATIBLE
&& parameterCompatibilityLevel(two, oneParams, true) == 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 TypeBinding.equalsEquals(convertedType, targetType) || convertedType.isCompatibleWith(targetType, this);
}
/* 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) {
method = method.original(); // https://bugs.eclipse.org/bugs/show_bug.cgi?id=350738
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 (TypeBinding.equalsEquals(topLevelTypes[i], enclosingType.original()))
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 (TypeBinding.equalsEquals(((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
ReferenceContext referenceContext = methodScope.referenceContext;
MethodBinding context = referenceContext instanceof AbstractMethodDeclaration ?
((AbstractMethodDeclaration)referenceContext).binding : ((LambdaExpression)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 (TypeBinding.equalsEquals(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 (TypeBinding.equalsEquals(next, interfacesToVisit[b])) continue nextInterface;
interfacesToVisit[nextPosition++] = next;
}
}
}
return false;
}
private TypeBinding leastContainingInvocation(TypeBinding mec, Object invocationData, ArrayList 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, (ArrayList)lubStack.clone());
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, (ArrayList)lubStack.clone());
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, ArrayList lubStack) {
if (u == null) return v;
if (TypeBinding.equalsEquals(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 (TypeBinding.equalsEquals(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 (TypeBinding.equalsEquals(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}, this, this.environment());
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 (TypeBinding.equalsEquals(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}, this, this.environment());
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 (TypeBinding.equalsEquals(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}, this, this.environment());
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 (TypeBinding.equalsEquals(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<String> and List<Exception>), 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, ArrayList 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 (TypeBinding.equalsEquals(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 :
// TODO(Stephan) : if null annotations differ, we need to create an intersection type and return.
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); // pass common null annotations by synthesized annotation bindings.
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;
}
public final MethodScope namedMethodScope() {
Scope scope = this;
do {
if (scope instanceof MethodScope && !scope.isLambdaScope())
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 == TypeBinding.NULL)
types[i] = type = null; // completely ignore null-type now and further down
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 (TypeBinding.notEquals(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 (TypeBinding.notEquals(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 (TypeBinding.notEquals(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 (TypeBinding.notEquals(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 || TypeBinding.equalsEquals(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 (TypeBinding.notEquals(match, (TypeBinding) 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 (TypeBinding.equalsEquals(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 (TypeBinding.equalsEquals(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 (TypeBinding.notEquals(match, (TypeBinding) 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 (TypeBinding.equalsEquals(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 && TypeBinding.notEquals(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;
// FIXME(SH): no parameter instantiation here!
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;
// FIXME(SH): no parameter instantiation here!
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, final InvocationSite invocationSite, ReferenceBinding receiverType) {
boolean isJdk18 = compilerOptions().sourceLevel >= ClassFileConstants.JDK1_8;
// common part for all compliance levels:
int[] compatibilityLevels = new int[visibleSize];
int compatibleCount = 0;
//{ObjectTeams: also record whether translation is required:
boolean[] useTranslation = new boolean[visibleSize];
Config oldConfig = Config.createOrResetConfig(this);
try {
// orig:
for (int i = 0; i < visibleSize; i++) {
TypeBinding[] argTypes = argumentTypes;
if (isJdk18 && invocationSite instanceof Invocation) {
InnerInferenceHelper innerInferenceHelper = ((Invocation)invocationSite).innerInferenceHelper();
if (innerInferenceHelper != null)
argTypes = innerInferenceHelper.getArgumentTypesForCandidate(visible[i], argumentTypes);
}
if ((compatibilityLevels[i] = parameterCompatibilityLevel(visible[i], argTypes)) != NOT_COMPATIBLE) {
if (i != compatibleCount) {
visible[compatibleCount] = visible[i];
compatibilityLevels[compatibleCount] = compatibilityLevels[i];
}
compatibleCount++;
}
// :giro
useTranslation[i] = Config.requireTypeAdjustment(); // TODO(SH): check kind of adjustment?
}
} finally {
Config.removeOrRestore(oldConfig, this);
// SH}
}
// TODO: Disabled, because we know a situation where this is expected, see https://bugs.eclipse.org/429490
// if (compatibleCount != visibleSize) {
// problemReporter().genericInferenceProblem("(Recovered) Internal inconsistency while checking invocation ambiguity", invocationSite, ProblemSeverities.Warning); //$NON-NLS-1$
// }
if (compatibleCount == 0) {
return new ProblemMethodBinding(visible[0].selector, argumentTypes, ProblemReasons.NotFound);
} else if (compatibleCount == 1) {
MethodBinding candidate = inferInvocationType(invocationSite, visible[0], argumentTypes);
compilationUnitScope().recordTypeReferences(candidate.thrownExceptions);
return candidate;
}
if (compatibleCount != visibleSize) {
System.arraycopy(visible, 0, visible = new MethodBinding[visibleSize = compatibleCount], 0, compatibleCount);
System.arraycopy(compatibilityLevels, 0, compatibilityLevels = new int[compatibleCount], 0, compatibleCount);
}
MethodBinding[] moreSpecific = new MethodBinding[visibleSize];
if (isJdk18) {
// 15.12.2.5 Choosing the Most Specific Method
int count = 0;
nextJ: for (int j = 0; j < visibleSize; j++) {
MethodBinding mbj = visible[j].genericMethod();
final TypeBinding[] mbjParameters = mbj.parameters;
int levelj = compatibilityLevels[j];
nextK: for (int k = 0; k < visibleSize; k++) {
if (j == k) continue;
// TODO do we want to check existing inference contexts whether they can tell us better about the used inferenceKind?
int levelk = compatibilityLevels[k];
if (levelj > -1 && levelk > -1 && levelj != levelk) {
if (levelj < levelk)
continue nextK; // j is more specific than this k
else
continue nextJ; // j cannot be more specific
}
MethodBinding mbk = visible[k].genericMethod();
final TypeBinding[] mbkParameters = mbk.parameters;
// TODO: should the following line also find diamond-typeVariables?
if (((invocationSite instanceof Invocation) || (invocationSite instanceof ReferenceExpression))
&& mbk.typeVariables() != Binding.NO_TYPE_VARIABLES)
{
// 18.5.4 More Specific Method Inference
Expression[] expressions = null;
if (invocationSite instanceof Invocation) {
expressions = ((Invocation)invocationSite).arguments();
} else {
expressions = ((ReferenceExpression)invocationSite).createPseudoExpressions(argumentTypes);
}
InferenceContext18 ic18 = new InferenceContext18(this, expressions, null);
if (!ic18.isMoreSpecificThan(mbj, mbk, levelj == VARARGS_COMPATIBLE, levelk == VARARGS_COMPATIBLE)) {
continue nextJ;
}
} else {
for (int i = 0, length = argumentTypes.length; i < length; i++) {
TypeBinding argumentType = argumentTypes[i];
TypeBinding s = InferenceContext18.getParameter(mbjParameters, i, levelj == VARARGS_COMPATIBLE);
TypeBinding t = InferenceContext18.getParameter(mbkParameters, i, levelk == VARARGS_COMPATIBLE);
if (TypeBinding.equalsEquals(s, t))
continue;
if (!argumentType.sIsMoreSpecific(s,t, this)) {
continue nextJ;
}
}
}
}
moreSpecific[count++] = visible[j];
}
if (count == 0) {
return new ProblemMethodBinding(visible[0], visible[0].selector, visible[0].parameters, ProblemReasons.Ambiguous);
} else if (count == 1) {
MethodBinding candidate = inferInvocationType(invocationSite, moreSpecific[0], argumentTypes);
compilationUnitScope().recordTypeReferences(candidate.thrownExceptions);
return candidate;
} else {
visibleSize = count;
// we proceed with pre 1.8 code below, which checks for overriding
}
} else {
// JLS7 implementation
InvocationSite tieBreakInvocationSite = new InvocationSite() {
public TypeBinding[] genericTypeArguments() { return null; } // ignore genericTypeArgs
public boolean isSuperAccess() { return invocationSite.isSuperAccess(); }
public boolean isTypeAccess() { return invocationSite.isTypeAccess(); }
public void setActualReceiverType(ReferenceBinding actualReceiverType) { /* ignore */}
public void setDepth(int depth) { /* ignore */}
public void setFieldIndex(int depth) { /* ignore */}
public int sourceStart() { return invocationSite.sourceStart(); }
public int sourceEnd() { return invocationSite.sourceStart(); }
public TypeBinding invocationTargetType() { return invocationSite.invocationTargetType(); }
public boolean receiverIsImplicitThis() { return invocationSite.receiverIsImplicitThis();}
public InferenceContext18 freshInferenceContext(Scope scope) { return null; /* no inference when ignoring genericTypeArgs */ }
public ExpressionContext getExpressionContext() { return ExpressionContext.VANILLA_CONTEXT; }
};
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 = current.tiebreakMethod();
for (int j = 0; j < visibleSize; j++) {
if (i == j || compatibilityLevels[j] != level) continue;
//{ObjectTeams:
if (!useTranslation[i] && useTranslation[j])
continue;
// SH}
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,
tieBreakInvocationSite, INVOCATION_TYPE, level == VARARGS_COMPATIBLE);
/* 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) {
// 1.8: Give inference a chance to perform outstanding tasks (18.5.2):
MethodBinding candidate = inferInvocationType(invocationSite, visible[i], argumentTypes);
compilationUnitScope().recordTypeReferences(candidate.thrownExceptions);
return candidate;
}
}
} 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 (TypeBinding.equalsEquals(original.declaringClass, original2.declaringClass))
break nextSpecific; // duplicates thru substitution
if (!original.isAbstract()) {
if (original2.isAbstract() || original2.isDefaultMethod())
continue; // only compare current against other concrete methods
original2 = original.findOriginalInheritedMethod(original2);
if (original2 == null)
continue nextSpecific; // current's declaringClass is not a subtype of next's declaringClass
if (current.hasSubstitutedParameters() || original.typeVariables != Binding.NO_TYPE_VARIABLES) {
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 (TypeBinding.equalsEquals(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 (TypeBinding.equalsEquals(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 (TypeBinding.notEquals(original.returnType, original2.returnType)) {
if (next.original().typeVariables != Binding.NO_TYPE_VARIABLES) {
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;
ReferenceBinding[] nextExceptions = getFilteredExceptions(next);
int nextLength = nextExceptions.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 = nextExceptions[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);
}
// 1.8: Before returning give inference a chance to perform outstanding tasks (18.5.2):
return inferInvocationType(invocationSite, current, argumentTypes);
}
}
return new ProblemMethodBinding(visible[0], visible[0].selector, visible[0].parameters, ProblemReasons.Ambiguous);
}
private ReferenceBinding[] getFilteredExceptions(MethodBinding method) {
// http://bugs.eclipse.org/387612 - Unreachable catch block...exception is never thrown from the try
// Need to filter redundant exceptions within the same throws clause.
// In this filtering the *most general* exception wins in order to capture all possible exceptions
// that could be thrown by the given method.
ReferenceBinding[] allExceptions = method.thrownExceptions;
int length = allExceptions.length;
if (length < 2) return allExceptions;
ReferenceBinding[] filteredExceptions = new ReferenceBinding[length];
int count = 0;
currents: for (int i = 0; i < length; i++) {
ReferenceBinding currentException = allExceptions[i];
for (int j = 0; j < length; j++) {
if (i == j) continue;
if (TypeBinding.equalsEquals(currentException, allExceptions[j])) {
// duplicate same exception
if (i < j)
break; // take only the first occurrence
else
continue currents; // skip
}
if (currentException.isCompatibleWith(allExceptions[j])) {
continue currents; // skip
}
}
filteredExceptions[count++] = currentException;
}
if (count != length) {
ReferenceBinding[] tmp = new ReferenceBinding[count];
System.arraycopy(filteredExceptions, 0, tmp, 0, count);
return tmp;
}
return allExceptions;
}
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) {
return parameterCompatibilityLevel(method, arguments, false);
}
public int parameterCompatibilityLevel(MethodBinding method, TypeBinding[] arguments, boolean tiebreakingVarargsMethods) {
TypeBinding[] parameters = method.parameters;
int paramLength = parameters.length;
int argLength = arguments.length;
//{ObjectTeams: instantiate declared parameters:
parameters = AnchorMapping.instantiateParameters(this, parameters, method);
//SH}
CompilerOptions compilerOptions = compilerOptions();
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];
//https://bugs.eclipse.org/bugs/show_bug.cgi?id=330445
if (TypeBinding.notEquals(arg, param) && !arg.isCompatibleWith(param.erasure(), this))
return NOT_COMPATIBLE;
}
//{ObjectTeams: store successful parameters:
AnchorMapping.storeInstantiatedParameters(method, parameters);
// SH}
return COMPATIBLE;
}
if (tiebreakingVarargsMethods) {
if (CompilerOptions.tolerateIllegalAmbiguousVarargsInvocation && compilerOptions.complianceLevel < ClassFileConstants.JDK1_7) {
tiebreakingVarargsMethods = false;
}
}
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 (TypeBinding.notEquals(param, arg)) {
level = parameterCompatibilityLevel(arg, param, env, tiebreakingVarargsMethods);
if (level == NOT_COMPATIBLE) {
// expect X[], is it called with X
param = ((ArrayBinding) param).elementsType();
if (tiebreakingVarargsMethods) {
arg = ((ArrayBinding) arg).elementsType();
}
if (parameterCompatibilityLevel(arg, param, env, tiebreakingVarargsMethods) == 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 = (tiebreakingVarargsMethods && (i == (argLength - 1))) ? ((ArrayBinding)arguments[i]).elementsType() : arguments[i];
if (TypeBinding.notEquals(param, arg) && parameterCompatibilityLevel(arg, param, env, tiebreakingVarargsMethods) == 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 = (tiebreakingVarargsMethods && (i == (argLength - 1))) ? ((ArrayBinding)arguments[i]).elementsType() : arguments[i];
if (TypeBinding.notEquals(arg,param)) {
int newLevel = parameterCompatibilityLevel(arg, param, env, tiebreakingVarargsMethods);
if (newLevel == NOT_COMPATIBLE)
return NOT_COMPATIBLE;
if (newLevel > level)
level = newLevel;
}
}
//{ObjectTeams: store successful parameters:
AnchorMapping.storeInstantiatedParameters(method, parameters);
// SH}
return level;
}
public int parameterCompatibilityLevel(TypeBinding arg, TypeBinding param) {
if (TypeBinding.equalsEquals(arg, param))
return COMPATIBLE;
if (arg == null || param == null)
return NOT_COMPATIBLE;
if (arg.isCompatibleWith(param, this))
return COMPATIBLE;
if (arg.kind() == Binding.POLY_TYPE || (arg.isBaseType() != param.isBaseType())) {
TypeBinding convertedType = environment().computeBoxingType(arg);
if (TypeBinding.equalsEquals(convertedType, param) || convertedType.isCompatibleWith(param, this))
return AUTOBOX_COMPATIBLE;
}
return NOT_COMPATIBLE;
}
private int parameterCompatibilityLevel(TypeBinding arg, TypeBinding param, LookupEnvironment env, boolean tieBreakingVarargsMethods) {
// only called if env.options.sourceLevel >= ClassFileConstants.JDK1_5
if (arg == null || param == null)
return NOT_COMPATIBLE;
if (arg.isCompatibleWith(param, this))
return COMPATIBLE;
if (tieBreakingVarargsMethods && (this.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_7 || !CompilerOptions.tolerateIllegalAmbiguousVarargsInvocation)) {
/* 15.12.2.5 Choosing the Most Specific Method, ... One variable arity member method named m is more specific than
another variable arity member method of the same name if either ... Only subtypes relationship should be used.
Actually this is true even for fixed arity methods, but in practice is not an issue since we run the algorithm
multiple times for each compatibility level.
https://bugs.eclipse.org/bugs/show_bug.cgi?id=346038, https://bugs.eclipse.org/bugs/show_bug.cgi?id=346039.
*/
return NOT_COMPATIBLE;
}
if (arg.kind() == Binding.POLY_TYPE || (arg.isBaseType() != param.isBaseType())) {
TypeBinding convertedType = env.computeBoxingType(arg);
if (TypeBinding.equalsEquals(convertedType, param) || convertedType.isCompatibleWith(param, this))
return AUTOBOX_COMPATIBLE;
}
return NOT_COMPATIBLE;
}
public abstract ProblemReporter problemReporter();
//{ObjectTeams: removed final:
public CompilationUnitDeclaration referenceCompilationUnit() {
// SH}
Scope scope, unitScope = this;
while ((scope = unitScope.parent) != null)
//{ObjectTeams: respect overridden version from OTClassScope:
{
if (scope instanceof OTClassScope)
return scope.referenceCompilationUnit();
// orig:
unitScope = scope;
// :giro
}
// SH}
return ((CompilationUnitScope) unitScope).referenceContext;
}
//{ObjectTeams: convenience lookups and queries
public TypeDeclaration referenceType() {
Scope scope = this;
do {
if (scope instanceof ClassScope)
return ((ClassScope)scope).referenceContext;
scope = scope.parent;
} while (scope != null);
return null;
}
// helper to find if we are in the context of a generated method.
// or resolving a generated field.
public boolean isGeneratedScope() {
MethodScope methodScope = methodScope();
if (methodScope == null)
return false;
ReferenceContext methodContext = methodScope.referenceContext;
if (methodContext instanceof AbstractMethodDeclaration) {
AbstractMethodDeclaration methodDecl = (AbstractMethodDeclaration)methodContext;
if (methodDecl.isGenerated)
return true;
}
if (methodScope.initializedField != null)
return CharOperation.prefixEquals(IOTConstants.OT_DOLLAR_NAME, methodScope.initializedField.name);
return false;
}
public boolean isMethodMappingWrapper() {
MethodScope methodScope = methodScope();
if (methodScope == null)
return false;
ReferenceContext methodContext = methodScope.referenceContext;
if (methodContext instanceof AbstractMethodDeclaration) {
AbstractMethodDeclaration methodDecl = (AbstractMethodDeclaration)methodContext;
return methodDecl.isMappingWrapper != WrapperKind.NONE;
}
return false;
}
public boolean isBaseGuard() {
MethodScope methodScope = methodScope();
if (methodScope == null)
return false;
AbstractMethodDeclaration referenceMethod = methodScope.referenceMethod();
if (referenceMethod == null)
return false;
return CharOperation.prefixEquals(IOTConstants.BASE_PREDICATE_PREFIX, referenceMethod.selector);
}
public boolean isFakeMethod(FakeKind kind) {
MethodScope methodScope = methodScope();
if (methodScope == null)
return false;
AbstractMethodDeclaration referenceMethod = methodScope.referenceMethod();
if (referenceMethod == null)
return false;
if (referenceMethod.model == null)
return false;
return referenceMethod.model._fakeKind == kind;
}
public boolean isStatic() {
MethodScope methodScope = methodScope();
if (methodScope != null)
return methodScope.isStatic;
ReferenceBinding enclosingType = enclosingSourceType();
if (enclosingType != null)
return enclosingType.isMemberType() && enclosingType.getRealClass().isStatic();
return false;
}
// SH}
/**
* 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) {
//{ObjectTeams:
case BINDING_SCOPE :
return ((CallinCalloutScope) current).referenceContext;
// SH}
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;
}
/**
* Returns the nearest original reference context, starting from current scope.
* If starting on a class, it will return current class. If starting on unitScope, returns unit.
*/
public ReferenceContext originalReferenceContext() {
Scope current = this;
do {
switch(current.kind) {
case METHOD_SCOPE :
ReferenceContext context = ((MethodScope) current).referenceContext;
if (context instanceof LambdaExpression) {
LambdaExpression expression = (LambdaExpression) context;
while (expression != expression.original)
expression = expression.original;
return expression;
}
return context;
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;
}
/* Given an allocation type and arguments at the allocation site, answer a synthetic generic static factory method
that could instead be invoked with identical results. Return null if no compatible, visible, most specific method
could be found. This method is modeled after Scope.getConstructor and Scope.getMethod.
*/
public MethodBinding getStaticFactory (ParameterizedTypeBinding allocationType, ReferenceBinding originalEnclosingType, TypeBinding[] argumentTypes, final Invocation allocationSite) {
// allocationType is the diamond type. originalEnclosingType is the real enclosing type ==> may be parameterized, parameterized with own type variables, raw, just plain type or null.
int classTypeVariablesArity = 0;
TypeVariableBinding[] classTypeVariables = Binding.NO_TYPE_VARIABLES;
ReferenceBinding genericType = allocationType.genericType();
ReferenceBinding currentType = genericType;
while (currentType != null) {
TypeVariableBinding[] typeVariables = currentType.typeVariables();
int length = typeVariables == null ? 0 : typeVariables.length;
if (length > 0) {
System.arraycopy(classTypeVariables, 0, classTypeVariables = new TypeVariableBinding[classTypeVariablesArity + length], 0, classTypeVariablesArity);
System.arraycopy(typeVariables, 0, classTypeVariables, classTypeVariablesArity, length);
classTypeVariablesArity += length;
}
if (currentType.isStatic()) // any enclosing types cannot be parameterized, if generic treat as raw.
break;
currentType = currentType.enclosingType();
}
MethodBinding[] methods = genericType.getMethods(TypeConstants.INIT, argumentTypes.length);
MethodBinding [] staticFactories = new MethodBinding[methods.length];
int sfi = 0;
for (int i = 0, length = methods.length; i < length; i++) {
MethodBinding method = methods[i];
if (!method.canBeSeenBy(allocationSite, this))
continue;
int paramLength = method.parameters.length;
boolean isVarArgs = method.isVarargs();
if (argumentTypes.length != paramLength)
if (!isVarArgs || argumentTypes.length < paramLength - 1)
continue; // incompatible
TypeVariableBinding[] methodTypeVariables = method.typeVariables();
int methodTypeVariablesArity = methodTypeVariables.length;
final int factoryArity = classTypeVariablesArity + methodTypeVariablesArity;
final LookupEnvironment environment = environment();
MethodBinding staticFactory = new SyntheticFactoryMethodBinding(method, environment, originalEnclosingType);
staticFactory.typeVariables = new TypeVariableBinding[factoryArity];
final SimpleLookupTable map = new SimpleLookupTable(factoryArity);
// Rename each type variable T of the type to T' or T'' or T''' based on the enclosing level to avoid a clash.
String prime = ""; //$NON-NLS-1$
Binding declaringElement = null;
for (int j = 0; j < classTypeVariablesArity; j++) {
TypeVariableBinding original;
original = classTypeVariables[j];
if (original.declaringElement != declaringElement) {
declaringElement = original.declaringElement;
prime += "'"; //$NON-NLS-1$
}
map.put(original, staticFactory.typeVariables[j] = new TypeVariableBinding(CharOperation.concat(original.sourceName, prime.toCharArray()),
staticFactory, j, environment));
}
// Rename each type variable U of method
prime += "'"; //$NON-NLS-1$
for (int j = classTypeVariablesArity, k = 0; j < factoryArity; j++, k++) {
map.put(methodTypeVariables[k],
(staticFactory.typeVariables[j] = new TypeVariableBinding(CharOperation.concat(methodTypeVariables[k].sourceName, prime.toCharArray()),
staticFactory, j, environment)));
}
final Scope scope = this;
Substitution substitution = new Substitution() {
public LookupEnvironment environment() {
return scope.environment();
}
public boolean isRawSubstitution() {
return false;
}
public TypeBinding substitute(TypeVariableBinding typeVariable) {
TypeBinding retVal = (TypeBinding) map.get(typeVariable);
return retVal == null ? typeVariable : typeVariable.hasTypeAnnotations() ? environment().createAnnotatedType(retVal, typeVariable.getTypeAnnotations()) : retVal;
}
//{ObjectTeams:
public ITeamAnchor substituteAnchor(ITeamAnchor anchor, int rank) {
// FIXME Auto-generated method stub
return null;
}
// SH}
};
// initialize new variable bounds
for (int j = 0; j < factoryArity; j++) {
TypeVariableBinding originalVariable = j < classTypeVariablesArity ? classTypeVariables[j] : methodTypeVariables[j - classTypeVariablesArity];
TypeVariableBinding substitutedVariable = (TypeVariableBinding) map.get(originalVariable);
TypeBinding substitutedSuperclass = Scope.substitute(substitution, originalVariable.superclass);
ReferenceBinding[] substitutedInterfaces = Scope.substitute(substitution, originalVariable.superInterfaces);
if (originalVariable.firstBound != null) {
TypeBinding firstBound;
firstBound = TypeBinding.equalsEquals(originalVariable.firstBound, originalVariable.superclass)
? substitutedSuperclass // could be array type or interface
: substitutedInterfaces[0];
substitutedVariable.setFirstBound(firstBound);
}
switch (substitutedSuperclass.kind()) {
case Binding.ARRAY_TYPE :
substitutedVariable.setSuperClass(environment.getResolvedType(TypeConstants.JAVA_LANG_OBJECT, null));
substitutedVariable.setSuperInterfaces(substitutedInterfaces);
break;
default:
if (substitutedSuperclass.isInterface()) {
substitutedVariable.setSuperClass(environment.getResolvedType(TypeConstants.JAVA_LANG_OBJECT, null));
int interfaceCount = substitutedInterfaces.length;
System.arraycopy(substitutedInterfaces, 0, substitutedInterfaces = new ReferenceBinding[interfaceCount+1], 1, interfaceCount);
substitutedInterfaces[0] = (ReferenceBinding) substitutedSuperclass;
substitutedVariable.setSuperInterfaces(substitutedInterfaces);
} else {
substitutedVariable.setSuperClass((ReferenceBinding) substitutedSuperclass); // typeVar was extending other typeVar which got substituted with interface
substitutedVariable.setSuperInterfaces(substitutedInterfaces);
}
}
}
staticFactory.returnType = environment.createParameterizedType(genericType, Scope.substitute(substitution, genericType.typeVariables()), originalEnclosingType);
staticFactory.parameters = Scope.substitute(substitution, method.parameters);
staticFactory.thrownExceptions = Scope.substitute(substitution, method.thrownExceptions);
if (staticFactory.thrownExceptions == null) {
staticFactory.thrownExceptions = Binding.NO_EXCEPTIONS;
}
staticFactories[sfi++] = new ParameterizedMethodBinding((ParameterizedTypeBinding) environment.convertToParameterizedType(staticFactory.declaringClass),
staticFactory);
}
if (sfi == 0)
return null;
if (sfi != methods.length) {
System.arraycopy(staticFactories, 0, staticFactories = new MethodBinding[sfi], 0, sfi);
}
MethodBinding[] compatible = new MethodBinding[sfi];
int compatibleIndex = 0;
for (int i = 0; i < sfi; i++) {
MethodBinding compatibleMethod = computeCompatibleMethod(staticFactories[i], argumentTypes, allocationSite, APPLICABILITY);
if (compatibleMethod != null) {
if (compatibleMethod.isValidBinding())
compatible[compatibleIndex++] = compatibleMethod;
}
}
if (compatibleIndex == 0) {
return null;
}
if (compatibleIndex == 1) {
// 1.8: Give inference a chance to perform outstanding tasks (18.5.2):
compatible[0] = inferInvocationType(allocationSite, compatible[0], argumentTypes);
}
return compatibleIndex == 1 ? compatible[0] : mostSpecificMethodBinding(compatible, compatibleIndex, argumentTypes, allocationSite, allocationType);
}
public boolean validateNullAnnotation(long tagBits, TypeReference typeRef, Annotation[] annotations) {
long nullAnnotationTagBit = tagBits & (TagBits.AnnotationNullMASK);
if (nullAnnotationTagBit != 0) {
TypeBinding type = typeRef.resolvedType;
if (type != null && type.isBaseType()) {
// type annotations are *always* illegal for 'void' (already reported)
if (!(typeRef.resolvedType.id == TypeIds.T_void && compilerOptions().sourceLevel >= ClassFileConstants.JDK1_8))
problemReporter().illegalAnnotationForBaseType(typeRef, annotations, nullAnnotationTagBit);
return false;
}
// for type annotations, more problems are detected in Annotation.isTypeUseCompatible()
}
return true;
}
public static BlockScope typeAnnotationsResolutionScope(Scope scope) {
BlockScope resolutionScope = null;
switch(scope.kind) {
case Scope.CLASS_SCOPE:
resolutionScope = ((ClassScope) scope).referenceContext.staticInitializerScope;
break;
case Scope.BLOCK_SCOPE :
case Scope.METHOD_SCOPE :
resolutionScope = (BlockScope) scope;
break;
}
return resolutionScope;
}
// Some entity in the receiver scope is referencing instance data of enclosing type. Tag all intervening methods as instance methods.
public void tagAsAccessingEnclosingInstanceStateOf(ReferenceBinding enclosingType, boolean typeVariableAccess) {
MethodScope methodScope = methodScope();
if (methodScope != null && methodScope.referenceContext instanceof TypeDeclaration) {
if (!methodScope.enclosingReceiverType().isCompatibleWith(enclosingType)) { // unless invoking a method of the local type ...
// anonymous type, find enclosing method
methodScope = methodScope.enclosingMethodScope();
}
}
while (methodScope != null) {
while (methodScope != null && methodScope.referenceContext instanceof LambdaExpression) {
LambdaExpression lambda = (LambdaExpression) methodScope.referenceContext;
if (!typeVariableAccess)
lambda.shouldCaptureInstance = true; // lambda can still be static, only when `this' is touched (implicitly or otherwise) it cannot be.
methodScope = methodScope.enclosingMethodScope();
}
if (methodScope != null) {
if (methodScope.referenceContext instanceof MethodDeclaration) {
MethodDeclaration methodDeclaration = (MethodDeclaration) methodScope.referenceContext;
methodDeclaration.bits &= ~ASTNode.CanBeStatic;
}
ClassScope enclosingClassScope = methodScope.enclosingClassScope();
if (enclosingClassScope != null) {
TypeDeclaration type = enclosingClassScope.referenceContext;
if (type != null && type.binding != null && enclosingType != null && !type.binding.isCompatibleWith(enclosingType.original())) {
methodScope = enclosingClassScope.enclosingMethodScope();
continue;
}
}
break;
}
}
}
/**
* Given a selected applibable method, check if it has an unfinished InferenceContext18 associated.
* If so perform the outstanding Invocation Type Inference and return the improved method,
* otherwise return the applicable method unchanged.
*/
protected MethodBinding inferInvocationType(InvocationSite invocationSite, MethodBinding applicable, TypeBinding[] argumentTypes) {
if (invocationSite instanceof Invocation) {
Invocation invocation = (Invocation) invocationSite;
if (applicable instanceof ParameterizedGenericMethodBinding) {
ParameterizedGenericMethodBinding parameterizedMethod = (ParameterizedGenericMethodBinding) applicable;
InferenceContext18 infCtx18 = invocation.getInferenceContext(parameterizedMethod);
if (infCtx18 != null && !infCtx18.hasResultFor(invocation.invocationTargetType())) {
return infCtx18.inferInvocationType(invocation, argumentTypes, parameterizedMethod);
}
} else {
ASTNode.resolvePolyExpressionArguments(invocation, applicable, argumentTypes, this);
}
}
return applicable;
}
}