| /******************************************************************************* |
| * 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 |
| * 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.aspectj.org.eclipse.jdt.internal.compiler.lookup; |
| |
| import java.util.*; |
| |
| import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ast.*; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.impl.CompilerOptions; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.impl.ReferenceContext; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.problem.AbortCompilation; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.problem.ProblemReporter; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.util.HashtableOfObject; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.util.ObjectVector; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.util.SimpleLookupTable; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.util.SimpleSet; |
| |
| @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; |
| |
| /* 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; |
| |
| 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$ |
| 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; |
| 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. |
| int nextLevel = compatibilityLevel18FromInner(method, innerInferenceHelper, invocationArguments[i], argumentType, argLen, i, isVarArgs); |
| if (nextLevel == NOT_COMPATIBLE) |
| return nextLevel; |
| if (nextLevel == -2) |
| break jdk18checks; |
| level = Math.max(level, nextLevel); |
| } |
| return level; // neither NOT_COMPATIBLE nor unknown(-2) seen |
| } |
| } |
| // fall back to old method: |
| return parameterCompatibilityLevel(method, arguments, tiebreakingVarargsMethods); |
| } |
| |
| private int compatibilityLevel18FromInner(MethodBinding method, InnerInferenceHelper innerInferenceHelper, Expression invocArg, TypeBinding argType, int argLen, int i, boolean[] isVarArgs) |
| { |
| int compatible = isVarArgs[0] ? VARARGS_COMPATIBLE : COMPATIBLE; |
| TypeBinding resolvedType = invocArg.resolvedType; |
| TypeBinding targetType = InferenceContext18.getParameter(method.parameters, i, isVarArgs[0]); |
| if (!isVarArgs[0] && shouldTryVarargs(method, resolvedType, targetType)) { |
| isVarArgs[0] = true; |
| targetType = InferenceContext18.getParameter(method.parameters, i, true); |
| } |
| 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; |
| int level1 = compatibilityLevel18FromInner(method, innerInferenceHelper, ce.valueIfTrue, argType, argLen, i, isVarArgs); |
| if (level1 == NOT_COMPATIBLE) |
| return NOT_COMPATIBLE; |
| int level2 = compatibilityLevel18FromInner(method, innerInferenceHelper, ce.valueIfFalse, argType, argLen, i, isVarArgs); |
| 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; |
| targetType = InferenceContext18.getParameter(method.parameters, i, true); |
| 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]; |
| 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]; |
| TypeVariableBinding typeVariable = typeParameter.binding; |
| TypeReference typeRef = typeParameter.type; |
| if (typeRef == null) |
| continue nextVariable; |
| boolean isFirstBoundTypeVariable = false; |
| TypeBinding superType = this.kind == METHOD_SCOPE |
| ? typeRef.resolveType((BlockScope)this, false/*no bound check*/) |
| : typeRef.resolveType((ClassScope)this); |
| if (superType == null) { |
| typeVariable.tagBits |= TagBits.HierarchyHasProblems; |
| } else { |
| typeRef.resolvedType = superType; // hold onto the problem type |
| firstBound: { |
| switch (superType.kind()) { |
| case Binding.ARRAY_TYPE : |
| problemReporter().boundCannotBeArray(typeRef, superType); |
| typeVariable.tagBits |= TagBits.HierarchyHasProblems; |
| break firstBound; // do not keep first bound |
| case Binding.TYPE_PARAMETER : |
| isFirstBoundTypeVariable = true; |
| TypeVariableBinding varSuperType = (TypeVariableBinding) superType; |
| if (varSuperType.rank >= typeVariable.rank && varSuperType.declaringElement == typeVariable.declaringElement) { |
| if (compilerOptions().complianceLevel <= ClassFileConstants.JDK1_6) { |
| problemReporter().forwardTypeVariableReference(typeParameter, varSuperType); |
| typeVariable.tagBits |= TagBits.HierarchyHasProblems; |
| break firstBound; // do not keep first bound |
| } |
| } |
| // 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()) { |
| problemReporter().finalVariableBound(typeVariable, typeRef); |
| } |
| break; |
| } |
| ReferenceBinding superRefType = (ReferenceBinding) superType; |
| 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; |
| } |
| |
| // AspectJ Extension |
| /** |
| * For Java scopes, the invocationType is always the same as the enclosingSourceType |
| * This distinction is important for AspectJ's inter-type declarations |
| * |
| * For inter-type declarations, the invocationType is the lexically enclosing type. |
| */ |
| public SourceTypeBinding invocationType() { |
| Scope scope = this; |
| do { |
| if (scope instanceof ClassScope) |
| return ((ClassScope) scope).invocationType(); |
| scope = scope.parent; |
| } while (scope != null); |
| return null; |
| //return enclosingSourceType(); |
| } |
| // End AspectJ Extension |
| |
| 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); |
| 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; |
| } |
| |
| // 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 |
| 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; |
| } |
| |
| // AspectJ Extension |
| public static final IPrivilegedHandler findPrivilegedHandler(ReferenceBinding type) { |
| if (type == null) return null; |
| if (type instanceof SourceTypeBinding) { |
| if (((SourceTypeBinding)type).privilegedHandler != null) { |
| return ((SourceTypeBinding)type).privilegedHandler; |
| } |
| } |
| return findPrivilegedHandler(type.enclosingType()); |
| } |
| // End AspectJ Extension |
| |
| // 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)) |
| return new ProblemFieldBinding((ReferenceBinding)leafType, fieldName, ProblemReasons.ReceiverTypeNotVisible); |
| if (CharOperation.equals(fieldName, TypeConstants.LENGTH)) { |
| if ((leafType.tagBits & TagBits.HasMissingType) != 0) { |
| return new ProblemFieldBinding(ArrayBinding.ArrayLength, null, fieldName, ProblemReasons.NotFound); |
| } |
| return ArrayBinding.ArrayLength; |
| } |
| return null; |
| } |
| |
| ReferenceBinding currentType = (ReferenceBinding) receiverType; |
| if (!currentType.canBeSeenBy(this)) |
| return new ProblemFieldBinding(currentType, fieldName, ProblemReasons.ReceiverTypeNotVisible); |
| |
| currentType.initializeForStaticImports(); |
| FieldBinding field = currentType.getField(fieldName,true/*resolve*/,invocationSite,this); // AspectJ extension, was getField(fieldName,needResolve); |
| // 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) { |
| // AspectJ Extension |
| FieldBinding ret = field.getVisibleBinding(currentType, invocationSite, this); |
| if (ret != null) |
| return ret; |
| // End AspectJ Extension |
| 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, invocationSite, this)) != null) { // AspectJ Extension - was getField(fieldName,needResolve) |
| if (invisibleFieldsOk) { |
| return field; |
| } |
| keepLooking = false; |
| // AspectJ Extension |
| field = field.getVisibleBinding(receiverType, invocationSite, this); |
| if (field != null) { |
| // End AspectJ Extension |
| if (field.canBeSeenBy(receiverType, invocationSite, this)) { |
| if (visibleField == null) |
| visibleField = field; |
| else |
| return new ProblemFieldBinding(visibleField /* closest match*/, visibleField.declaringClass, fieldName, ProblemReasons.Ambiguous); |
| } // AspectJ extension - completes new if branch |
| } 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*/, invocationSite, this)) != null) { // AspectJ Extension - was getField(fieldName,true/*resolve*/) |
| // AspectJ Extension |
| field = field.getVisibleBinding(receiverType, invocationSite, this); |
| if (field != null) { |
| // End AspectJ Extension |
| if (invisibleFieldsOk) { |
| return field; |
| } |
| if (visibleField == null) { |
| visibleField = field; |
| } else { |
| ambiguous = new ProblemFieldBinding(visibleField /* closest match*/, visibleField.declaringClass, fieldName, ProblemReasons.Ambiguous); |
| break done; |
| } |
| } // AspectJ Extension - completes new if() |
| } 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) { |
| if ((enclosingType.tagBits & TagBits.HasNoMemberTypes) != 0) |
| return null; // know it has no member types (nor inherited member types) |
| |
| ReferenceBinding enclosingSourceType = invocationType();// AspectJ Extension - was 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) { |
| ReferenceBinding currentType = receiverType; |
| boolean receiverTypeIsInterface = receiverType.isInterface(); |
| ObjectVector found = new ObjectVector(3); |
| CompilationUnitScope unitScope = compilationUnitScope(); |
| unitScope.recordTypeReferences(argumentTypes); |
| |
| if (receiverTypeIsInterface) { |
| unitScope.recordTypeReference(receiverType); |
| MethodBinding[] receiverMethods = receiverType.getMethods(selector, argumentTypes.length); |
| if (receiverMethods.length > 0) |
| found.addAll(receiverMethods); |
| findMethodInSuperInterfaces(receiverType, selector, found, invocationSite); |
| currentType = getJavaLangObject(); |
| } |
| |
| // superclass lookup |
| long complianceLevel = compilerOptions().complianceLevel; |
| boolean isCompliant14 = complianceLevel >= ClassFileConstants.JDK1_4; |
| boolean isCompliant15 = complianceLevel >= ClassFileConstants.JDK1_5; |
| 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]; |
| // New AspectJ Extension |
| // old code: |
| // if (methodBinding.canBeSeenBy(receiverType, invocationSite, this)) { |
| // if (visiblesCount != i) { |
| // candidates[i] = null; |
| // candidates[visiblesCount] = methodBinding; |
| // } |
| // visiblesCount++; |
| // } |
| // new code: |
| boolean b = methodBinding.canBeSeenBy(receiverType,invocationSite,this); |
| if (!b) { |
| MethodBinding visMethodBinding = methodBinding.getVisibleBinding(receiverType,invocationSite,this); |
| if (visMethodBinding!=null) { |
| methodBinding = visMethodBinding; |
| if (visiblesCount != i) { |
| candidates[i] = null; |
| candidates[visiblesCount] = methodBinding; |
| } |
| visiblesCount++; |
| } |
| } else { |
| if (visiblesCount != i) { |
| candidates[i] = null; |
| candidates[visiblesCount] = methodBinding; |
| } |
| visiblesCount++; |
| } |
| // // End AspectJ Extension |
| } |
| 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]; |
| 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; |
| } |
| 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; |
| |
| 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(); |
| if (!insideTypeAnnotation) { |
| // ASPECTJ START |
| /*{ |
| FieldBinding fieldBinding = classScope.findField(receiverType, name, invocationSite, needResolve); |
| }*/ |
| FieldBinding fieldBinding = null; |
| if (receiverType!=null) { |
| fieldBinding = classScope.findField(receiverType, name, invocationSite, needResolve); |
| } |
| // ASPECTJ END |
| // 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+=classScope.addDepth(); // AspectJ Extension - was depth++ |
| shouldTrackOuterLocals = true; |
| insideStaticContext |= invocationType().isStatic(); // AspectJ Extension - was receiverType |
| // 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; |
| 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; |
| } |
| } |
| |
| 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); |
| // AspectJ Extension |
| if (methodBinding != null) { |
| methodBinding = methodBinding.getVisibleBinding(invocationSite, this); |
| } |
| // End AspectJ Extension |
| 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]; |
| // AspectJ Extension |
| method = method.getVisibleBinding(invocationSite, this); |
| if (method != null) { |
| // End AspectJ Extension |
| if (method.canBeSeenBy(invocationSite, this)) |
| visible[visibleIndex++] = method; |
| } // AspectJ Extension - completes new if() |
| } |
| 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; |
| FieldBinding field = findField(receiverType, fieldName, invocationSite, true /*resolve*/); |
| if (field != null) return field; |
| |
| return new ProblemFieldBinding( |
| receiverType instanceof ReferenceBinding ? (ReferenceBinding) receiverType : null, |
| fieldName, |
| ProblemReasons.NotFound); |
| } catch (AbortCompilation e) { |
| e.updateContext(invocationSite, referenceCompilationUnit().compilationResult); |
| throw e; |
| } finally { |
| env.missingClassFileLocation = null; |
| } |
| } |
| |
| /* API |
| * |
| * Answer the method binding that corresponds to selector, argumentTypes. |
| * Start the lookup at the enclosing type of the receiver. |
| * InvocationSite implements |
| * isSuperAccess(); this is used to determine if the discovered method is visible. |
| * setDepth(int); this is used to record the depth of the discovered method |
| * relative to the enclosing type of the receiver. (If the method is defined |
| * in the enclosing type of the receiver, the depth is 0; in the next enclosing |
| * type, the depth is 1; and so on |
| * |
| * If no visible method is discovered, an error binding is answered. |
| */ |
| public MethodBinding getImplicitMethod(char[] selector, TypeBinding[] argumentTypes, InvocationSite invocationSite) { |
| |
| boolean insideStaticContext = false; |
| boolean insideConstructorCall = false; |
| boolean insideTypeAnnotation = false; |
| MethodBinding foundMethod = null; |
| MethodBinding foundProblem = null; |
| boolean foundProblemVisible = false; |
| Scope scope = this; |
| 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(); |
| if (!insideTypeAnnotation) { |
| // retrieve an exact visible match (if possible) |
| // compilationUnitScope().recordTypeReference(receiverType); not needed since receiver is the source type |
| MethodBinding methodBinding = classScope.findExactMethod(receiverType, selector, argumentTypes, invocationSite); |
| if (methodBinding == null) |
| methodBinding = classScope.findMethod(receiverType, selector, argumentTypes, invocationSite, 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; |
| 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); |
| } |
| |
| /* Answer the type binding corresponding to the typeName argument, relative to the enclosingType. |
| */ |
| public final ReferenceBinding getMemberType(char[] typeName, ReferenceBinding enclosingType) { |
| ReferenceBinding memberType = findMemberType(typeName, enclosingType); |
| if (memberType != null) return memberType; |
| char[][] compoundName = new char[][] { typeName }; |
| return new ProblemReferenceBinding(compoundName, null, ProblemReasons.NotFound); |
| } |
| |
| public MethodBinding getMethod(TypeBinding receiverType, char[] selector, TypeBinding[] argumentTypes, InvocationSite invocationSite) { |
| CompilationUnitScope unitScope = compilationUnitScope(); |
| LookupEnvironment env = unitScope.environment; |
| try { |
| env.missingClassFileLocation = invocationSite; |
| switch (receiverType.kind()) { |
| case Binding.BASE_TYPE : |
| return new ProblemMethodBinding(selector, argumentTypes, ProblemReasons.NotFound); |
| case Binding.ARRAY_TYPE : |
| unitScope.recordTypeReference(receiverType); |
| return findMethodForArray((ArrayBinding) receiverType, selector, argumentTypes, invocationSite); |
| } |
| unitScope.recordTypeReference(receiverType); |
| |
| ReferenceBinding currentType = (ReferenceBinding) receiverType; |
| if (!currentType.canBeSeenBy(this)) |
| return new ProblemMethodBinding(selector, argumentTypes, ProblemReasons.ReceiverTypeNotVisible); |
| |
| // retrieve an exact visible match (if possible) |
| MethodBinding methodBinding = findExactMethod(currentType, selector, argumentTypes, invocationSite); |
| if (methodBinding != null) return methodBinding; |
| |
| methodBinding = findMethod(currentType, selector, argumentTypes, invocationSite, false); |
| if (methodBinding == null) |
| return new ProblemMethodBinding(selector, argumentTypes, ProblemReasons.NotFound); |
| if (!methodBinding.isValidBinding()) |
| return methodBinding; |
| // MERGECONFLICT |
| // New AspectJ Extension - trying this? looked very different in the old compiler |
| if (!methodBinding.canBeSeenBy(currentType, invocationSite, this)) { |
| MethodBinding visMethodBinding = methodBinding.getVisibleBinding(invocationSite,this); |
| if (visMethodBinding == null) |
| return new ProblemMethodBinding( methodBinding, selector, methodBinding.parameters, ProblemReasons.NotVisible); |
| methodBinding = visMethodBinding; |
| } |
| // End AspectJ Extension |
| |
| // 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; |
| } |
| } |
| |
| /* 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. |
| public final TypeBinding getType(char[] name) { |
| // Would like to remove this test and require senders to specially handle base types |
| TypeBinding binding = getBaseType(name); |
| if (binding != null) return binding; |
| return (ReferenceBinding) getTypeOrPackage(name, Binding.TYPE, true); |
| } |
| |
| /* Answer the type binding that corresponds to the given name, starting the lookup in the receiver |
| * or the packageBinding if provided. |
| * The name provided is a simple source name (e.g., "Object" , "Point", ...) |
| */ |
| public final TypeBinding getType(char[] name, PackageBinding packageBinding) { |
| if (packageBinding == null) |
| return getType(name); |
| |
| Binding binding = packageBinding.getTypeOrPackage(name); |
| if (binding == null) { |
| return new ProblemReferenceBinding( |
| CharOperation.arrayConcat(packageBinding.compoundName, name), |
| null, |
| ProblemReasons.NotFound); |
| } |
| if (!binding.isValidBinding()) { |
| return new ProblemReferenceBinding( |
| binding instanceof ReferenceBinding ? ((ReferenceBinding)binding).compoundName : CharOperation.arrayConcat(packageBinding.compoundName, name), |
| binding instanceof ReferenceBinding ? (ReferenceBinding)((ReferenceBinding)binding).closestMatch() : null, |
| binding.problemId()); |
| } |
| ReferenceBinding typeBinding = (ReferenceBinding) binding; |
| if (!typeBinding.canBeSeenBy(this)) |
| return new ProblemReferenceBinding( |
| typeBinding.compoundName, |
| typeBinding, |
| ProblemReasons.NotVisible); |
| return typeBinding; |
| } |
| |
| /* Answer the type binding corresponding to the compoundName. |
| * |
| * NOTE: If a problem binding is returned, senders should extract the compound name |
| * from the binding & not assume the problem applies to the entire compoundName. |
| */ |
| public final TypeBinding getType(char[][] compoundName, int typeNameLength) { |
| if (typeNameLength == 1) { |
| // Would like to remove this test and require senders to specially handle base types |
| TypeBinding binding = getBaseType(compoundName[0]); |
| if (binding != null) return binding; |
| } |
| |
| CompilationUnitScope unitScope = compilationUnitScope(); |
| unitScope.recordQualifiedReference(compoundName); |
| Binding binding = getTypeOrPackage(compoundName[0], typeNameLength == 1 ? Binding.TYPE : Binding.TYPE | Binding.PACKAGE, true); |
| if (binding == null) { |
| char[][] qName = new char[][] { compoundName[0] }; |
| return new ProblemReferenceBinding(qName, environment().createMissingType(compilationUnitScope().getCurrentPackage(), qName), ProblemReasons.NotFound); |
| } |
| if (!binding.isValidBinding()) { |
| if (binding instanceof PackageBinding) { |
| char[][] qName = new char[][] { compoundName[0] }; |
| return new ProblemReferenceBinding( |
| qName, |
| environment().createMissingType(null, qName), |
| ProblemReasons.NotFound); |
| } |
| return (ReferenceBinding) binding; |
| } |
| int currentIndex = 1; |
| boolean checkVisibility = false; |
| if (binding instanceof PackageBinding) { |
| PackageBinding packageBinding = (PackageBinding) binding; |
| while (currentIndex < typeNameLength) { |
| binding = packageBinding.getTypeOrPackage(compoundName[currentIndex++]); // does not check visibility |
| if (binding == null) { |
| char[][] qName = CharOperation.subarray(compoundName, 0, currentIndex); |
| return new ProblemReferenceBinding( |
| qName, |
| environment().createMissingType(packageBinding, qName), |
| ProblemReasons.NotFound); |
| } |
| if (!binding.isValidBinding()) |
| return new ProblemReferenceBinding( |
| CharOperation.subarray(compoundName, 0, currentIndex), |
| binding instanceof ReferenceBinding ? (ReferenceBinding)((ReferenceBinding)binding).closestMatch() : null, |
| binding.problemId()); |
| if (!(binding instanceof PackageBinding)) |
| break; |
| packageBinding = (PackageBinding) binding; |
| } |
| if (binding instanceof PackageBinding) { |
| char[][] qName = CharOperation.subarray(compoundName, 0, currentIndex); |
| return new ProblemReferenceBinding( |
| qName, |
| environment().createMissingType(null, qName), |
| ProblemReasons.NotFound); |
| } |
| checkVisibility = true; |
| } |
| |
| // binding is now a ReferenceBinding |
| ReferenceBinding typeBinding = (ReferenceBinding) binding; |
| unitScope.recordTypeReference(typeBinding); |
| if (checkVisibility) // handles the fall through case |
| if (!typeBinding.canBeSeenBy(this)) |
| return new ProblemReferenceBinding( |
| CharOperation.subarray(compoundName, 0, currentIndex), |
| typeBinding, |
| ProblemReasons.NotVisible); |
| |
| while (currentIndex < typeNameLength) { |
| typeBinding = getMemberType(compoundName[currentIndex++], typeBinding); |
| if (!typeBinding.isValidBinding()) { |
| if (typeBinding instanceof ProblemReferenceBinding) { |
| ProblemReferenceBinding problemBinding = (ProblemReferenceBinding) typeBinding; |
| return new ProblemReferenceBinding( |
| CharOperation.subarray(compoundName, 0, currentIndex), |
| problemBinding.closestReferenceMatch(), |
| typeBinding.problemId()); |
| } |
| return new ProblemReferenceBinding( |
| CharOperation.subarray(compoundName, 0, currentIndex), |
| (ReferenceBinding)((ReferenceBinding)binding).closestMatch(), |
| typeBinding.problemId()); |
| } |
| } |
| return typeBinding; |
| } |
| |
| /* Internal use only |
| */ |
| final Binding getTypeOrPackage(char[] name, int mask, boolean needResolve) { |
| Scope scope = this; |
| MethodScope methodScope = null; |
| ReferenceBinding foundType = null; |
| boolean insideStaticContext = false; |
| boolean insideTypeAnnotation = false; |
| if ((mask & Binding.TYPE) == 0) { |
| Scope next = scope; |
| while ((next = scope.parent) != null) |
| scope = next; |
| } else { |
| boolean inheritedHasPrecedence = compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4; |
| done : while (true) { // done when a COMPILATION_UNIT_SCOPE is found |
| switch (scope.kind) { |
| case METHOD_SCOPE : |
| methodScope = (MethodScope) 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; |
| 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 |
| // ASPECTJ START |
| /* { |
| ReferenceBinding memberType = findMemberType(name, sourceType); |
| }*/ |
| ReferenceBinding memberType = sourceType==null?null:findMemberType(name,sourceType); |
| // ASPECTJ END |
| 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 = scope.findTypeVariable(name,sourceType); // AspectJ extension - was 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; |
| } |
| // ASPECTJ START |
| if (sourceType!=null) { |
| // ASPECTJ END |
| insideStaticContext |= sourceType.isStatic(); |
| // ASPECTJ START |
| } |
| // ASPECTJ END |
| insideTypeAnnotation = false; |
| // ASPECTJ START |
| if (sourceType!=null) { |
| // ASPECTJ END |
| 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; |
| } |
| // ASPECTJ START |
| } |
| // ASPECTJ END |
| 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) { |
| 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; |
| } |
| |
| 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; |
| if (!visible[j].areParametersCompatibleWith(method.parameters)) |
| continue nextVisible; |
| } |
| compilationUnitScope().recordTypeReferences(method.thrownExceptions); |
| return method; |
| } |
| return new ProblemMethodBinding(visible[0], visible[0].selector, visible[0].parameters, ProblemReasons.Ambiguous); |
| } |
| |
| // Internal use only |
| /* All methods in visible are acceptable matches for the method in question... |
| * Since the receiver type is an interface, we ignore the possibility that 2 inherited |
| * but unrelated superinterfaces may define the same method in acceptable but |
| * not identical ways... we just take the best match that we find since any class which |
| * implements the receiver interface MUST implement all signatures for the method... |
| * in which case the best match is correct. |
| * |
| * NOTE: This is different than javac... in the following example, the message send of |
| * bar(X) in class Y is supposed to be ambiguous. But any class which implements the |
| * interface I MUST implement both signatures for bar. If this class was the receiver of |
| * the message send instead of the interface I, then no problem would be reported. |
| * |
| interface I1 { |
| void bar(J j); |
| } |
| interface I2 { |
| // void bar(J j); |
| void bar(Object o); |
| } |
| interface I extends I1, I2 {} |
| interface J {} |
| |
| class X implements J {} |
| |
| class Y extends X { |
| public void foo(I i, X x) { i.bar(x); } |
| } |
| */ |
| protected final MethodBinding mostSpecificInterfaceMethodBinding(MethodBinding[] visible, int visibleSize, InvocationSite invocationSite) { |
| nextVisible : for (int i = 0; i < visibleSize; i++) { |
| MethodBinding method = visible[i]; |
| for (int j = 0; j < visibleSize; j++) { |
| if (i == j) continue; |
| if (!visible[j].areParametersCompatibleWith(method.parameters)) |
| continue nextVisible; |
| } |
| compilationUnitScope().recordTypeReferences(method.thrownExceptions); |
| return method; |
| } |
| return new ProblemMethodBinding(visible[0], visible[0].selector, visible[0].parameters, ProblemReasons.Ambiguous); |
| } |
| |
| // caveat: this is not a direct implementation of JLS |
| protected final MethodBinding mostSpecificMethodBinding(MethodBinding[] visible, int visibleSize, TypeBinding[] argumentTypes, 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; |
| 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++; |
| } |
| } |
| // 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; |
| 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(); |
| // ASPECTJ EXTENSION |
| // if (TypeBinding.equalsEquals(original.declaringClass, original2.declaringClass)) |
| if (TypeBinding.equalsEquals(original.getOwningClass(),original2.getOwningClass())) |
| break nextSpecific; // duplicates thru substitution |
| |
| if (!original.isAbstract()) { |
| if (original2.isAbstract() || original2.isDefaultMethod()) |
| continue; // only compare current against other concrete methods |
| // AspectJ Extension - moved this test down into the if block (pr233838) |
| //original2 = original.findOriginalInheritedMethod(original2); |
| //if (original2 == null) |
| // continue nextSpecific; // current's declaringClass is not a subtype of next's declaringClass |
| // AspectJ Extension End |
| if (current.hasSubstitutedParameters() || original.typeVariables != Binding.NO_TYPE_VARIABLES) { |
| // AspectJ Extension - from above |
| original2 = original.findOriginalInheritedMethod(original2); |
| if (original2 == null) |
| continue nextSpecific; // current's declaringClass is not a subtype of next's declaringClass |
| // AspectJ Extension End |
| 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.getOwningClass().erasure());// AspectJ Extension - was declaringClass, now getOwningClass() |
| 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; |
| |
| 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; |
| } |
| 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; |
| } |
| } |
| 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(); |
| |
| public final CompilationUnitDeclaration referenceCompilationUnit() { |
| Scope scope, unitScope = this; |
| while ((scope = unitScope.parent) != null) |
| unitScope = scope; |
| return ((CompilationUnitScope) unitScope).referenceContext; |
| } |
| |
| /** |
| * Returns the nearest reference context, starting from current scope. |
| * If starting on a class, it will return current class. If starting on unitScope, returns unit. |
| */ |
| public ReferenceContext referenceContext() { |
| Scope current = this; |
| do { |
| switch(current.kind) { |
| case METHOD_SCOPE : |
| return ((MethodScope) current).referenceContext; |
| case CLASS_SCOPE : |
| return ((ClassScope) current).referenceContext; |
| case COMPILATION_UNIT_SCOPE : |
| return ((CompilationUnitScope) current).referenceContext; |
| } |
| } while ((current = current.parent) != null); |
| return null; |
| } |
| |
| /** |
| * 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; |
| } |
| }; |
| |
| // 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; |
| } |
| |
| // AspectJ Extension |
| /** |
| * Other scopes can override this method |
| */ |
| public TypeVariableBinding findTypeVariable(char[] name, SourceTypeBinding sourceType) { |
| return sourceType.getTypeVariable(name); |
| } |
| // End AspectJ Extension |
| |
| } |