void computeMethods() { MethodBinding[] methods = type.methods(); int size = methods.length; this.currentMethods = new HashtableOfObject( size == 0 ? 1 : size); // maps method selectors to an array of methods... must search to match // paramaters & return type for (int m = size; --m >= 0; ) { MethodBinding method = methods[m]; if (!(method.isConstructor() || method .isDefaultAbstract())) { // keep all methods which are NOT constructors or default // abstract MethodBinding[] existingMethods = (MethodBinding[]) this.currentMethods.get(method.selector); if (existingMethods == null) existingMethods = new MethodBinding[1]; else System.arraycopy( existingMethods, 0, (existingMethods = new MethodBinding[existingMethods.length + 1]), 0, existingMethods.length - 1); existingMethods[existingMethods.length - 1] = method; this.currentMethods.put(method.selector, existingMethods); } } }
void checkPackagePrivateAbstractMethod(MethodBinding abstractMethod) { // check that the inherited abstract method (package private visibility) is implemented within // the same package PackageBinding necessaryPackage = abstractMethod.declaringClass.fPackage; if (necessaryPackage == this.type.fPackage) return; // not a problem ReferenceBinding superType = this.type.superclass(); char[] selector = abstractMethod.selector; do { if (!superType.isValidBinding()) return; if (!superType.isAbstract()) return; // closer non abstract super type will be flagged instead if (necessaryPackage == superType.fPackage) { MethodBinding[] methods = superType.getMethods(selector); nextMethod: for (int m = methods.length; --m >= 0; ) { MethodBinding method = methods[m]; if (method.isPrivate() || method.isConstructor() || method.isDefaultAbstract()) continue nextMethod; if (doesMethodOverride(method, abstractMethod)) return; // found concrete implementation of abstract method in same package } } } while ((superType = superType.superclass()) != abstractMethod.declaringClass); // non visible abstract methods cannot be overridden so the type must be defined abstract this.problemReporter().abstractMethodCannotBeOverridden(this.type, abstractMethod); }
public boolean isSuper(String currentClassName) { if (!isSpecial) return false; if (methodBinding.isConstructor()) { if (!(getExpression() instanceof ThisExpression)) return false; } String name = methodBinding.getDeclaringClass().getClassName(); if (currentClassName.equals(name)) return false; return true; }
private Object reduceSubType(Scope scope, TypeBinding subCandidate, TypeBinding superCandidate) { // 18.2.3 Subtyping Constraints if (subCandidate.isProperType(true) && superCandidate.isProperType(true)) { if (subCandidate.isCompatibleWith(superCandidate, scope)) return TRUE; return FALSE; } if (subCandidate.id == TypeIds.T_null) return TRUE; if (superCandidate.id == TypeIds.T_null) return FALSE; if (subCandidate instanceof InferenceVariable) return new TypeBound((InferenceVariable) subCandidate, superCandidate, SUBTYPE, this.isSoft); if (superCandidate instanceof InferenceVariable) return new TypeBound( (InferenceVariable) superCandidate, subCandidate, SUPERTYPE, this.isSoft); // normalize to have variable on LHS switch (superCandidate.kind()) { case Binding.GENERIC_TYPE: case Binding.TYPE: case Binding.RAW_TYPE: { if (subCandidate.isSubtypeOf(superCandidate)) return TRUE; return FALSE; } case Binding.PARAMETERIZED_TYPE: { List<ConstraintFormula> constraints = new ArrayList<>(); while (superCandidate != null && superCandidate.kind() == Binding.PARAMETERIZED_TYPE && subCandidate != null) { if (!addConstraintsFromTypeParameters( subCandidate, (ParameterizedTypeBinding) superCandidate, constraints)) return FALSE; // travel to enclosing types to check if they have type parameters, too: superCandidate = superCandidate.enclosingType(); subCandidate = subCandidate.enclosingType(); } switch (constraints.size()) { case 0: return TRUE; case 1: return constraints.get(0); default: return constraints.toArray(new ConstraintFormula[constraints.size()]); } } case Binding.ARRAY_TYPE: TypeBinding tPrime = ((ArrayBinding) superCandidate).elementsType(); // let S'[] be the most specific array type that is a supertype of S (or S itself) ArrayBinding sPrimeArray = null; switch (subCandidate.kind()) { case Binding.INTERSECTION_TYPE: { WildcardBinding intersection = (WildcardBinding) subCandidate; sPrimeArray = findMostSpecificSuperArray( intersection.bound, intersection.otherBounds, intersection); break; } case Binding.ARRAY_TYPE: sPrimeArray = (ArrayBinding) subCandidate; break; case Binding.TYPE_PARAMETER: { TypeVariableBinding subTVB = (TypeVariableBinding) subCandidate; sPrimeArray = findMostSpecificSuperArray(subTVB.firstBound, subTVB.otherUpperBounds(), subTVB); break; } default: return FALSE; } if (sPrimeArray == null) return FALSE; TypeBinding sPrime = sPrimeArray.elementsType(); if (!tPrime.isPrimitiveType() && !sPrime.isPrimitiveType()) { return ConstraintTypeFormula.create(sPrime, tPrime, SUBTYPE, this.isSoft); } return TypeBinding.equalsEquals(tPrime, sPrime) ? TRUE : FALSE; // same primitive type? // "type variable" has two implementations in JDT: case Binding.WILDCARD_TYPE: if (subCandidate.kind() == Binding.INTERSECTION_TYPE) { ReferenceBinding[] intersectingTypes = subCandidate.getIntersectingTypes(); if (intersectingTypes != null) for (int i = 0; i < intersectingTypes.length; i++) if (TypeBinding.equalsEquals(intersectingTypes[i], superCandidate)) return true; } WildcardBinding variable = (WildcardBinding) superCandidate; if (variable.boundKind == Wildcard.SUPER) return ConstraintTypeFormula.create(subCandidate, variable.bound, SUBTYPE, this.isSoft); return FALSE; case Binding.TYPE_PARAMETER: // similar to wildcard, but different queries for lower bound if (subCandidate.kind() == Binding.INTERSECTION_TYPE) { ReferenceBinding[] intersectingTypes = subCandidate.getIntersectingTypes(); if (intersectingTypes != null) for (int i = 0; i < intersectingTypes.length; i++) if (TypeBinding.equalsEquals(intersectingTypes[i], superCandidate)) return true; } if (superCandidate instanceof CaptureBinding) { CaptureBinding capture = (CaptureBinding) superCandidate; if (capture.lowerBound != null && (capture.firstBound == null || capture.firstBound.id == TypeIds.T_JavaLangObject)) return ConstraintTypeFormula.create( subCandidate, capture.lowerBound, SUBTYPE, this.isSoft); } return FALSE; case Binding.INTERSECTION_TYPE: superCandidate = ((WildcardBinding) superCandidate).allBounds(); // $FALL-THROUGH$ case Binding.INTERSECTION_TYPE18: TypeBinding[] intersectingTypes = ((IntersectionTypeBinding18) superCandidate).intersectingTypes; ConstraintFormula[] result = new ConstraintFormula[intersectingTypes.length]; for (int i = 0; i < intersectingTypes.length; i++) { result[i] = ConstraintTypeFormula.create( subCandidate, intersectingTypes[i], SUBTYPE, this.isSoft); } return result; case Binding.POLY_TYPE: PolyTypeBinding poly = (PolyTypeBinding) superCandidate; Invocation invocation = (Invocation) poly.expression; MethodBinding binding = invocation.binding(); if (binding == null || !binding.isValidBinding()) return FALSE; TypeBinding returnType = binding.isConstructor() ? binding.declaringClass : binding.returnType; return reduceSubType( scope, subCandidate, returnType.capture(scope, invocation.sourceStart(), invocation.sourceEnd())); } throw new IllegalStateException("Unexpected RHS " + superCandidate); // $NON-NLS-1$ }
/** Locate declaration in the current class file. This class file is always in a jar. */ public void locateMatches(MatchLocator locator, ClassFile classFile, IBinaryType info) throws CoreException { SearchPattern pattern = locator.pattern; // check annotations references matchAnnotations(pattern, locator, classFile, info); // check class definition BinaryType binaryType = (BinaryType) classFile.getType(); if (matchBinary(pattern, info, null)) { binaryType = new ResolvedBinaryType( (JavaElement) binaryType.getParent(), binaryType.getElementName(), binaryType.getKey()); locator.reportBinaryMemberDeclaration(null, binaryType, null, info, SearchMatch.A_ACCURATE); return; } // Define arrays to store methods/fields from binary type if necessary IBinaryMethod[] binaryMethods = info.getMethods(); int bMethodsLength = binaryMethods == null ? 0 : binaryMethods.length; IBinaryMethod[] unresolvedMethods = null; char[][] binaryMethodSignatures = null; boolean hasUnresolvedMethods = false; // Get fields from binary type info IBinaryField[] binaryFields = info.getFields(); int bFieldsLength = binaryFields == null ? 0 : binaryFields.length; IBinaryField[] unresolvedFields = null; boolean hasUnresolvedFields = false; // Report as many accurate matches as possible int accuracy = SearchMatch.A_ACCURATE; boolean mustResolve = pattern.mustResolve; if (mustResolve) { BinaryTypeBinding binding = locator.cacheBinaryType(binaryType, info); if (binding != null) { // filter out element not in hierarchy scope if (!locator.typeInHierarchy(binding)) return; // Search matches on resolved methods MethodBinding[] availableMethods = binding.availableMethods(); int aMethodsLength = availableMethods == null ? 0 : availableMethods.length; hasUnresolvedMethods = bMethodsLength != aMethodsLength; for (int i = 0; i < aMethodsLength; i++) { MethodBinding method = availableMethods[i]; char[] methodSignature = method.genericSignature(); if (methodSignature == null) methodSignature = method.signature(); // Report the match if possible int level = locator.patternLocator.resolveLevel(method); if (level != PatternLocator.IMPOSSIBLE_MATCH) { IMethod methodHandle = binaryType.getMethod( new String( method.isConstructor() ? binding.compoundName[binding.compoundName.length - 1] : method.selector), CharOperation.toStrings( Signature.getParameterTypes(convertClassFileFormat(methodSignature)))); accuracy = level == PatternLocator.ACCURATE_MATCH ? SearchMatch.A_ACCURATE : SearchMatch.A_INACCURATE; locator.reportBinaryMemberDeclaration(null, methodHandle, method, info, accuracy); } // Remove method from unresolved list if (hasUnresolvedMethods) { if (binaryMethodSignatures == null) { // Store binary method signatures to avoid multiple computation binaryMethodSignatures = new char[bMethodsLength][]; for (int j = 0; j < bMethodsLength; j++) { IBinaryMethod binaryMethod = binaryMethods[j]; char[] signature = binaryMethod.getGenericSignature(); if (signature == null) signature = binaryMethod.getMethodDescriptor(); binaryMethodSignatures[j] = signature; } } for (int j = 0; j < bMethodsLength; j++) { if (CharOperation.equals(binaryMethods[j].getSelector(), method.selector) && CharOperation.equals(binaryMethodSignatures[j], methodSignature)) { if (unresolvedMethods == null) { System.arraycopy( binaryMethods, 0, unresolvedMethods = new IBinaryMethod[bMethodsLength], 0, bMethodsLength); } unresolvedMethods[j] = null; break; } } } } // Search matches on resolved fields FieldBinding[] availableFields = binding.availableFields(); int aFieldsLength = availableFields == null ? 0 : availableFields.length; hasUnresolvedFields = bFieldsLength != aFieldsLength; for (int i = 0; i < aFieldsLength; i++) { FieldBinding field = availableFields[i]; // Report the match if possible int level = locator.patternLocator.resolveLevel(field); if (level != PatternLocator.IMPOSSIBLE_MATCH) { IField fieldHandle = binaryType.getField(new String(field.name)); accuracy = level == PatternLocator.ACCURATE_MATCH ? SearchMatch.A_ACCURATE : SearchMatch.A_INACCURATE; locator.reportBinaryMemberDeclaration(null, fieldHandle, field, info, accuracy); } // Remove the field from unresolved list if (hasUnresolvedFields) { for (int j = 0; j < bFieldsLength; j++) { if (CharOperation.equals(binaryFields[j].getName(), field.name)) { if (unresolvedFields == null) { System.arraycopy( binaryFields, 0, unresolvedFields = new IBinaryField[bFieldsLength], 0, bFieldsLength); } unresolvedFields[j] = null; break; } } } } // If all methods/fields were accurate then returns now if (!hasUnresolvedMethods && !hasUnresolvedFields) { return; } } accuracy = SearchMatch.A_INACCURATE; } // Report inaccurate methods if (mustResolve) binaryMethods = unresolvedMethods; bMethodsLength = binaryMethods == null ? 0 : binaryMethods.length; for (int i = 0; i < bMethodsLength; i++) { IBinaryMethod method = binaryMethods[i]; if (method == null) continue; // impossible match or already reported as accurate if (matchBinary(pattern, method, info)) { char[] name; if (method.isConstructor()) { name = info.getName(); int lastSlash = CharOperation.lastIndexOf('/', name); if (lastSlash != -1) { name = CharOperation.subarray(name, lastSlash + 1, name.length); } } else { name = method.getSelector(); } String selector = new String(name); char[] methodSignature = binaryMethodSignatures == null ? null : binaryMethodSignatures[i]; if (methodSignature == null) { methodSignature = method.getGenericSignature(); if (methodSignature == null) methodSignature = method.getMethodDescriptor(); } String[] parameterTypes = CharOperation.toStrings( Signature.getParameterTypes(convertClassFileFormat(methodSignature))); IMethod methodHandle = binaryType.getMethod(selector, parameterTypes); methodHandle = new ResolvedBinaryMethod(binaryType, selector, parameterTypes, methodHandle.getKey()); locator.reportBinaryMemberDeclaration(null, methodHandle, null, info, accuracy); } } // Report inaccurate fields if (mustResolve) binaryFields = unresolvedFields; bFieldsLength = binaryFields == null ? 0 : binaryFields.length; for (int i = 0; i < bFieldsLength; i++) { IBinaryField field = binaryFields[i]; if (field == null) continue; // impossible match or already reported as accurate if (matchBinary(pattern, field, info)) { String fieldName = new String(field.getName()); IField fieldHandle = binaryType.getField(fieldName); fieldHandle = new ResolvedBinaryField(binaryType, fieldName, fieldHandle.getKey()); locator.reportBinaryMemberDeclaration(null, fieldHandle, null, info, accuracy); } } }
/* Binding creation is responsible for reporting: - all modifier problems (duplicates & multiple visibility modifiers + incompatible combinations) - plus invalid modifiers given the context... examples: - interface methods can only be public - abstract methods can only be defined by abstract classes - collisions... 2 methods with identical vmSelectors - multiple methods with the same message pattern but different return types - ambiguous, invisible or missing return/argument/exception types - check the type of any array is not void - check that each exception type is Throwable or a subclass of it */ void computeInheritedMethods() { // only want to remember inheritedMethods that can have an impact on the current type // if an inheritedMethod has been 'replaced' by a supertype's method then skip it this.inheritedMethods = new HashtableOfObject( 51); // maps method selectors to an array of methods... must search to match paramaters // & return type ReferenceBinding[][] interfacesToVisit = new ReferenceBinding[3][]; int lastPosition = -1; ReferenceBinding[] itsInterfaces = type.superInterfaces(); if (itsInterfaces != NoSuperInterfaces) interfacesToVisit[++lastPosition] = itsInterfaces; ReferenceBinding superType = this.type.isClass() ? this.type.superclass() : this.type.scope.getJavaLangObject(); // check interface methods against Object HashtableOfObject nonVisibleDefaultMethods = new HashtableOfObject(3); // maps method selectors to an array of methods boolean allSuperclassesAreAbstract = true; while (superType != null) { if (superType.isValidBinding()) { if (allSuperclassesAreAbstract) { if (superType.isAbstract()) { // only need to include superinterfaces if immediate superclasses are abstract if ((itsInterfaces = superType.superInterfaces()) != NoSuperInterfaces) { if (++lastPosition == interfacesToVisit.length) System.arraycopy( interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[lastPosition * 2][], 0, lastPosition); interfacesToVisit[lastPosition] = itsInterfaces; } } else { allSuperclassesAreAbstract = false; } } MethodBinding[] methods = superType.unResolvedMethods(); nextMethod: for (int m = methods.length; --m >= 0; ) { MethodBinding inheritedMethod = methods[m]; if (inheritedMethod.isPrivate() || inheritedMethod.isConstructor() || inheritedMethod.isDefaultAbstract()) continue nextMethod; MethodBinding[] existingMethods = (MethodBinding[]) this.inheritedMethods.get(inheritedMethod.selector); if (existingMethods != null) { for (int i = 0, length = existingMethods.length; i < length; i++) { if (doesMethodOverride(existingMethods[i], inheritedMethod)) { if (inheritedMethod.isDefault() && inheritedMethod.isAbstract()) checkPackagePrivateAbstractMethod(inheritedMethod); continue nextMethod; } } } MethodBinding[] nonVisible = (MethodBinding[]) nonVisibleDefaultMethods.get(inheritedMethod.selector); if (nonVisible != null) for (int i = 0, l = nonVisible.length; i < l; i++) if (doesMethodOverride(nonVisible[i], inheritedMethod)) continue nextMethod; if (!inheritedMethod.isDefault() || inheritedMethod.declaringClass.fPackage == type.fPackage) { if (existingMethods == null) { existingMethods = new MethodBinding[] {inheritedMethod}; } else { int length = existingMethods.length; System.arraycopy( existingMethods, 0, existingMethods = new MethodBinding[length + 1], 0, length); existingMethods[length] = inheritedMethod; } this.inheritedMethods.put(inheritedMethod.selector, existingMethods); } else { if (nonVisible == null) { nonVisible = new MethodBinding[] {inheritedMethod}; } else { int length = nonVisible.length; System.arraycopy( nonVisible, 0, nonVisible = new MethodBinding[length + 1], 0, length); nonVisible[length] = inheritedMethod; } nonVisibleDefaultMethods.put(inheritedMethod.selector, nonVisible); if (inheritedMethod.isAbstract() && !this.type .isAbstract()) // non visible abstract methods cannot be overridden so the type // must be defined abstract this.problemReporter().abstractMethodCannotBeOverridden(this.type, inheritedMethod); MethodBinding[] current = (MethodBinding[]) this.currentMethods.get(inheritedMethod.selector); if (current != null) { // non visible methods cannot be overridden so a warning is issued foundMatch: for (int i = 0, length = current.length; i < length; i++) { if (doesMethodOverride(current[i], inheritedMethod)) { this.problemReporter().overridesPackageDefaultMethod(current[i], inheritedMethod); break foundMatch; } } } } } superType = superType.superclass(); } } for (int i = 0; i <= lastPosition; i++) { ReferenceBinding[] interfaces = interfacesToVisit[i]; for (int j = 0, l = interfaces.length; j < l; j++) { superType = interfaces[j]; if ((superType.tagBits & InterfaceVisited) == 0) { superType.tagBits |= InterfaceVisited; if (superType.isValidBinding()) { if ((itsInterfaces = superType.superInterfaces()) != NoSuperInterfaces) { if (++lastPosition == interfacesToVisit.length) System.arraycopy( interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[lastPosition * 2][], 0, lastPosition); interfacesToVisit[lastPosition] = itsInterfaces; } MethodBinding[] methods = superType.unResolvedMethods(); nextMethod: for (int m = methods.length; --m >= 0; ) { // Interface methods are all abstract public MethodBinding inheritedMethod = methods[m]; MethodBinding[] existingMethods = (MethodBinding[]) this.inheritedMethods.get(inheritedMethod.selector); if (existingMethods == null) { existingMethods = new MethodBinding[] {inheritedMethod}; } else { int length = existingMethods.length; for (int e = 0; e < length; e++) { MethodBinding existing = existingMethods[e]; // look to see if any of the existingMethods implement this inheritedMethod if (areParametersEqual(existing, inheritedMethod) && existing.declaringClass.implementsInterface(superType, true)) // so if the implemented method is abstract & has a different return type then // did it get a bridge method? continue nextMethod; // skip interface method with the same signature if visible to // its declaringClass } System.arraycopy( existingMethods, 0, existingMethods = new MethodBinding[length + 1], 0, length); existingMethods[length] = inheritedMethod; } this.inheritedMethods.put(inheritedMethod.selector, existingMethods); } } } } } // bit reinitialization for (int i = 0; i <= lastPosition; i++) { ReferenceBinding[] interfaces = interfacesToVisit[i]; for (int j = 0, length = interfaces.length; j < length; j++) interfaces[j].tagBits &= ~InterfaceVisited; } }
/** * Check and fill in implicit annotations from overridden methods and from default. Precondition: * caller has checked whether annotation-based null analysis is enabled. */ public void checkImplicitNullAnnotations( MethodBinding currentMethod, AbstractMethodDeclaration srcMethod, boolean complain, Scope scope) { // check inherited nullness from superclass and superInterfaces try { ReferenceBinding currentType = currentMethod.declaringClass; if (currentType.id == TypeIds.T_JavaLangObject) { return; } boolean usesTypeAnnotations = scope.environment().usesNullTypeAnnotations(); boolean needToApplyReturnNonNullDefault = currentMethod.hasNonNullDefaultFor( Binding.DefaultLocationReturnType, usesTypeAnnotations); boolean needToApplyParameterNonNullDefault = currentMethod.hasNonNullDefaultFor(Binding.DefaultLocationParameter, usesTypeAnnotations); boolean needToApplyNonNullDefault = needToApplyReturnNonNullDefault | needToApplyParameterNonNullDefault; // compatibility & inheritance do not consider constructors / static methods: boolean isInstanceMethod = !currentMethod.isConstructor() && !currentMethod.isStatic(); complain &= isInstanceMethod; if (!needToApplyNonNullDefault && !complain && !(this.inheritNullAnnotations && isInstanceMethod)) { return; // short cut, no work to be done } if (isInstanceMethod) { List superMethodList = new ArrayList(); // need super types connected: if (currentType instanceof SourceTypeBinding && !currentType.isHierarchyConnected() && !currentType.isAnonymousType()) { ((SourceTypeBinding) currentType).scope.connectTypeHierarchy(); } int paramLen = currentMethod.parameters.length; findAllOverriddenMethods( currentMethod.original(), currentMethod.selector, paramLen, currentType, new HashSet(), superMethodList); // prepare interim storage for nullness info so we don't pollute currentMethod before we // know its conflict-free: InheritedNonNullnessInfo[] inheritedNonNullnessInfos = new InheritedNonNullnessInfo[paramLen + 1]; // index 0 is for the return type for (int i = 0; i < paramLen + 1; i++) inheritedNonNullnessInfos[i] = new InheritedNonNullnessInfo(); int length = superMethodList.size(); for (int i = length; --i >= 0; ) { MethodBinding currentSuper = (MethodBinding) superMethodList.get(i); if ((currentSuper.tagBits & TagBits.IsNullnessKnown) == 0) { // recurse to prepare currentSuper checkImplicitNullAnnotations( currentSuper, null, false, scope); // TODO (stephan) complain=true if currentSuper is source method?? } checkNullSpecInheritance( currentMethod, srcMethod, needToApplyReturnNonNullDefault, needToApplyParameterNonNullDefault, complain, currentSuper, null, scope, inheritedNonNullnessInfos); needToApplyNonNullDefault = false; } // transfer collected information into currentMethod: InheritedNonNullnessInfo info = inheritedNonNullnessInfos[0]; if (!info.complained) { long tagBits = 0; if (info.inheritedNonNullness == Boolean.TRUE) { tagBits = TagBits.AnnotationNonNull; } else if (info.inheritedNonNullness == Boolean.FALSE) { tagBits = TagBits.AnnotationNullable; } if (tagBits != 0) { if (!usesTypeAnnotations) { currentMethod.tagBits |= tagBits; } else { if (!currentMethod.returnType.isBaseType()) { LookupEnvironment env = scope.environment(); currentMethod.returnType = env.createAnnotatedType( currentMethod.returnType, env.nullAnnotationsFromTagBits(tagBits)); } } } } for (int i = 0; i < paramLen; i++) { info = inheritedNonNullnessInfos[i + 1]; if (!info.complained && info.inheritedNonNullness != null) { Argument currentArg = srcMethod == null ? null : srcMethod.arguments[i]; if (!usesTypeAnnotations) recordArgNonNullness( currentMethod, paramLen, i, currentArg, info.inheritedNonNullness); else recordArgNonNullness18( currentMethod, i, currentArg, info.inheritedNonNullness, scope.environment()); } } } if (needToApplyNonNullDefault) { if (!usesTypeAnnotations) currentMethod.fillInDefaultNonNullness(srcMethod); else currentMethod.fillInDefaultNonNullness18(srcMethod, scope.environment()); } } finally { currentMethod.tagBits |= TagBits.IsNullnessKnown; } }
/* Binding creation is responsible for reporting: - all modifier problems (duplicates & multiple visibility modifiers + incompatible combinations) - plus invalid modifiers given the context... examples: - interface methods can only be public - abstract methods can only be defined by abstract classes - collisions... 2 methods with identical vmSelectors - multiple methods with the same message pattern but different return types - ambiguous, invisible or missing return/argument/exception types - check the type of any array is not void - check that each exception type is Throwable or a subclass of it */ void computeInheritedMethods(ReferenceBinding superclass, ReferenceBinding[] superInterfaces) { // only want to remember inheritedMethods that can have an impact on the current type // if an inheritedMethod has been 'replaced' by a supertype's method then skip it this.inheritedMethods = new HashtableOfObject( 51); // maps method selectors to an array of methods... must search to match paramaters // & return type ReferenceBinding[] interfacesToVisit = null; int nextPosition = 0; ReferenceBinding[] itsInterfaces = superInterfaces; if (itsInterfaces != null) { nextPosition = itsInterfaces.length; interfacesToVisit = itsInterfaces; } ReferenceBinding superType = superclass; HashtableOfObject nonVisibleDefaultMethods = new HashtableOfObject(3); // maps method selectors to an array of methods boolean allSuperclassesAreAbstract = true; while (superType != null && superType.isValidBinding()) { if (allSuperclassesAreAbstract) { if (superType.isAbstract()) { } else { allSuperclassesAreAbstract = false; } } MethodBinding[] methods = superType.unResolvedMethods(); nextMethod: for (int m = methods.length; --m >= 0; ) { MethodBinding inheritedMethod = methods[m]; if (inheritedMethod.isPrivate() || inheritedMethod.isConstructor() || inheritedMethod.isDefaultAbstract()) continue nextMethod; MethodBinding[] existingMethods = (MethodBinding[]) this.inheritedMethods.get(inheritedMethod.selector); if (existingMethods != null) { for (int i = 0, length = existingMethods.length; i < length; i++) { if (existingMethods[i].declaringClass != inheritedMethod.declaringClass && areMethodsCompatible(existingMethods[i], inheritedMethod)) { if (inheritedMethod.isDefault() && inheritedMethod.isAbstract()) checkPackagePrivateAbstractMethod(inheritedMethod); continue nextMethod; } } } MethodBinding[] nonVisible = (MethodBinding[]) nonVisibleDefaultMethods.get(inheritedMethod.selector); if (nonVisible != null) for (int i = 0, l = nonVisible.length; i < l; i++) if (areMethodsCompatible(nonVisible[i], inheritedMethod)) continue nextMethod; if (!inheritedMethod.isDefault() || inheritedMethod.declaringClass.fPackage == type.fPackage) { if (existingMethods == null) { existingMethods = new MethodBinding[] {inheritedMethod}; } else { int length = existingMethods.length; System.arraycopy( existingMethods, 0, existingMethods = new MethodBinding[length + 1], 0, length); existingMethods[length] = inheritedMethod; } this.inheritedMethods.put(inheritedMethod.selector, existingMethods); } else { if (nonVisible == null) { nonVisible = new MethodBinding[] {inheritedMethod}; } else { int length = nonVisible.length; System.arraycopy(nonVisible, 0, nonVisible = new MethodBinding[length + 1], 0, length); nonVisible[length] = inheritedMethod; } nonVisibleDefaultMethods.put(inheritedMethod.selector, nonVisible); if (inheritedMethod.isAbstract() && !this.type .isAbstract()) // non visible abstract methods cannot be overridden so the type // must be defined abstract problemReporter().abstractMethodCannotBeOverridden(this.type, inheritedMethod); MethodBinding[] current = (MethodBinding[]) this.currentMethods.get(inheritedMethod.selector); if (current != null) { // non visible methods cannot be overridden so a warning is issued foundMatch: for (int i = 0, length = current.length; i < length; i++) { if (areMethodsCompatible(current[i], inheritedMethod)) { problemReporter().overridesPackageDefaultMethod(current[i], inheritedMethod); break foundMatch; } } } } } superType = superType.superclass(); } if (nextPosition == 0) return; for (int i = 0; i < nextPosition; i++) { superType = interfacesToVisit[i]; if (superType.isValidBinding()) { MethodBinding[] methods = superType.unResolvedMethods(); for (int m = methods.length; --m >= 0; ) { // Interface methods are all abstract public MethodBinding inheritedMethod = methods[m]; MethodBinding[] existingMethods = (MethodBinding[]) this.inheritedMethods.get(inheritedMethod.selector); if (existingMethods == null) { existingMethods = new MethodBinding[] {inheritedMethod}; } else { int length = existingMethods.length; System.arraycopy( existingMethods, 0, existingMethods = new MethodBinding[length + 1], 0, length); existingMethods[length] = inheritedMethod; } this.inheritedMethods.put(inheritedMethod.selector, existingMethods); } } } }