| /******************************************************************************* |
| * 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 |
| * |
| * This is an implementation of an early-draft specification developed under the Java |
| * Community Process (JCP) and is made available for testing and evaluation purposes |
| * only. The code is not compatible with any specification of the JCP. |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Stephan Herrmann - Contributions for |
| * bug 349326 - [1.7] new warning for missing try-with-resources |
| * bug 186342 - [compiler][null] Using annotations for null checking |
| * bug 365519 - editorial cleanup after bug 186342 and bug 365387 |
| * bug 358903 - Filter practically unimportant resource leak warnings |
| * bug 365531 - [compiler][null] investigate alternative strategy for internally encoding nullness defaults |
| * bug 388281 - [compiler][null] inheritance of null annotations as an option |
| * bug 395002 - Self bound generic class doesn't resolve bounds properly for wildcards for certain parametrisation. |
| * bug 392862 - [1.8][compiler][null] Evaluate null annotations on array types |
| * bug 400421 - [compiler] Null analysis for fields does not take @com.google.inject.Inject into account |
| * bug 382069 - [null] Make the null analysis consider JUnit's assertNotNull similarly to assertions |
| * bug 392384 - [1.8][compiler][null] Restore nullness info from type annotations in class files |
| * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis |
| * Bug 415291 - [1.8][null] differentiate type incompatibilities due to null annotations |
| * Bug 415043 - [1.8][null] Follow-up re null type annotations after bug 392099 |
| * 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 423504 - [1.8] Implement "18.5.3 Functional Interface Parameterization Inference" |
| * Bug 426792 - [1.8][inference][impl] generify new type inference engine |
| * Bug 428019 - [1.8][compiler] Type inference failure with nested generic invocation. |
| * Jesper S Moller - Contributions for |
| * bug 382701 - [1.8][compiler] Implement semantic analysis of Lambda expressions & Reference expression |
| * bug 412153 - [1.8][compiler] Check validity of annotations which may be repeatable |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.compiler.lookup; |
| |
| import java.util.Arrays; |
| import java.util.Comparator; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.core.compiler.InvalidInputException; |
| import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; |
| import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable; |
| |
| /* |
| Not all fields defined by this type (& its subclasses) are initialized when it is created. |
| Some are initialized only when needed. |
| |
| Accessors have been provided for some public fields so all TypeBindings have the same API... |
| but access public fields directly whenever possible. |
| Non-public fields have accessors which should be used everywhere you expect the field to be initialized. |
| |
| null is NOT a valid value for a non-public field... it just means the field is not initialized. |
| */ |
| |
| abstract public class ReferenceBinding extends TypeBinding { |
| |
| public char[][] compoundName; |
| public char[] sourceName; |
| public int modifiers; |
| public PackageBinding fPackage; |
| char[] fileName; |
| char[] constantPoolName; |
| char[] signature; |
| |
| private SimpleLookupTable compatibleCache; |
| |
| int typeBits; // additional bits characterizing this type |
| protected MethodBinding singleAbstractMethod; |
| |
| public static final ReferenceBinding LUB_GENERIC = new ReferenceBinding() { /* used for lub computation */ |
| { this.id = TypeIds.T_undefined; } |
| public boolean hasTypeBit(int bit) { return false; } |
| }; |
| |
| private static final Comparator<FieldBinding> FIELD_COMPARATOR = new Comparator<FieldBinding>() { |
| public int compare(FieldBinding o1, FieldBinding o2) { |
| char[] n1 = o1.name; |
| char[] n2 = o2.name; |
| return ReferenceBinding.compare(n1, n2, n1.length, n2.length); |
| } |
| }; |
| private static final Comparator<MethodBinding> METHOD_COMPARATOR = new Comparator<MethodBinding>() { |
| public int compare(MethodBinding o1, MethodBinding o2) { |
| MethodBinding m1 = o1; |
| MethodBinding m2 = o2; |
| char[] s1 = m1.selector; |
| char[] s2 = m2.selector; |
| int c = ReferenceBinding.compare(s1, s2, s1.length, s2.length); |
| return c == 0 ? m1.parameters.length - m2.parameters.length : c; |
| } |
| }; |
| static protected ProblemMethodBinding samProblemBinding = new ProblemMethodBinding(TypeConstants.ANONYMOUS_METHOD, null, ProblemReasons.NoSuchSingleAbstractMethod); |
| |
| |
| public ReferenceBinding(ReferenceBinding prototype) { |
| super(prototype); |
| |
| this.compoundName = prototype.compoundName; |
| this.sourceName = prototype.sourceName; |
| this.modifiers = prototype.modifiers; |
| this.fPackage = prototype.fPackage; |
| this.fileName = prototype.fileName; |
| this.constantPoolName = prototype.constantPoolName; |
| this.signature = prototype.signature; |
| this.compatibleCache = prototype.compatibleCache; |
| this.typeBits = prototype.typeBits; |
| this.singleAbstractMethod = prototype.singleAbstractMethod; |
| } |
| |
| public ReferenceBinding() { |
| super(); |
| } |
| |
| public static FieldBinding binarySearch(char[] name, FieldBinding[] sortedFields) { |
| if (sortedFields == null) |
| return null; |
| int max = sortedFields.length; |
| if (max == 0) |
| return null; |
| int left = 0, right = max - 1, nameLength = name.length; |
| int mid = 0; |
| char[] midName; |
| while (left <= right) { |
| mid = left + (right - left) /2; |
| int compare = compare(name, midName = sortedFields[mid].name, nameLength, midName.length); |
| if (compare < 0) { |
| right = mid-1; |
| } else if (compare > 0) { |
| left = mid+1; |
| } else { |
| return sortedFields[mid]; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Returns a combined range value representing: (start + (end<<32)), where start is the index of the first matching method |
| * (remember methods are sorted alphabetically on selectors), and end is the index of last contiguous methods with same |
| * selector. |
| * -1 means no method got found |
| * @param selector |
| * @param sortedMethods |
| * @return (start + (end<<32)) or -1 if no method found |
| */ |
| public static long binarySearch(char[] selector, MethodBinding[] sortedMethods) { |
| if (sortedMethods == null) |
| return -1; |
| int max = sortedMethods.length; |
| if (max == 0) |
| return -1; |
| int left = 0, right = max - 1, selectorLength = selector.length; |
| int mid = 0; |
| char[] midSelector; |
| while (left <= right) { |
| mid = left + (right - left) /2; |
| int compare = compare(selector, midSelector = sortedMethods[mid].selector, selectorLength, midSelector.length); |
| if (compare < 0) { |
| right = mid-1; |
| } else if (compare > 0) { |
| left = mid+1; |
| } else { |
| int start = mid, end = mid; |
| // find first method with same selector |
| while (start > left && CharOperation.equals(sortedMethods[start-1].selector, selector)){ start--; } |
| // find last method with same selector |
| while (end < right && CharOperation.equals(sortedMethods[end+1].selector, selector)){ end++; } |
| return start + ((long)end<< 32); |
| } |
| } |
| return -1; |
| } |
| |
| /** |
| * Compares two strings lexicographically. |
| * The comparison is based on the Unicode value of each character in |
| * the strings. |
| * |
| * @return the value <code>0</code> if the str1 is equal to str2; |
| * a value less than <code>0</code> if str1 |
| * is lexicographically less than str2; |
| * and a value greater than <code>0</code> if str1 is |
| * lexicographically greater than str2. |
| */ |
| static int compare(char[] str1, char[] str2, int len1, int len2) { |
| int n= Math.min(len1, len2); |
| int i= 0; |
| while (n-- != 0) { |
| char c1= str1[i]; |
| char c2= str2[i++]; |
| if (c1 != c2) { |
| return c1 - c2; |
| } |
| } |
| return len1 - len2; |
| } |
| |
| /** |
| * Sort the field array using a quicksort |
| */ |
| public static void sortFields(FieldBinding[] sortedFields, int left, int right) { |
| Arrays.sort(sortedFields, left, right, FIELD_COMPARATOR); |
| } |
| |
| /** |
| * Sort the field array using a quicksort |
| */ |
| public static void sortMethods(MethodBinding[] sortedMethods, int left, int right) { |
| Arrays.sort(sortedMethods, left, right, METHOD_COMPARATOR); |
| } |
| |
| /** |
| * Return the array of resolvable fields (resilience) |
| */ |
| public FieldBinding[] availableFields() { |
| return fields(); |
| } |
| |
| /** |
| * Return the array of resolvable methods (resilience) |
| */ |
| public MethodBinding[] availableMethods() { |
| return methods(); |
| } |
| |
| /** |
| * Answer true if the receiver can be instantiated |
| */ |
| public boolean canBeInstantiated() { |
| return (this.modifiers & (ClassFileConstants.AccAbstract | ClassFileConstants.AccInterface | ClassFileConstants.AccEnum | ClassFileConstants.AccAnnotation)) == 0; |
| } |
| |
| /** |
| * Answer true if the receiver is visible to the invocationPackage. |
| */ |
| public boolean canBeSeenBy(PackageBinding invocationPackage) { |
| if (isPublic()) return true; |
| if (isPrivate()) return false; |
| |
| // isProtected() or isDefault() |
| return invocationPackage == this.fPackage; |
| } |
| |
| /** |
| * Answer true if the receiver is visible to the receiverType and the invocationType. |
| */ |
| public boolean canBeSeenBy(ReferenceBinding receiverType, ReferenceBinding invocationType) { |
| if (isPublic()) return true; |
| |
| if (TypeBinding.equalsEquals(invocationType, this) && TypeBinding.equalsEquals(invocationType, receiverType)) return true; |
| |
| if (isProtected()) { |
| // answer true if the invocationType is the declaringClass or they are in the same package |
| // OR the invocationType is a subclass of the declaringClass |
| // AND the invocationType is the invocationType or its subclass |
| // OR the type is a static method accessed directly through a type |
| // OR previous assertions are true for one of the enclosing type |
| if (TypeBinding.equalsEquals(invocationType, this)) return true; |
| if (invocationType.fPackage == this.fPackage) return true; |
| |
| TypeBinding currentType = invocationType.erasure(); |
| TypeBinding declaringClass = enclosingType().erasure(); // protected types always have an enclosing one |
| if (TypeBinding.equalsEquals(declaringClass, invocationType)) return true; |
| if (declaringClass == null) return false; // could be null if incorrect top-level protected type |
| //int depth = 0; |
| do { |
| if (currentType.findSuperTypeOriginatingFrom(declaringClass) != null) return true; |
| //depth++; |
| currentType = currentType.enclosingType(); |
| } while (currentType != null); |
| return false; |
| } |
| |
| if (isPrivate()) { |
| // answer true if the receiverType is the receiver or its enclosingType |
| // AND the invocationType and the receiver have a common enclosingType |
| receiverCheck: { |
| if (!(TypeBinding.equalsEquals(receiverType, this) || TypeBinding.equalsEquals(receiverType, enclosingType()))) { |
| // special tolerance for type variable direct bounds, but only if compliance <= 1.6, see: https://bugs.eclipse.org/bugs/show_bug.cgi?id=334622 |
| if (receiverType.isTypeVariable()) { |
| TypeVariableBinding typeVariable = (TypeVariableBinding) receiverType; |
| if (typeVariable.environment.globalOptions.complianceLevel <= ClassFileConstants.JDK1_6 && (typeVariable.isErasureBoundTo(erasure()) || typeVariable.isErasureBoundTo(enclosingType().erasure()))) |
| break receiverCheck; |
| } |
| return false; |
| } |
| } |
| |
| if (TypeBinding.notEquals(invocationType, this)) { |
| ReferenceBinding outerInvocationType = invocationType; |
| ReferenceBinding temp = outerInvocationType.enclosingType(); |
| while (temp != null) { |
| outerInvocationType = temp; |
| temp = temp.enclosingType(); |
| } |
| |
| ReferenceBinding outerDeclaringClass = (ReferenceBinding)erasure(); |
| temp = outerDeclaringClass.enclosingType(); |
| while (temp != null) { |
| outerDeclaringClass = temp; |
| temp = temp.enclosingType(); |
| } |
| if (TypeBinding.notEquals(outerInvocationType, outerDeclaringClass)) return false; |
| } |
| return true; |
| } |
| |
| // isDefault() |
| if (invocationType.fPackage != this.fPackage) return false; |
| |
| ReferenceBinding currentType = receiverType; |
| TypeBinding originalDeclaringClass = (enclosingType() == null ? this : enclosingType()).original(); |
| do { |
| if (currentType.isCapture()) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=285002 |
| if (TypeBinding.equalsEquals(originalDeclaringClass, currentType.erasure().original())) return true; |
| } else { |
| if (TypeBinding.equalsEquals(originalDeclaringClass, currentType.original())) return true; |
| } |
| PackageBinding currentPackage = currentType.fPackage; |
| // package could be null for wildcards/intersection types, ignore and recurse in superclass |
| if (currentPackage != null && currentPackage != this.fPackage) return false; |
| } while ((currentType = currentType.superclass()) != null); |
| return false; |
| } |
| |
| /** |
| * Answer true if the receiver is visible to the type provided by the scope. |
| */ |
| public boolean canBeSeenBy(Scope scope) { |
| if (isPublic()) return true; |
| |
| SourceTypeBinding invocationType = scope.enclosingSourceType(); |
| if (TypeBinding.equalsEquals(invocationType, this)) return true; |
| |
| if (invocationType == null) // static import call |
| return !isPrivate() && scope.getCurrentPackage() == this.fPackage; |
| |
| if (isProtected()) { |
| // answer true if the invocationType is the declaringClass or they are in the same package |
| // OR the invocationType is a subclass of the declaringClass |
| // AND the invocationType is the invocationType or its subclass |
| // OR the type is a static method accessed directly through a type |
| // OR previous assertions are true for one of the enclosing type |
| if (invocationType.fPackage == this.fPackage) return true; |
| |
| TypeBinding declaringClass = enclosingType(); // protected types always have an enclosing one |
| if (declaringClass == null) return false; // could be null if incorrect top-level protected type |
| declaringClass = declaringClass.erasure();// erasure cannot be null |
| TypeBinding currentType = invocationType.erasure(); |
| // int depth = 0; |
| do { |
| if (TypeBinding.equalsEquals(declaringClass, invocationType)) return true; |
| if (currentType.findSuperTypeOriginatingFrom(declaringClass) != null) return true; |
| // depth++; |
| currentType = currentType.enclosingType(); |
| } while (currentType != null); |
| return false; |
| } |
| if (isPrivate()) { |
| // answer true if the receiver and the invocationType have a common enclosingType |
| // already know they are not the identical type |
| ReferenceBinding outerInvocationType = invocationType; |
| ReferenceBinding temp = outerInvocationType.enclosingType(); |
| while (temp != null) { |
| outerInvocationType = temp; |
| temp = temp.enclosingType(); |
| } |
| |
| ReferenceBinding outerDeclaringClass = (ReferenceBinding)erasure(); |
| temp = outerDeclaringClass.enclosingType(); |
| while (temp != null) { |
| outerDeclaringClass = temp; |
| temp = temp.enclosingType(); |
| } |
| return TypeBinding.equalsEquals(outerInvocationType, outerDeclaringClass); |
| } |
| |
| // isDefault() |
| return invocationType.fPackage == this.fPackage; |
| } |
| |
| public char[] computeGenericTypeSignature(TypeVariableBinding[] typeVariables) { |
| |
| boolean isMemberOfGeneric = isMemberType() && (enclosingType().modifiers & ExtraCompilerModifiers.AccGenericSignature) != 0; |
| if (typeVariables == Binding.NO_TYPE_VARIABLES && !isMemberOfGeneric) { |
| return signature(); |
| } |
| StringBuffer sig = new StringBuffer(10); |
| if (isMemberOfGeneric) { |
| char[] typeSig = enclosingType().genericTypeSignature(); |
| sig.append(typeSig, 0, typeSig.length-1); // copy all but trailing semicolon |
| sig.append('.'); // NOTE: cannot override trailing ';' with '.' in enclosing signature, since shared char[] |
| sig.append(this.sourceName); |
| } else { |
| char[] typeSig = signature(); |
| sig.append(typeSig, 0, typeSig.length-1); // copy all but trailing semicolon |
| } |
| if (typeVariables == Binding.NO_TYPE_VARIABLES) { |
| sig.append(';'); |
| } else { |
| sig.append('<'); |
| for (int i = 0, length = typeVariables.length; i < length; i++) { |
| sig.append(typeVariables[i].genericTypeSignature()); |
| } |
| sig.append(">;"); //$NON-NLS-1$ |
| } |
| int sigLength = sig.length(); |
| char[] result = new char[sigLength]; |
| sig.getChars(0, sigLength, result, 0); |
| return result; |
| } |
| |
| public void computeId() { |
| // note that more (configurable) ids are assigned from PackageBinding#checkIfNullAnnotationType() |
| |
| // try to avoid multiple checks against a package/type name |
| switch (this.compoundName.length) { |
| |
| case 3 : |
| char[] packageName = this.compoundName[0]; |
| // expect only java.*.* and javax.*.* and junit.*.* and org.junit.* |
| switch (packageName.length) { |
| case 3: // only one type in this group, yet: |
| if (CharOperation.equals(TypeConstants.ORG_JUNIT_ASSERT, this.compoundName)) |
| this.id = TypeIds.T_OrgJunitAssert; |
| return; |
| case 4: |
| if (!CharOperation.equals(TypeConstants.JAVA, packageName)) |
| return; |
| break; // continue below ... |
| case 5: |
| switch (packageName[1]) { |
| case 'a': |
| if (CharOperation.equals(TypeConstants.JAVAX_ANNOTATION_INJECT_INJECT, this.compoundName)) |
| this.id = TypeIds.T_JavaxInjectInject; |
| return; |
| case 'u': |
| if (CharOperation.equals(TypeConstants.JUNIT_FRAMEWORK_ASSERT, this.compoundName)) |
| this.id = TypeIds.T_JunitFrameworkAssert; |
| return; |
| } |
| return; |
| default: return; |
| } |
| // ... at this point we know it's java.*.* |
| |
| packageName = this.compoundName[1]; |
| if (packageName.length == 0) return; // just to be safe |
| char[] typeName = this.compoundName[2]; |
| if (typeName.length == 0) return; // just to be safe |
| // remaining types MUST be in java.*.* |
| if (!CharOperation.equals(TypeConstants.LANG, this.compoundName[1])) { |
| switch (packageName[0]) { |
| case 'i' : |
| if (CharOperation.equals(packageName, TypeConstants.IO)) { |
| switch (typeName[0]) { |
| case 'C' : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_IO_CLOSEABLE[2])) |
| this.typeBits |= TypeIds.BitCloseable; // don't assign id, only typeBit (for analysis of resource leaks) |
| return; |
| case 'E' : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_IO_EXTERNALIZABLE[2])) |
| this.id = TypeIds.T_JavaIoExternalizable; |
| return; |
| case 'I' : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_IO_IOEXCEPTION[2])) |
| this.id = TypeIds.T_JavaIoException; |
| return; |
| case 'O' : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_IO_OBJECTSTREAMEXCEPTION[2])) |
| this.id = TypeIds.T_JavaIoObjectStreamException; |
| return; |
| case 'P' : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_IO_PRINTSTREAM[2])) |
| this.id = TypeIds.T_JavaIoPrintStream; |
| return; |
| case 'S' : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_IO_SERIALIZABLE[2])) |
| this.id = TypeIds.T_JavaIoSerializable; |
| return; |
| } |
| } |
| return; |
| case 'u' : |
| if (CharOperation.equals(packageName, TypeConstants.UTIL)) { |
| switch (typeName[0]) { |
| case 'C' : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_UTIL_COLLECTION[2])) |
| this.id = TypeIds.T_JavaUtilCollection; |
| return; |
| case 'I' : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_UTIL_ITERATOR[2])) |
| this.id = TypeIds.T_JavaUtilIterator; |
| return; |
| case 'O' : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_UTIL_OBJECTS[2])) |
| this.id = TypeIds.T_JavaUtilObjects; |
| return; |
| } |
| } |
| return; |
| } |
| return; |
| } |
| |
| // remaining types MUST be in java.lang.* |
| switch (typeName[0]) { |
| case 'A' : |
| switch(typeName.length) { |
| case 13 : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_AUTOCLOSEABLE[2])) { |
| this.id = TypeIds.T_JavaLangAutoCloseable; |
| this.typeBits |= TypeIds.BitAutoCloseable; |
| } |
| return; |
| case 14: |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_ASSERTIONERROR[2])) |
| this.id = TypeIds.T_JavaLangAssertionError; |
| return; |
| } |
| return; |
| case 'B' : |
| switch (typeName.length) { |
| case 4 : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_BYTE[2])) |
| this.id = TypeIds.T_JavaLangByte; |
| return; |
| case 7 : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_BOOLEAN[2])) |
| this.id = TypeIds.T_JavaLangBoolean; |
| return; |
| } |
| return; |
| case 'C' : |
| switch (typeName.length) { |
| case 5 : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_CLASS[2])) |
| this.id = TypeIds.T_JavaLangClass; |
| return; |
| case 9 : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_CHARACTER[2])) |
| this.id = TypeIds.T_JavaLangCharacter; |
| else if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_CLONEABLE[2])) |
| this.id = TypeIds.T_JavaLangCloneable; |
| return; |
| case 22 : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_CLASSNOTFOUNDEXCEPTION[2])) |
| this.id = TypeIds.T_JavaLangClassNotFoundException; |
| return; |
| } |
| return; |
| case 'D' : |
| switch (typeName.length) { |
| case 6 : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_DOUBLE[2])) |
| this.id = TypeIds.T_JavaLangDouble; |
| return; |
| case 10 : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_DEPRECATED[2])) |
| this.id = TypeIds.T_JavaLangDeprecated; |
| return; |
| } |
| return; |
| case 'E' : |
| switch (typeName.length) { |
| case 4 : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_ENUM[2])) |
| this.id = TypeIds.T_JavaLangEnum; |
| return; |
| case 5 : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_ERROR[2])) |
| this.id = TypeIds.T_JavaLangError; |
| return; |
| case 9 : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_EXCEPTION[2])) |
| this.id = TypeIds.T_JavaLangException; |
| return; |
| } |
| return; |
| case 'F' : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_FLOAT[2])) |
| this.id = TypeIds.T_JavaLangFloat; |
| else if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_FUNCTIONAL_INTERFACE[2])) |
| this.id = TypeIds.T_JavaLangFunctionalInterface; |
| return; |
| case 'I' : |
| switch (typeName.length) { |
| case 7 : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_INTEGER[2])) |
| this.id = TypeIds.T_JavaLangInteger; |
| return; |
| case 8 : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_ITERABLE[2])) |
| this.id = TypeIds.T_JavaLangIterable; |
| return; |
| case 24 : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_ILLEGALARGUMENTEXCEPTION[2])) |
| this.id = TypeIds.T_JavaLangIllegalArgumentException; |
| return; |
| } |
| return; |
| case 'L' : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_LONG[2])) |
| this.id = TypeIds.T_JavaLangLong; |
| return; |
| case 'N' : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_NOCLASSDEFERROR[2])) |
| this.id = TypeIds.T_JavaLangNoClassDefError; |
| return; |
| case 'O' : |
| switch (typeName.length) { |
| case 6 : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_OBJECT[2])) |
| this.id = TypeIds.T_JavaLangObject; |
| return; |
| case 8 : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_OVERRIDE[2])) |
| this.id = TypeIds.T_JavaLangOverride; |
| return; |
| } |
| return; |
| case 'R' : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_RUNTIMEEXCEPTION[2])) |
| this.id = TypeIds.T_JavaLangRuntimeException; |
| break; |
| case 'S' : |
| switch (typeName.length) { |
| case 5 : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_SHORT[2])) |
| this.id = TypeIds.T_JavaLangShort; |
| return; |
| case 6 : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_STRING[2])) |
| this.id = TypeIds.T_JavaLangString; |
| else if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_SYSTEM[2])) |
| this.id = TypeIds.T_JavaLangSystem; |
| return; |
| case 11 : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_SAFEVARARGS[2])) |
| this.id = TypeIds.T_JavaLangSafeVarargs; |
| return; |
| case 12 : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_STRINGBUFFER[2])) |
| this.id = TypeIds.T_JavaLangStringBuffer; |
| return; |
| case 13 : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_STRINGBUILDER[2])) |
| this.id = TypeIds.T_JavaLangStringBuilder; |
| return; |
| case 16 : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_SUPPRESSWARNINGS[2])) |
| this.id = TypeIds.T_JavaLangSuppressWarnings; |
| return; |
| } |
| return; |
| case 'T' : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_THROWABLE[2])) |
| this.id = TypeIds.T_JavaLangThrowable; |
| return; |
| case 'V' : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_VOID[2])) |
| this.id = TypeIds.T_JavaLangVoid; |
| return; |
| } |
| break; |
| |
| case 4: |
| // expect one type from com.*.*.*: |
| if (CharOperation.equals(TypeConstants.COM_GOOGLE_INJECT_INJECT, this.compoundName)) { |
| this.id = TypeIds.T_ComGoogleInjectInject; |
| return; |
| } |
| // otherwise only expect java.*.*.* |
| if (!CharOperation.equals(TypeConstants.JAVA, this.compoundName[0])) |
| return; |
| packageName = this.compoundName[1]; |
| if (packageName.length == 0) return; // just to be safe |
| |
| packageName = this.compoundName[2]; |
| if (packageName.length == 0) return; // just to be safe |
| typeName = this.compoundName[3]; |
| if (typeName.length == 0) return; // just to be safe |
| switch (packageName[0]) { |
| case 'a' : |
| if (CharOperation.equals(packageName, TypeConstants.ANNOTATION)) { |
| switch (typeName[0]) { |
| case 'A' : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_ANNOTATION_ANNOTATION[3])) |
| this.id = TypeIds.T_JavaLangAnnotationAnnotation; |
| return; |
| case 'D' : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_ANNOTATION_DOCUMENTED[3])) |
| this.id = TypeIds.T_JavaLangAnnotationDocumented; |
| return; |
| case 'E' : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_ANNOTATION_ELEMENTTYPE[3])) |
| this.id = TypeIds.T_JavaLangAnnotationElementType; |
| return; |
| case 'I' : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_ANNOTATION_INHERITED[3])) |
| this.id = TypeIds.T_JavaLangAnnotationInherited; |
| return; |
| case 'R' : |
| switch (typeName.length) { |
| case 9 : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_ANNOTATION_RETENTION[3])) |
| this.id = TypeIds.T_JavaLangAnnotationRetention; |
| return; |
| case 10 : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_ANNOTATION_REPEATABLE[3])) |
| this.id = TypeIds.T_JavaLangAnnotationRepeatable; |
| return; |
| case 15 : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_ANNOTATION_RETENTIONPOLICY[3])) |
| this.id = TypeIds.T_JavaLangAnnotationRetentionPolicy; |
| return; |
| } |
| return; |
| case 'T' : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_ANNOTATION_TARGET[3])) |
| this.id = TypeIds.T_JavaLangAnnotationTarget; |
| return; |
| } |
| } |
| return; |
| case 'i': |
| if (CharOperation.equals(packageName, TypeConstants.INVOKE)) { |
| if (typeName.length == 0) return; // just to be safe |
| switch (typeName[0]) { |
| case 'M' : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_INVOKE_METHODHANDLE_$_POLYMORPHICSIGNATURE[3])) |
| this.id = TypeIds.T_JavaLangInvokeMethodHandlePolymorphicSignature; |
| return; |
| } |
| } |
| return; |
| case 'r' : |
| if (CharOperation.equals(packageName, TypeConstants.REFLECT)) { |
| switch (typeName[0]) { |
| case 'C' : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_REFLECT_CONSTRUCTOR[2])) |
| this.id = TypeIds.T_JavaLangReflectConstructor; |
| return; |
| case 'F' : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_REFLECT_FIELD[2])) |
| this.id = TypeIds.T_JavaLangReflectField; |
| return; |
| case 'M' : |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_REFLECT_METHOD[2])) |
| this.id = TypeIds.T_JavaLangReflectMethod; |
| return; |
| } |
| } |
| return; |
| } |
| break; |
| case 5 : |
| packageName = this.compoundName[0]; |
| switch (packageName[0]) { |
| case 'j' : |
| if (!CharOperation.equals(TypeConstants.JAVA, this.compoundName[0])) |
| return; |
| packageName = this.compoundName[1]; |
| if (packageName.length == 0) return; // just to be safe |
| |
| if (CharOperation.equals(TypeConstants.LANG, packageName)) { |
| packageName = this.compoundName[2]; |
| if (packageName.length == 0) return; // just to be safe |
| switch (packageName[0]) { |
| case 'i' : |
| if (CharOperation.equals(packageName, TypeConstants.INVOKE)) { |
| typeName = this.compoundName[3]; |
| if (typeName.length == 0) return; // just to be safe |
| switch (typeName[0]) { |
| case 'M' : |
| char[] memberTypeName = this.compoundName[4]; |
| if (memberTypeName.length == 0) return; // just to be safe |
| if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_INVOKE_METHODHANDLE_POLYMORPHICSIGNATURE[3]) |
| && CharOperation.equals(memberTypeName, TypeConstants.JAVA_LANG_INVOKE_METHODHANDLE_POLYMORPHICSIGNATURE[4])) |
| this.id = TypeIds.T_JavaLangInvokeMethodHandlePolymorphicSignature; |
| return; |
| } |
| } |
| return; |
| } |
| return; |
| } |
| return; |
| case 'o': |
| if (!CharOperation.equals(TypeConstants.ORG, this.compoundName[0])) |
| return; |
| packageName = this.compoundName[1]; |
| if (packageName.length == 0) return; // just to be safe |
| |
| switch (packageName[0]) { |
| case 'e': |
| if (CharOperation.equals(TypeConstants.ECLIPSE, packageName)) { |
| packageName = this.compoundName[2]; |
| if (packageName.length == 0) return; // just to be safe |
| switch (packageName[0]) { |
| case 'c' : |
| if (CharOperation.equals(packageName, TypeConstants.CORE)) { |
| typeName = this.compoundName[3]; |
| if (typeName.length == 0) return; // just to be safe |
| switch (typeName[0]) { |
| case 'r' : |
| char[] memberTypeName = this.compoundName[4]; |
| if (memberTypeName.length == 0) return; // just to be safe |
| if (CharOperation.equals(typeName, TypeConstants.ORG_ECLIPSE_CORE_RUNTIME_ASSERT[3]) |
| && CharOperation.equals(memberTypeName, TypeConstants.ORG_ECLIPSE_CORE_RUNTIME_ASSERT[4])) |
| this.id = TypeIds.T_OrgEclipseCoreRuntimeAssert; |
| return; |
| } |
| } |
| return; |
| } |
| return; |
| } |
| return; |
| case 'a': |
| if (CharOperation.equals(TypeConstants.APACHE, packageName)) { |
| if (CharOperation.equals(TypeConstants.COMMONS, this.compoundName[2])) { |
| if (CharOperation.equals(TypeConstants.ORG_APACHE_COMMONS_LANG_VALIDATE, this.compoundName)) |
| this.id = TypeIds.T_OrgApacheCommonsLangValidate; |
| else if (CharOperation.equals(TypeConstants.ORG_APACHE_COMMONS_LANG3_VALIDATE, this.compoundName)) |
| this.id = TypeIds.T_OrgApacheCommonsLang3Validate; |
| } |
| } |
| return; |
| } |
| return; |
| case 'c': |
| if (!CharOperation.equals(TypeConstants.COM, this.compoundName[0])) |
| return; |
| if (CharOperation.equals(TypeConstants.COM_GOOGLE_COMMON_BASE_PRECONDITIONS, this.compoundName)) |
| this.id = TypeIds.T_ComGoogleCommonBasePreconditions; |
| return; |
| } |
| break; |
| case 6: |
| if (!CharOperation.equals(TypeConstants.JDT, this.compoundName[2]) || !CharOperation.equals(TypeConstants.ITYPEBINDING, this.compoundName[5])) |
| return; |
| if (CharOperation.equals(TypeConstants.ORG_ECLIPSE_JDT_CORE_DOM_ITYPEBINDING, this.compoundName)) |
| this.typeBits |= TypeIds.BitUninternedType; |
| break; |
| case 7 : |
| if (!CharOperation.equals(TypeConstants.JDT, this.compoundName[2]) || !CharOperation.equals(TypeConstants.TYPEBINDING, this.compoundName[6])) |
| return; |
| if (CharOperation.equals(TypeConstants.ORG_ECLIPSE_JDT_INTERNAL_COMPILER_LOOKUP_TYPEBINDING, this.compoundName)) |
| this.typeBits |= TypeIds.BitUninternedType; |
| break; |
| } |
| } |
| |
| /** |
| * p.X<T extends Y & I, U extends Y> {} -> Lp/X<TT;TU;>; |
| */ |
| public char[] computeUniqueKey(boolean isLeaf) { |
| if (!isLeaf) return signature(); |
| return genericTypeSignature(); |
| } |
| |
| /** |
| * Answer the receiver's constant pool name. |
| * |
| * NOTE: This method should only be used during/after code gen. |
| */ |
| public char[] constantPoolName() /* java/lang/Object */ { |
| if (this.constantPoolName != null) return this.constantPoolName; |
| return this.constantPoolName = CharOperation.concatWith(this.compoundName, '/'); |
| } |
| |
| public String debugName() { |
| return (this.compoundName != null) ? this.hasTypeAnnotations() ? annotatedDebugName() : new String(readableName()) : "UNNAMED TYPE"; //$NON-NLS-1$ |
| } |
| |
| public int depth() { |
| int depth = 0; |
| ReferenceBinding current = this; |
| while ((current = current.enclosingType()) != null) |
| depth++; |
| return depth; |
| } |
| |
| public boolean detectAnnotationCycle() { |
| if ((this.tagBits & TagBits.EndAnnotationCheck) != 0) return false; // already checked |
| if ((this.tagBits & TagBits.BeginAnnotationCheck) != 0) return true; // in the middle of checking its methods |
| |
| this.tagBits |= TagBits.BeginAnnotationCheck; |
| MethodBinding[] currentMethods = methods(); |
| boolean inCycle = false; // check each method before failing |
| for (int i = 0, l = currentMethods.length; i < l; i++) { |
| TypeBinding returnType = currentMethods[i].returnType.leafComponentType().erasure(); |
| if (TypeBinding.equalsEquals(this, returnType)) { |
| if (this instanceof SourceTypeBinding) { |
| MethodDeclaration decl = (MethodDeclaration) currentMethods[i].sourceMethod(); |
| ((SourceTypeBinding) this).scope.problemReporter().annotationCircularity(this, this, decl != null ? decl.returnType : null); |
| } |
| } else if (returnType.isAnnotationType() && ((ReferenceBinding) returnType).detectAnnotationCycle()) { |
| if (this instanceof SourceTypeBinding) { |
| MethodDeclaration decl = (MethodDeclaration) currentMethods[i].sourceMethod(); |
| ((SourceTypeBinding) this).scope.problemReporter().annotationCircularity(this, returnType, decl != null ? decl.returnType : null); |
| } |
| inCycle = true; |
| } |
| } |
| if (inCycle) |
| return true; |
| this.tagBits |= TagBits.EndAnnotationCheck; |
| return false; |
| } |
| |
| public final ReferenceBinding enclosingTypeAt(int relativeDepth) { |
| ReferenceBinding current = this; |
| while (relativeDepth-- > 0 && current != null) |
| current = current.enclosingType(); |
| return current; |
| } |
| |
| public int enumConstantCount() { |
| int count = 0; |
| FieldBinding[] fields = fields(); |
| for (int i = 0, length = fields.length; i < length; i++) { |
| if ((fields[i].modifiers & ClassFileConstants.AccEnum) != 0) count++; |
| } |
| return count; |
| } |
| |
| public int fieldCount() { |
| return fields().length; |
| } |
| |
| public FieldBinding[] fields() { |
| return Binding.NO_FIELDS; |
| } |
| |
| public final int getAccessFlags() { |
| return this.modifiers & ExtraCompilerModifiers.AccJustFlag; |
| } |
| |
| /** |
| * @return the JSR 175 annotations for this type. |
| */ |
| public AnnotationBinding[] getAnnotations() { |
| return retrieveAnnotations(this); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.internal.compiler.lookup.Binding#getAnnotationTagBits() |
| */ |
| public long getAnnotationTagBits() { |
| return this.tagBits; |
| } |
| |
| /** |
| * @return the enclosingInstancesSlotSize |
| */ |
| public int getEnclosingInstancesSlotSize() { |
| if (isStatic()) return 0; |
| return enclosingType() == null ? 0 : 1; |
| } |
| |
| public MethodBinding getExactConstructor(TypeBinding[] argumentTypes) { |
| return null; |
| } |
| |
| public MethodBinding getExactMethod(char[] selector, TypeBinding[] argumentTypes, CompilationUnitScope refScope) { |
| return null; |
| } |
| public FieldBinding getField(char[] fieldName, boolean needResolve) { |
| return null; |
| } |
| /** |
| * @see org.eclipse.jdt.internal.compiler.env.IDependent#getFileName() |
| */ |
| public char[] getFileName() { |
| return this.fileName; |
| } |
| |
| public ReferenceBinding getMemberType(char[] typeName) { |
| ReferenceBinding[] memberTypes = memberTypes(); |
| for (int i = memberTypes.length; --i >= 0;) |
| if (CharOperation.equals(memberTypes[i].sourceName, typeName)) |
| return memberTypes[i]; |
| return null; |
| } |
| |
| public MethodBinding[] getMethods(char[] selector) { |
| return Binding.NO_METHODS; |
| } |
| |
| // Answer methods named selector, which take no more than the suggestedParameterLength. |
| // The suggested parameter length is optional and may not be guaranteed by every type. |
| public MethodBinding[] getMethods(char[] selector, int suggestedParameterLength) { |
| return getMethods(selector); |
| } |
| |
| /** |
| * @return the outerLocalVariablesSlotSize |
| */ |
| public int getOuterLocalVariablesSlotSize() { |
| return 0; |
| } |
| |
| public PackageBinding getPackage() { |
| return this.fPackage; |
| } |
| |
| public TypeVariableBinding getTypeVariable(char[] variableName) { |
| TypeVariableBinding[] typeVariables = typeVariables(); |
| for (int i = typeVariables.length; --i >= 0;) |
| if (CharOperation.equals(typeVariables[i].sourceName, variableName)) |
| return typeVariables[i]; |
| return null; |
| } |
| |
| public int hashCode() { |
| // ensure ReferenceBindings hash to the same position as UnresolvedReferenceBindings so they can be replaced without rehashing |
| // ALL ReferenceBindings are unique when created so equals() is the same as == |
| return (this.compoundName == null || this.compoundName.length == 0) |
| ? super.hashCode() |
| : CharOperation.hashCode(this.compoundName[this.compoundName.length - 1]); |
| } |
| |
| /** |
| * Returns true if the two types have an incompatible common supertype, |
| * e.g. List<String> and List<Integer> |
| */ |
| public boolean hasIncompatibleSuperType(ReferenceBinding otherType) { |
| |
| if (TypeBinding.equalsEquals(this, otherType)) return false; |
| |
| ReferenceBinding[] interfacesToVisit = null; |
| int nextPosition = 0; |
| ReferenceBinding currentType = this; |
| TypeBinding match; |
| do { |
| match = otherType.findSuperTypeOriginatingFrom(currentType); |
| if (match != null && match.isProvablyDistinct(currentType)) |
| return true; |
| |
| 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 (TypeBinding.equalsEquals(currentType, otherType)) return false; |
| match = otherType.findSuperTypeOriginatingFrom(currentType); |
| if (match != null && match.isProvablyDistinct(currentType)) |
| 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; |
| } |
| |
| public boolean hasMemberTypes() { |
| return false; |
| } |
| |
| /** |
| * Answer whether a @NonNullByDefault is applicable at the given method binding. |
| */ |
| boolean hasNonNullDefault() { |
| // Note, STB overrides for correctly handling local types |
| ReferenceBinding currentType = this; |
| while (currentType != null) { |
| if ((currentType.tagBits & TagBits.AnnotationNonNullByDefault) != 0) |
| return true; |
| if ((currentType.tagBits & TagBits.AnnotationNullUnspecifiedByDefault) != 0) |
| return false; |
| currentType = currentType.enclosingType(); |
| } |
| // package |
| return this.getPackage().defaultNullness == NONNULL_BY_DEFAULT; |
| } |
| |
| public final boolean hasRestrictedAccess() { |
| return (this.modifiers & ExtraCompilerModifiers.AccRestrictedAccess) != 0; |
| } |
| |
| /** Answer true if the receiver implements anInterface or is identical to anInterface. |
| * If searchHierarchy is true, then also search the receiver's superclasses. |
| * |
| * NOTE: Assume that anInterface is an interface. |
| */ |
| public boolean implementsInterface(ReferenceBinding anInterface, boolean searchHierarchy) { |
| if (TypeBinding.equalsEquals(this, anInterface)) |
| return true; |
| |
| ReferenceBinding[] interfacesToVisit = null; |
| int nextPosition = 0; |
| ReferenceBinding currentType = this; |
| do { |
| ReferenceBinding[] itsInterfaces = currentType.superInterfaces(); |
| if (itsInterfaces != null && itsInterfaces != Binding.NO_SUPERINTERFACES) { // in code assist cases when source types are added late, may not be finished connecting hierarchy |
| 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 (searchHierarchy && (currentType = currentType.superclass()) != null); |
| |
| for (int i = 0; i < nextPosition; i++) { |
| currentType = interfacesToVisit[i]; |
| if (currentType.isEquivalentTo(anInterface)) |
| return true; |
| |
| ReferenceBinding[] itsInterfaces = currentType.superInterfaces(); |
| if (itsInterfaces != null && itsInterfaces != Binding.NO_SUPERINTERFACES) { // in code assist cases when source types are added late, may not be finished connecting hierarchy |
| 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; |
| } |
| |
| // Internal method... assume its only sent to classes NOT interfaces |
| boolean implementsMethod(MethodBinding method) { |
| char[] selector = method.selector; |
| ReferenceBinding type = this; |
| while (type != null) { |
| MethodBinding[] methods = type.methods(); |
| long range; |
| if ((range = ReferenceBinding.binarySearch(selector, methods)) >= 0) { |
| int start = (int) range, end = (int) (range >> 32); |
| for (int i = start; i <= end; i++) { |
| if (methods[i].areParametersEqual(method)) |
| return true; |
| } |
| } |
| type = type.superclass(); |
| } |
| return false; |
| } |
| |
| /** |
| * Answer true if the receiver is an abstract type |
| */ |
| public final boolean isAbstract() { |
| return (this.modifiers & ClassFileConstants.AccAbstract) != 0; |
| } |
| |
| public boolean isAnnotationType() { |
| return (this.modifiers & ClassFileConstants.AccAnnotation) != 0; |
| } |
| |
| public final boolean isBinaryBinding() { |
| return (this.tagBits & TagBits.IsBinaryBinding) != 0; |
| } |
| |
| public boolean isClass() { |
| return (this.modifiers & (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation | ClassFileConstants.AccEnum)) == 0; |
| } |
| |
| public boolean isProperType(boolean admitCapture18) { |
| ReferenceBinding outer = enclosingType(); |
| if (outer != null && !outer.isProperType(admitCapture18)) |
| return false; |
| return super.isProperType(admitCapture18); |
| } |
| |
| /** |
| * Answer true if the receiver type can be assigned to the argument type (right) |
| * In addition to improving performance, caching also ensures there is no infinite regression |
| * since per nature, the compatibility check is recursive through parameterized type arguments (122775) |
| */ |
| public boolean isCompatibleWith(TypeBinding otherType, /*@Nullable*/ Scope captureScope) { |
| if (equalsEquals(otherType, this)) |
| return true; |
| |
| if (otherType.id == TypeIds.T_JavaLangObject) |
| return true; |
| Object result; |
| if (this.compatibleCache == null) { |
| this.compatibleCache = new SimpleLookupTable(3); |
| result = null; |
| } else { |
| result = this.compatibleCache.get(otherType); // [dbg reset] this.compatibleCache.put(otherType,null) |
| if (result != null) { |
| return result == Boolean.TRUE; |
| } |
| } |
| this.compatibleCache.put(otherType, Boolean.FALSE); // protect from recursive call |
| if (isCompatibleWith0(otherType, captureScope)) { |
| this.compatibleCache.put(otherType, Boolean.TRUE); |
| return true; |
| } |
| if (captureScope == null |
| && this instanceof TypeVariableBinding |
| && ((TypeVariableBinding)this).firstBound instanceof ParameterizedTypeBinding) { |
| // see https://bugs.eclipse.org/395002#c9 |
| // in this case a subsequent check with captureScope != null may actually get |
| // a better result, reset this info to ensure we're not blocking that re-check. |
| this.compatibleCache.put(otherType, null); |
| } |
| return false; |
| } |
| |
| /** |
| * Answer true if the receiver type can be assigned to the argument type (right) |
| */ |
| private boolean isCompatibleWith0(TypeBinding otherType, /*@Nullable*/ Scope captureScope) { |
| if (TypeBinding.equalsEquals(otherType, this)) |
| return true; |
| if (otherType.id == TypeIds.T_JavaLangObject) |
| return true; |
| // equivalence may allow compatibility with array type through wildcard |
| // bound |
| if (isEquivalentTo(otherType)) |
| return true; |
| switch (otherType.kind()) { |
| case Binding.WILDCARD_TYPE : |
| case Binding.INTERSECTION_TYPE: |
| return false; // should have passed equivalence check above if |
| // wildcard |
| case Binding.TYPE_PARAMETER : |
| // check compatibility with capture of ? super X |
| if (otherType.isCapture()) { |
| CaptureBinding otherCapture = (CaptureBinding) otherType; |
| TypeBinding otherLowerBound; |
| if ((otherLowerBound = otherCapture.lowerBound) != null) { |
| if (otherLowerBound.isArrayType()) return false; |
| return isCompatibleWith(otherLowerBound); |
| } |
| } |
| //$FALL-THROUGH$ |
| case Binding.GENERIC_TYPE : |
| case Binding.TYPE : |
| case Binding.PARAMETERIZED_TYPE : |
| case Binding.RAW_TYPE : |
| switch (kind()) { |
| case Binding.GENERIC_TYPE : |
| case Binding.PARAMETERIZED_TYPE : |
| case Binding.RAW_TYPE : |
| if (TypeBinding.equalsEquals(erasure(), otherType.erasure())) |
| return false; // should have passed equivalence check |
| // above if same erasure |
| } |
| ReferenceBinding otherReferenceType = (ReferenceBinding) otherType; |
| if (otherReferenceType.isInterface()) { // could be annotation type |
| if (implementsInterface(otherReferenceType, true)) |
| return true; |
| if (this instanceof TypeVariableBinding && captureScope != null) { |
| TypeVariableBinding typeVariable = (TypeVariableBinding) this; |
| if (typeVariable.firstBound instanceof ParameterizedTypeBinding) { |
| TypeBinding bound = typeVariable.firstBound.capture(captureScope, -1); // no position needed as this capture will never escape this context |
| return bound.isCompatibleWith(otherReferenceType); |
| } |
| } |
| } |
| if (isInterface()) // Explicit conversion from an interface |
| // to a class is not allowed |
| return false; |
| return otherReferenceType.isSuperclassOf(this); |
| default : |
| return false; |
| } |
| } |
| |
| public boolean isSubtypeOf(TypeBinding other) { |
| if (isSubTypeOfRTL(other)) |
| return true; |
| // TODO: if this has wildcards, perform capture before the next call: |
| TypeBinding candidate = findSuperTypeOriginatingFrom(other); |
| if (candidate == null) |
| return false; |
| if (TypeBinding.equalsEquals(candidate, other)) |
| return true; |
| |
| // T<Ai...> <: T#RAW: |
| if (other.isRawType() && TypeBinding.equalsEquals(candidate.erasure(), other.erasure())) |
| return true; |
| |
| TypeBinding[] sis = other.typeArguments(); |
| TypeBinding[] tis = candidate.typeArguments(); |
| if (tis == null || sis == null) |
| return false; |
| if (sis.length != tis.length) |
| return false; |
| for (int i = 0; i < sis.length; i++) { |
| if (!tis[i].isTypeArgumentContainedBy(sis[i])) |
| return false; |
| } |
| return true; |
| } |
| |
| protected boolean isSubTypeOfRTL(TypeBinding other) { |
| if (TypeBinding.equalsEquals(this, other)) |
| return true; |
| if (other instanceof CaptureBinding) { |
| // for this one kind we must first unwrap the rhs: |
| TypeBinding lower = ((CaptureBinding) other).lowerBound; |
| return (lower != null && isSubtypeOf(lower)); |
| } |
| if (other instanceof ReferenceBinding) { |
| TypeBinding[] intersecting = ((ReferenceBinding) other).getIntersectingTypes(); |
| if (intersecting != null) { |
| for (int i = 0; i < intersecting.length; i++) { |
| if (!isSubtypeOf(intersecting[i])) |
| return false; |
| } |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Answer true if the receiver has default visibility |
| */ |
| public final boolean isDefault() { |
| return (this.modifiers & (ClassFileConstants.AccPublic | ClassFileConstants.AccProtected | ClassFileConstants.AccPrivate)) == 0; |
| } |
| |
| /** |
| * Answer true if the receiver is a deprecated type |
| */ |
| public final boolean isDeprecated() { |
| return (this.modifiers & ClassFileConstants.AccDeprecated) != 0; |
| } |
| |
| public boolean isEnum() { |
| return (this.modifiers & ClassFileConstants.AccEnum) != 0; |
| } |
| |
| /** |
| * Answer true if the receiver is final and cannot be subclassed |
| */ |
| public final boolean isFinal() { |
| return (this.modifiers & ClassFileConstants.AccFinal) != 0; |
| } |
| |
| /** |
| * Returns true if the type hierarchy is being connected |
| */ |
| public boolean isHierarchyBeingConnected() { |
| return (this.tagBits & TagBits.EndHierarchyCheck) == 0 && (this.tagBits & TagBits.BeginHierarchyCheck) != 0; |
| } |
| /** |
| * Returns true if the type hierarchy is being connected "actively" i.e not paused momentatrily, |
| * while resolving type arguments. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=294057 |
| */ |
| public boolean isHierarchyBeingActivelyConnected() { |
| return (this.tagBits & TagBits.EndHierarchyCheck) == 0 && (this.tagBits & TagBits.BeginHierarchyCheck) != 0 && (this.tagBits & TagBits.PauseHierarchyCheck) == 0; |
| } |
| |
| /** |
| * Returns true if the type hierarchy is connected |
| */ |
| public boolean isHierarchyConnected() { |
| return true; |
| } |
| |
| public boolean isInterface() { |
| // consider strict interfaces and annotation types |
| return (this.modifiers & ClassFileConstants.AccInterface) != 0; |
| } |
| |
| public boolean isFunctionalInterface(Scope scope) { |
| MethodBinding method; |
| return isInterface() && (method = getSingleAbstractMethod(scope, true)) != null && method.isValidBinding(); |
| } |
| |
| /** |
| * Answer true if the receiver has private visibility |
| */ |
| public final boolean isPrivate() { |
| return (this.modifiers & ClassFileConstants.AccPrivate) != 0; |
| } |
| |
| /** |
| * Answer true if the receiver or any of its enclosing types have private visibility |
| */ |
| public final boolean isOrEnclosedByPrivateType() { |
| if (isLocalType()) return true; // catch all local types |
| ReferenceBinding type = this; |
| while (type != null) { |
| if ((type.modifiers & ClassFileConstants.AccPrivate) != 0) |
| return true; |
| type = type.enclosingType(); |
| } |
| return false; |
| } |
| |
| /** |
| * Answer true if the receiver has protected visibility |
| */ |
| public final boolean isProtected() { |
| return (this.modifiers & ClassFileConstants.AccProtected) != 0; |
| } |
| |
| /** |
| * Answer true if the receiver has public visibility |
| */ |
| public final boolean isPublic() { |
| return (this.modifiers & ClassFileConstants.AccPublic) != 0; |
| } |
| |
| /** |
| * Answer true if the receiver is a static member type (or toplevel) |
| */ |
| public final boolean isStatic() { |
| return (this.modifiers & (ClassFileConstants.AccStatic | ClassFileConstants.AccInterface)) != 0 || (this.tagBits & TagBits.IsNestedType) == 0; |
| } |
| |
| /** |
| * Answer true if all float operations must adher to IEEE 754 float/double rules |
| */ |
| public final boolean isStrictfp() { |
| return (this.modifiers & ClassFileConstants.AccStrictfp) != 0; |
| } |
| |
| /** |
| * Answer true if the receiver is in the superclass hierarchy of aType |
| * NOTE: Object.isSuperclassOf(Object) -> false |
| */ |
| public boolean isSuperclassOf(ReferenceBinding otherType) { |
| while ((otherType = otherType.superclass()) != null) { |
| if (otherType.isEquivalentTo(this)) return true; |
| } |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.internal.compiler.lookup.TypeBinding#isThrowable() |
| */ |
| public boolean isThrowable() { |
| ReferenceBinding current = this; |
| do { |
| switch (current.id) { |
| case TypeIds.T_JavaLangThrowable : |
| case TypeIds.T_JavaLangError : |
| case TypeIds.T_JavaLangRuntimeException : |
| case TypeIds.T_JavaLangException : |
| return true; |
| } |
| } while ((current = current.superclass()) != null); |
| return false; |
| } |
| |
| /** |
| * JLS 11.5 ensures that Throwable, Exception, RuntimeException and Error are directly connected. |
| * (Throwable<- Exception <- RumtimeException, Throwable <- Error). Thus no need to check #isCompatibleWith |
| * but rather check in type IDs so as to avoid some eager class loading for JCL writers. |
| * When 'includeSupertype' is true, answers true if the given type can be a supertype of some unchecked exception |
| * type (i.e. Throwable or Exception). |
| * @see org.eclipse.jdt.internal.compiler.lookup.TypeBinding#isUncheckedException(boolean) |
| */ |
| public boolean isUncheckedException(boolean includeSupertype) { |
| switch (this.id) { |
| case TypeIds.T_JavaLangError : |
| case TypeIds.T_JavaLangRuntimeException : |
| return true; |
| case TypeIds.T_JavaLangThrowable : |
| case TypeIds.T_JavaLangException : |
| return includeSupertype; |
| } |
| ReferenceBinding current = this; |
| while ((current = current.superclass()) != null) { |
| switch (current.id) { |
| case TypeIds.T_JavaLangError : |
| case TypeIds.T_JavaLangRuntimeException : |
| return true; |
| case TypeIds.T_JavaLangThrowable : |
| case TypeIds.T_JavaLangException : |
| return false; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Answer true if the receiver has private visibility and is used locally |
| */ |
| public final boolean isUsed() { |
| return (this.modifiers & ExtraCompilerModifiers.AccLocallyUsed) != 0; |
| } |
| |
| /** |
| * Answer true if the receiver is deprecated (or any of its enclosing types) |
| */ |
| public final boolean isViewedAsDeprecated() { |
| return (this.modifiers & (ClassFileConstants.AccDeprecated | ExtraCompilerModifiers.AccDeprecatedImplicitly)) != 0 |
| || getPackage().isViewedAsDeprecated(); |
| } |
| |
| public ReferenceBinding[] memberTypes() { |
| return Binding.NO_MEMBER_TYPES; |
| } |
| |
| public MethodBinding[] methods() { |
| return Binding.NO_METHODS; |
| } |
| |
| public final ReferenceBinding outermostEnclosingType() { |
| ReferenceBinding current = this; |
| while (true) { |
| ReferenceBinding last = current; |
| if ((current = current.enclosingType()) == null) |
| return last; |
| } |
| } |
| |
| /** |
| * Answer the source name for the type. |
| * In the case of member types, as the qualified name from its top level type. |
| * For example, for a member type N defined inside M & A: "A.M.N". |
| */ |
| public char[] qualifiedSourceName() { |
| if (isMemberType()) |
| return CharOperation.concat(enclosingType().qualifiedSourceName(), sourceName(), '.'); |
| return sourceName(); |
| } |
| |
| /** |
| * Answer the receiver's signature. |
| * |
| * NOTE: This method should only be used during/after code gen. |
| */ |
| public char[] readableName() /*java.lang.Object, p.X<T> */ { |
| char[] readableName; |
| if (isMemberType()) { |
| readableName = CharOperation.concat(enclosingType().readableName(), this.sourceName, '.'); |
| } else { |
| readableName = CharOperation.concatWith(this.compoundName, '.'); |
| } |
| TypeVariableBinding[] typeVars; |
| if ((typeVars = typeVariables()) != Binding.NO_TYPE_VARIABLES) { |
| StringBuffer nameBuffer = new StringBuffer(10); |
| nameBuffer.append(readableName).append('<'); |
| for (int i = 0, length = typeVars.length; i < length; i++) { |
| if (i > 0) nameBuffer.append(','); |
| nameBuffer.append(typeVars[i].readableName()); |
| } |
| nameBuffer.append('>'); |
| int nameLength = nameBuffer.length(); |
| readableName = new char[nameLength]; |
| nameBuffer.getChars(0, nameLength, readableName, 0); |
| } |
| return readableName; |
| } |
| |
| protected void appendNullAnnotation(StringBuffer nameBuffer, CompilerOptions options) { |
| if (options.isAnnotationBasedNullAnalysisEnabled) { |
| // restore applied null annotation from tagBits: |
| if ((this.tagBits & TagBits.AnnotationNonNull) != 0) { |
| char[][] nonNullAnnotationName = options.nonNullAnnotationName; |
| nameBuffer.append('@').append(nonNullAnnotationName[nonNullAnnotationName.length-1]).append(' '); |
| } else if ((this.tagBits & TagBits.AnnotationNullable) != 0) { |
| char[][] nullableAnnotationName = options.nullableAnnotationName; |
| nameBuffer.append('@').append(nullableAnnotationName[nullableAnnotationName.length-1]).append(' '); |
| } |
| } |
| } |
| |
| public AnnotationHolder retrieveAnnotationHolder(Binding binding, boolean forceInitialization) { |
| SimpleLookupTable store = storedAnnotations(forceInitialization); |
| return store == null ? null : (AnnotationHolder) store.get(binding); |
| } |
| |
| AnnotationBinding[] retrieveAnnotations(Binding binding) { |
| AnnotationHolder holder = retrieveAnnotationHolder(binding, true); |
| return holder == null ? Binding.NO_ANNOTATIONS : holder.getAnnotations(); |
| } |
| |
| public void setAnnotations(AnnotationBinding[] annotations) { |
| storeAnnotations(this, annotations); |
| } |
| public void setContainerAnnotationType(ReferenceBinding value) { |
| // Leave this to subclasses |
| } |
| public void tagAsHavingDefectiveContainerType() { |
| // Leave this to subclasses |
| } |
| |
| /** |
| * @see org.eclipse.jdt.internal.compiler.lookup.TypeBinding#nullAnnotatedReadableName(CompilerOptions,boolean) |
| */ |
| public char[] nullAnnotatedReadableName(CompilerOptions options, boolean shortNames) { |
| if (shortNames) |
| return nullAnnotatedShortReadableName(options); |
| return nullAnnotatedReadableName(options); |
| } |
| |
| char[] nullAnnotatedReadableName(CompilerOptions options) { |
| StringBuffer nameBuffer = new StringBuffer(10); |
| if (isMemberType()) { |
| nameBuffer.append(enclosingType().nullAnnotatedReadableName(options, false)); |
| nameBuffer.append('.'); |
| appendNullAnnotation(nameBuffer, options); |
| nameBuffer.append(this.sourceName); |
| } else if (this.compoundName != null) { |
| int i; |
| int l=this.compoundName.length; |
| for (i=0; i<l-1; i++) { |
| nameBuffer.append(this.compoundName[i]); |
| nameBuffer.append('.'); |
| } |
| appendNullAnnotation(nameBuffer, options); |
| nameBuffer.append(this.compoundName[i]); |
| } else { |
| // case of TypeVariableBinding with nullAnnotationTagBits: |
| appendNullAnnotation(nameBuffer, options); |
| if (this.sourceName != null) |
| nameBuffer.append(this.sourceName); |
| else // WildcardBinding, CaptureBinding have no sourceName |
| nameBuffer.append(this.readableName()); |
| } |
| TypeBinding [] arguments = typeArguments(); |
| if (arguments != null && arguments.length > 0) { // empty arguments array happens when PTB has been created just to capture type annotations |
| nameBuffer.append('<'); |
| for (int i = 0, length = arguments.length; i < length; i++) { |
| if (i > 0) nameBuffer.append(','); |
| nameBuffer.append(arguments[i].nullAnnotatedReadableName(options, false)); |
| } |
| nameBuffer.append('>'); |
| } |
| int nameLength = nameBuffer.length(); |
| char[] readableName = new char[nameLength]; |
| nameBuffer.getChars(0, nameLength, readableName, 0); |
| return readableName; |
| } |
| |
| char[] nullAnnotatedShortReadableName(CompilerOptions options) { |
| StringBuffer nameBuffer = new StringBuffer(10); |
| if (isMemberType()) { |
| nameBuffer.append(enclosingType().nullAnnotatedReadableName(options, true)); |
| nameBuffer.append('.'); |
| appendNullAnnotation(nameBuffer, options); |
| nameBuffer.append(this.sourceName); |
| } else { |
| appendNullAnnotation(nameBuffer, options); |
| if (this.sourceName != null) |
| nameBuffer.append(this.sourceName); |
| else // WildcardBinding, CaptureBinding have no sourceName |
| nameBuffer.append(this.shortReadableName()); |
| } |
| TypeBinding [] arguments = typeArguments(); |
| if (arguments != null && arguments.length > 0) { // empty arguments array happens when PTB has been created just to capture type annotations |
| nameBuffer.append('<'); |
| for (int i = 0, length = arguments.length; i < length; i++) { |
| if (i > 0) nameBuffer.append(','); |
| nameBuffer.append(arguments[i].nullAnnotatedReadableName(options, true)); |
| } |
| nameBuffer.append('>'); |
| } |
| int nameLength = nameBuffer.length(); |
| char[] shortReadableName = new char[nameLength]; |
| nameBuffer.getChars(0, nameLength, shortReadableName, 0); |
| return shortReadableName; |
| } |
| |
| public char[] shortReadableName() /*Object*/ { |
| char[] shortReadableName; |
| if (isMemberType()) { |
| shortReadableName = CharOperation.concat(enclosingType().shortReadableName(), this.sourceName, '.'); |
| } else { |
| shortReadableName = this.sourceName; |
| } |
| TypeVariableBinding[] typeVars; |
| if ((typeVars = typeVariables()) != Binding.NO_TYPE_VARIABLES) { |
| StringBuffer nameBuffer = new StringBuffer(10); |
| nameBuffer.append(shortReadableName).append('<'); |
| for (int i = 0, length = typeVars.length; i < length; i++) { |
| if (i > 0) nameBuffer.append(','); |
| nameBuffer.append(typeVars[i].shortReadableName()); |
| } |
| nameBuffer.append('>'); |
| int nameLength = nameBuffer.length(); |
| shortReadableName = new char[nameLength]; |
| nameBuffer.getChars(0, nameLength, shortReadableName, 0); |
| } |
| return shortReadableName; |
| } |
| |
| public char[] signature() /* Ljava/lang/Object; */ { |
| if (this.signature != null) |
| return this.signature; |
| |
| return this.signature = CharOperation.concat('L', constantPoolName(), ';'); |
| } |
| |
| public char[] sourceName() { |
| return this.sourceName; |
| } |
| |
| void storeAnnotationHolder(Binding binding, AnnotationHolder holder) { |
| if (holder == null) { |
| SimpleLookupTable store = storedAnnotations(false); |
| if (store != null) |
| store.removeKey(binding); |
| } else { |
| SimpleLookupTable store = storedAnnotations(true); |
| if (store != null) |
| store.put(binding, holder); |
| } |
| } |
| |
| void storeAnnotations(Binding binding, AnnotationBinding[] annotations) { |
| AnnotationHolder holder = null; |
| if (annotations == null || annotations.length == 0) { |
| SimpleLookupTable store = storedAnnotations(false); |
| if (store != null) |
| holder = (AnnotationHolder) store.get(binding); |
| if (holder == null) return; // nothing to delete |
| } else { |
| SimpleLookupTable store = storedAnnotations(true); |
| if (store == null) return; // not supported |
| holder = (AnnotationHolder) store.get(binding); |
| if (holder == null) |
| holder = new AnnotationHolder(); |
| } |
| storeAnnotationHolder(binding, holder.setAnnotations(annotations)); |
| } |
| |
| SimpleLookupTable storedAnnotations(boolean forceInitialize) { |
| return null; // overrride if interested in storing annotations for the receiver, its fields and methods |
| } |
| |
| public ReferenceBinding superclass() { |
| return null; |
| } |
| |
| public ReferenceBinding[] superInterfaces() { |
| return Binding.NO_SUPERINTERFACES; |
| } |
| |
| public ReferenceBinding[] syntheticEnclosingInstanceTypes() { |
| if (isStatic()) return null; |
| ReferenceBinding enclosingType = enclosingType(); |
| if (enclosingType == null) |
| return null; |
| return new ReferenceBinding[] {enclosingType}; |
| } |
| public SyntheticArgumentBinding[] syntheticOuterLocalVariables() { |
| return null; // is null if no enclosing instances are required |
| } |
| |
| MethodBinding[] unResolvedMethods() { // for the MethodVerifier so it doesn't resolve types |
| return methods(); |
| } |
| |
| public FieldBinding[] unResolvedFields() { |
| return Binding.NO_FIELDS; |
| } |
| |
| /* |
| * If a type - known to be a Closeable - is mentioned in one of our white lists |
| * answer the typeBit for the white list (BitWrapperCloseable or BitResourceFreeCloseable). |
| */ |
| protected int applyCloseableWhitelists() { |
| switch (this.compoundName.length) { |
| case 3: |
| if (CharOperation.equals(TypeConstants.JAVA, this.compoundName[0])) { |
| if (CharOperation.equals(TypeConstants.IO, this.compoundName[1])) { |
| char[] simpleName = this.compoundName[2]; |
| int l = TypeConstants.JAVA_IO_WRAPPER_CLOSEABLES.length; |
| for (int i = 0; i < l; i++) { |
| if (CharOperation.equals(simpleName, TypeConstants.JAVA_IO_WRAPPER_CLOSEABLES[i])) |
| return TypeIds.BitWrapperCloseable; |
| } |
| l = TypeConstants.JAVA_IO_RESOURCE_FREE_CLOSEABLES.length; |
| for (int i = 0; i < l; i++) { |
| if (CharOperation.equals(simpleName, TypeConstants.JAVA_IO_RESOURCE_FREE_CLOSEABLES[i])) |
| return TypeIds.BitResourceFreeCloseable; |
| } |
| } |
| } |
| break; |
| case 4: |
| if (CharOperation.equals(TypeConstants.JAVA, this.compoundName[0])) { |
| if (CharOperation.equals(TypeConstants.UTIL, this.compoundName[1])) { |
| if (CharOperation.equals(TypeConstants.ZIP, this.compoundName[2])) { |
| char[] simpleName = this.compoundName[3]; |
| int l = TypeConstants.JAVA_UTIL_ZIP_WRAPPER_CLOSEABLES.length; |
| for (int i = 0; i < l; i++) { |
| if (CharOperation.equals(simpleName, TypeConstants.JAVA_UTIL_ZIP_WRAPPER_CLOSEABLES[i])) |
| return TypeIds.BitWrapperCloseable; |
| } |
| } |
| } |
| } |
| break; |
| } |
| int l = TypeConstants.OTHER_WRAPPER_CLOSEABLES.length; |
| for (int i = 0; i < l; i++) { |
| if (CharOperation.equals(this.compoundName, TypeConstants.OTHER_WRAPPER_CLOSEABLES[i])) |
| return TypeIds.BitWrapperCloseable; |
| } |
| return 0; |
| } |
| |
| |
| private MethodBinding [] getInterfaceAbstractContracts(Scope scope) throws InvalidInputException { |
| |
| if (!isInterface() || !isValidBinding()) { |
| throw new InvalidInputException("Not a functional interface"); //$NON-NLS-1$ |
| } |
| |
| MethodBinding [] methods = methods(); |
| MethodBinding [] contracts = new MethodBinding[0]; |
| int contractsCount = 0; |
| int contractsLength = 0; |
| |
| // -- the following are used for early termination. |
| MethodBinding aContract = null; |
| int contractParameterLength = 0; |
| char [] contractSelector = null; |
| // --- |
| |
| ReferenceBinding [] superInterfaces = superInterfaces(); |
| for (int i = 0, length = superInterfaces.length; i < length; i++) { |
| MethodBinding [] superInterfaceContracts = superInterfaces[i].getInterfaceAbstractContracts(scope); |
| final int superInterfaceContractsLength = superInterfaceContracts == null ? 0 : superInterfaceContracts.length; |
| |
| if (superInterfaceContractsLength == 0) continue; |
| if (aContract == null) { |
| aContract = superInterfaceContracts[0]; |
| contractParameterLength = aContract.parameters.length; |
| contractSelector = aContract.selector; |
| contracts = superInterfaceContracts; |
| contractsCount = contractsLength = superInterfaceContractsLength; |
| } else { |
| if (superInterfaceContracts[0].parameters.length != contractParameterLength || !CharOperation.equals(contractSelector, superInterfaceContracts[0].selector)) { |
| throw new InvalidInputException("Not a functional interface"); //$NON-NLS-1$ |
| } |
| if (contractsLength < contractsCount + superInterfaceContractsLength) { |
| System.arraycopy(contracts, 0, contracts = new MethodBinding[contractsLength = contractsCount + superInterfaceContractsLength], 0, contractsCount); |
| } |
| System.arraycopy(superInterfaceContracts, 0, contracts, contractsCount, superInterfaceContractsLength); |
| contractsCount += superInterfaceContractsLength; |
| } |
| } |
| for (int i = 0, length = methods == null ? 0 : methods.length; i < length; i++) { |
| final MethodBinding method = methods[i]; |
| if (method.isStatic() || method.redeclaresPublicObjectMethod(scope)) continue; |
| if (method.isDefaultMethod()) { |
| for (int j = 0; j < contractsCount; j++) { |
| if (contracts[j] == null) |
| continue; |
| if (MethodVerifier.doesMethodOverride(method, contracts[j], scope.environment())) { |
| if (aContract == contracts[j]) { |
| aContract = null; |
| contractParameterLength = 0; |
| contractSelector = null; |
| } |
| contractsCount--; |
| // abstract method from super type rendered default by present interface ==> contracts[j] = null; |
| if (j < contractsCount) |
| System.arraycopy(contracts, j+1, contracts, j, contractsCount - j); |
| } |
| } |
| continue; // skip default method itself |
| } |
| final boolean validBinding = method.isValidBinding(); |
| if (aContract == null && validBinding) { |
| aContract = method; |
| contractParameterLength = aContract.parameters.length; |
| contractSelector = aContract.selector; |
| } else { |
| if (!validBinding || method.parameters.length != contractParameterLength || !CharOperation.equals(contractSelector, method.selector)) { |
| throw new InvalidInputException("Not a functional interface"); //$NON-NLS-1$ |
| } |
| } |
| if (contractsCount == contractsLength) { |
| System.arraycopy(contracts, 0, contracts = new MethodBinding[contractsLength += 16], 0, contractsCount); |
| } |
| contracts[contractsCount++] = method; |
| } |
| if (contractsCount < contractsLength) { |
| System.arraycopy(contracts, 0, contracts = new MethodBinding[contractsCount], 0, contractsCount); |
| } |
| return contracts; |
| } |
| public MethodBinding getSingleAbstractMethod(Scope scope, boolean replaceWildcards) { |
| |
| if (this.singleAbstractMethod != null) { |
| return this.singleAbstractMethod; |
| } |
| |
| if (this.compoundName != null) |
| scope.compilationUnitScope().recordQualifiedReference(this.compoundName); |
| MethodBinding[] methods = null; |
| try { |
| methods = getInterfaceAbstractContracts(scope); |
| } catch (InvalidInputException e) { |
| return this.singleAbstractMethod = samProblemBinding; |
| } |
| if (methods != null && methods.length == 1) |
| return this.singleAbstractMethod = methods[0]; |
| |
| final LookupEnvironment environment = scope.environment(); |
| boolean genericMethodSeen = false; |
| next:for (int i = 0, length = methods.length; i < length; i++) { |
| MethodBinding method = methods[i], otherMethod = null; |
| if (method.typeVariables != Binding.NO_TYPE_VARIABLES) |
| genericMethodSeen = true; |
| for (int j = 0; j < length; j++) { |
| if (i == j) continue; |
| otherMethod = methods[j]; |
| if (otherMethod.typeVariables != Binding.NO_TYPE_VARIABLES) |
| genericMethodSeen = true; |
| |
| if (genericMethodSeen) { // adapt type parameters. |
| otherMethod = MethodVerifier.computeSubstituteMethod(otherMethod, method, environment); |
| if (otherMethod == null) |
| continue next; |
| } |
| if (!MethodVerifier.isSubstituteParameterSubsignature(method, otherMethod, environment) || !MethodVerifier.areReturnTypesCompatible(method, otherMethod, environment)) |
| continue next; |
| } |
| // If we reach here, we found a method that is override equivalent with every other method and is also return type substitutable. Compute kosher exceptions now ... |
| ReferenceBinding [] exceptions = new ReferenceBinding[0]; |
| int exceptionsCount = 0, exceptionsLength = 0; |
| final MethodBinding theAbstractMethod = method; |
| boolean shouldEraseThrows = theAbstractMethod.typeVariables == Binding.NO_TYPE_VARIABLES && genericMethodSeen; |
| boolean shouldAdaptThrows = theAbstractMethod.typeVariables != Binding.NO_TYPE_VARIABLES; |
| final int typeVariableLength = theAbstractMethod.typeVariables.length; |
| |
| none:for (i = 0; i < length; i++) { |
| method = methods[i]; |
| ReferenceBinding[] methodThrownExceptions = method.thrownExceptions; |
| int methodExceptionsLength = methodThrownExceptions == null ? 0: methodThrownExceptions.length; |
| if (methodExceptionsLength == 0) break none; |
| if (shouldAdaptThrows && method != theAbstractMethod) { |
| System.arraycopy(methodThrownExceptions, 0, methodThrownExceptions = new ReferenceBinding[methodExceptionsLength], 0, methodExceptionsLength); |
| for (int tv = 0; tv < typeVariableLength; tv++) { |
| if (methodThrownExceptions[tv] instanceof TypeVariableBinding) { |
| methodThrownExceptions[tv] = theAbstractMethod.typeVariables[tv]; |
| } |
| } |
| } |
| nextException: for (int j = 0; j < methodExceptionsLength; j++) { |
| ReferenceBinding methodException = methodThrownExceptions[j]; |
| if (shouldEraseThrows) |
| methodException = (ReferenceBinding) methodException.erasure(); |
| nextMethod: for (int k = 0; k < length; k++) { |
| if (i == k) continue; |
| otherMethod = methods[k]; |
| ReferenceBinding[] otherMethodThrownExceptions = otherMethod.thrownExceptions; |
| int otherMethodExceptionsLength = otherMethodThrownExceptions == null ? 0 : otherMethodThrownExceptions.length; |
| if (otherMethodExceptionsLength == 0) break none; |
| if (shouldAdaptThrows && otherMethod != theAbstractMethod) { |
| System.arraycopy(otherMethodThrownExceptions, |
| 0, |
| otherMethodThrownExceptions = new ReferenceBinding[otherMethodExceptionsLength], |
| 0, |
| otherMethodExceptionsLength); |
| for (int tv = 0; tv < typeVariableLength; tv++) { |
| if (otherMethodThrownExceptions[tv] instanceof TypeVariableBinding) { |
| otherMethodThrownExceptions[tv] = theAbstractMethod.typeVariables[tv]; |
| } |
| } |
| } |
| for (int l = 0; l < otherMethodExceptionsLength; l++) { |
| ReferenceBinding otherException = otherMethodThrownExceptions[l]; |
| if (shouldEraseThrows) |
| otherException = (ReferenceBinding) otherException.erasure(); |
| if (methodException.isCompatibleWith(otherException)) |
| continue nextMethod; |
| } |
| continue nextException; |
| } |
| // If we reach here, method exception or its super type is covered by every throws clause. |
| if (exceptionsCount == exceptionsLength) { |
| System.arraycopy(exceptions, 0, exceptions = new ReferenceBinding[exceptionsLength += 16], 0, exceptionsCount); |
| } |
| exceptions[exceptionsCount++] = methodException; |
| } |
| } |
| if (exceptionsCount != exceptionsLength) { |
| System.arraycopy(exceptions, 0, exceptions = new ReferenceBinding[exceptionsCount], 0, exceptionsCount); |
| } |
| this.singleAbstractMethod = new MethodBinding(theAbstractMethod.modifiers, |
| theAbstractMethod.selector, |
| theAbstractMethod.returnType, |
| theAbstractMethod.parameters, |
| exceptions, |
| theAbstractMethod.declaringClass); |
| this.singleAbstractMethod.typeVariables = theAbstractMethod.typeVariables; |
| return this.singleAbstractMethod; |
| } |
| return this.singleAbstractMethod = samProblemBinding; |
| } |
| } |