import eclipse 3.1 M4 compiler
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / lookup / MethodVerifier.java
index 115864c..e47cdd3 100644 (file)
  *******************************************************************************/
 package org.eclipse.jdt.internal.compiler.lookup;
 
-import org.eclipse.jdt.core.compiler.CharOperation;
 import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
 import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.env.IConstants;
 import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
 import org.eclipse.jdt.internal.compiler.util.HashtableOfObject;
 
-public final class MethodVerifier implements TagBits, TypeConstants {
+public class MethodVerifier implements TagBits, TypeConstants {
        SourceTypeBinding type;
        HashtableOfObject inheritedMethods;
        HashtableOfObject currentMethods;
@@ -40,7 +40,7 @@ Binding creation is responsible for reporting all problems with types:
                - defining a static class or interface inside a non-static nested class
                - defining an interface as a local type (local types can only be classes)
 */
-public MethodVerifier(LookupEnvironment environment) {
+MethodVerifier(LookupEnvironment environment) {
        this.type = null;  // Initialized with the public method verify(SourceTypeBinding)
        this.inheritedMethods = null;
        this.currentMethods = null;
@@ -48,7 +48,10 @@ public MethodVerifier(LookupEnvironment environment) {
        this.errorException = null;
        this.environment = environment;
 }
-private boolean areParametersEqual(MethodBinding one, MethodBinding two) {
+boolean areMethodsEqual(MethodBinding one, MethodBinding two) {
+       return areParametersEqual(one, two);
+}
+boolean areParametersEqual(MethodBinding one, MethodBinding two) {
        TypeBinding[] oneArgs = one.parameters;
        TypeBinding[] twoArgs = two.parameters;
        if (oneArgs == twoArgs) return true;
@@ -60,51 +63,79 @@ private boolean areParametersEqual(MethodBinding one, MethodBinding two) {
                if (!areTypesEqual(oneArgs[i], twoArgs[i])) return false;
        return true;
 }
-private boolean areReturnTypesEqual(MethodBinding one, MethodBinding two) {
+boolean areReturnTypesEqual(MethodBinding one, MethodBinding two) {
        return areTypesEqual(one.returnType, two.returnType);
 }
-private boolean areTypesEqual(TypeBinding one, TypeBinding two) {
+boolean canSkipInheritedMethods() {
+       if (this.type.superclass() != null && this.type.superclass().isAbstract())
+               return false;
+       return this.type.superInterfaces() == NoSuperInterfaces;
+}
+boolean canSkipInheritedMethods(MethodBinding one, MethodBinding two) {
+       return two == null // already know one is not null
+               || one.declaringClass == two.declaringClass;
+}
+boolean areTypesEqual(TypeBinding one, TypeBinding two) {
        if (one == two) return true;
-       if (one instanceof ReferenceBinding && two instanceof ReferenceBinding)
-               // can compare unresolved to resolved reference bindings
-               return CharOperation.equals(((ReferenceBinding) one).compoundName, ((ReferenceBinding) two).compoundName);
+
+       // its possible that an UnresolvedReferenceBinding can be compared to its resolved type
+       // when they're both UnresolvedReferenceBindings then they must be identical like all other types
+       // all wrappers of UnresolvedReferenceBindings are converted as soon as the type is resolved
+       // so its not possible to have 2 arrays where one is UnresolvedX[] and the other is X[]
+       if (one instanceof UnresolvedReferenceBinding)
+               return ((UnresolvedReferenceBinding) one).resolvedType == two;
+       if (two instanceof UnresolvedReferenceBinding)
+               return ((UnresolvedReferenceBinding) two).resolvedType == one;
        return false; // all other type bindings are identical
 }
-private void checkAbstractMethod(MethodBinding abstractMethod) {
-       if (mustImplementAbstractMethod(abstractMethod)) {
+void checkAbstractMethod(MethodBinding abstractMethod) {
+       if (mustImplementAbstractMethod(abstractMethod.declaringClass)) {
                TypeDeclaration typeDeclaration = this.type.scope.referenceContext;
                if (typeDeclaration != null) {
                        MethodDeclaration missingAbstractMethod = typeDeclaration.addMissingAbstractMethodFor(abstractMethod);
                        missingAbstractMethod.scope.problemReporter().abstractMethodMustBeImplemented(this.type, abstractMethod);
                } else {
-                       this.problemReporter().abstractMethodMustBeImplemented(this.type, abstractMethod);
+                       problemReporter().abstractMethodMustBeImplemented(this.type, abstractMethod);
                }
        }
 }
-private void checkAgainstInheritedMethods(MethodBinding currentMethod, MethodBinding[] methods, int length) {
+void checkAgainstInheritedMethods(MethodBinding currentMethod, MethodBinding[] methods, int length) {
+       boolean isAnnotationMember = this.type.isAnnotationType();
        nextMethod : for (int i = length; --i >= 0;) {
                MethodBinding inheritedMethod = methods[i];
                if (currentMethod.isStatic() != inheritedMethod.isStatic()) {  // Cannot override a static method or hide an instance method
-                       this.problemReporter(currentMethod).staticAndInstanceConflict(currentMethod, inheritedMethod);
+                       problemReporter(currentMethod).staticAndInstanceConflict(currentMethod, inheritedMethod);
                        continue nextMethod;
                }
 
-               if (!currentMethod.isAbstract() && inheritedMethod.isAbstract()) {
-                       if ((currentMethod.modifiers & CompilerModifiers.AccOverriding) == 0)
+               if (inheritedMethod.isAbstract()) {
+                       if (inheritedMethod.declaringClass.isInterface()) {
                                currentMethod.modifiers |= CompilerModifiers.AccImplementing;
+                       } else {
+                               currentMethod.modifiers |= CompilerModifiers.AccImplementing | CompilerModifiers.AccOverriding;
+                       }
+// with the above change an abstract method is tagged as implementing the inherited abstract method
+//             if (!currentMethod.isAbstract() && inheritedMethod.isAbstract()) {
+//                     if ((currentMethod.modifiers & CompilerModifiers.AccOverriding) == 0)
+//                             currentMethod.modifiers |= CompilerModifiers.AccImplementing;
                } else {
                        currentMethod.modifiers |= CompilerModifiers.AccOverriding;
                }
 
+               if (isAnnotationMember) {
+                       // annotation cannot override any method
+                       problemReporter().annotationCannotOverrideMethod(currentMethod, inheritedMethod);
+                       return; // do not repoort against subsequent inherited methods
+               }               
                if (!areReturnTypesEqual(currentMethod, inheritedMethod)) {
-                       this.problemReporter(currentMethod).incompatibleReturnType(currentMethod, inheritedMethod);
+                       problemReporter(currentMethod).incompatibleReturnType(currentMethod, inheritedMethod);
                } else {
                        if (currentMethod.thrownExceptions != NoExceptions)
-                               this.checkExceptions(currentMethod, inheritedMethod);
+                               checkExceptions(currentMethod, inheritedMethod);
                        if (inheritedMethod.isFinal())
-                               this.problemReporter(currentMethod).finalMethodCannotBeOverridden(currentMethod, inheritedMethod);
-                       if (!this.isAsVisible(currentMethod, inheritedMethod))
-                               this.problemReporter(currentMethod).visibilityConflict(currentMethod, inheritedMethod);
+                               problemReporter(currentMethod).finalMethodCannotBeOverridden(currentMethod, inheritedMethod);
+                       if (!isAsVisible(currentMethod, inheritedMethod))
+                               problemReporter(currentMethod).visibilityConflict(currentMethod, inheritedMethod);
                        if (environment.options.reportDeprecationWhenOverridingDeprecatedMethod && inheritedMethod.isViewedAsDeprecated()) {
                                if (!currentMethod.isViewedAsDeprecated() || environment.options.reportDeprecationInsideDeprecatedCode) {
                                        // check against the other inherited methods to see if they hide this inheritedMethod
@@ -114,9 +145,10 @@ private void checkAgainstInheritedMethods(MethodBinding currentMethod, MethodBin
                                                        if (i != j && methods[j].declaringClass.implementsInterface(declaringClass, false))
                                                                continue nextMethod;
 
-                                       this.problemReporter(currentMethod).overridesDeprecatedMethod(currentMethod, inheritedMethod);
+                                       problemReporter(currentMethod).overridesDeprecatedMethod(currentMethod, inheritedMethod);
                                }
                        }
+                       checkForBridgeMethod(currentMethod, inheritedMethod);
                }
        }
 }
@@ -126,24 +158,27 @@ Verify that newExceptions are all included in inheritedExceptions.
 Assumes all exceptions are valid and throwable.
 Unchecked exceptions (compatible with runtime & error) are ignored (see the spec on pg. 203).
 */
-private void checkExceptions(MethodBinding newMethod, MethodBinding inheritedMethod) {
+void checkExceptions(MethodBinding newMethod, MethodBinding inheritedMethod) {
        ReferenceBinding[] newExceptions = resolvedExceptionTypesFor(newMethod);
        ReferenceBinding[] inheritedExceptions = resolvedExceptionTypesFor(inheritedMethod);
        for (int i = newExceptions.length; --i >= 0;) {
                ReferenceBinding newException = newExceptions[i];
                int j = inheritedExceptions.length;
-               while (--j > -1 && !this.isSameClassOrSubclassOf(newException, inheritedExceptions[j])){/*empty*/}
+               while (--j > -1 && !isSameClassOrSubclassOf(newException, inheritedExceptions[j])){/*empty*/}
                if (j == -1)
-                       if (!(newException.isCompatibleWith(this.runtimeException()) || newException.isCompatibleWith(this.errorException())))
-                               this.problemReporter(newMethod).incompatibleExceptionInThrowsClause(this.type, newMethod, inheritedMethod, newException);
+                       if (!(newException.isCompatibleWith(runtimeException()) || newException.isCompatibleWith(errorException())))
+                               problemReporter(newMethod).incompatibleExceptionInThrowsClause(this.type, newMethod, inheritedMethod, newException);
        }
 }
-private void checkInheritedMethods(MethodBinding[] methods, int length) {
+void checkForBridgeMethod(MethodBinding currentMethod, MethodBinding inheritedMethod) {
+       // no op before 1.5
+}
+void checkInheritedMethods(MethodBinding[] methods, int length) {
        MethodBinding first = methods[0];
        int index = length;
        while (--index > 0 && areReturnTypesEqual(first, methods[index])){/*empty*/}
        if (index > 0) {  // All inherited methods do NOT have the same vmSignature
-               this.problemReporter().inheritedMethodsHaveIncompatibleReturnTypes(this.type, methods, length);
+               problemReporter().inheritedMethodsHaveIncompatibleReturnTypes(this.type, methods, length);
                return;
        }
 
@@ -159,13 +194,13 @@ private void checkInheritedMethods(MethodBinding[] methods, int length) {
        if (concreteMethod == null) {
                if (this.type.isClass() && !this.type.isAbstract()) {
                        for (int i = length; --i >= 0;) {
-                               if (mustImplementAbstractMethod(methods[i])) {
+                               if (mustImplementAbstractMethod(methods[i].declaringClass)) {
                                        TypeDeclaration typeDeclaration = this.type.scope.referenceContext;
                                        if (typeDeclaration != null) {
                                                MethodDeclaration missingAbstractMethod = typeDeclaration.addMissingAbstractMethodFor(methods[0]);
                                                missingAbstractMethod.scope.problemReporter().abstractMethodMustBeImplemented(this.type, methods[0]);
                                        } else {
-                                               this.problemReporter().abstractMethodMustBeImplemented(this.type, methods[0]);
+                                               problemReporter().abstractMethodMustBeImplemented(this.type, methods[0]);
                                        }
                                        return;
                                }
@@ -183,13 +218,13 @@ private void checkInheritedMethods(MethodBinding[] methods, int length) {
        // Remember that interfaces can only define public instance methods
        if (concreteMethod.isStatic())
                // Cannot inherit a static method which is specified as an instance method by an interface
-               this.problemReporter().staticInheritedMethodConflicts(type, concreteMethod, abstractMethods);   
+               problemReporter().staticInheritedMethodConflicts(type, concreteMethod, abstractMethods);        
        if (!concreteMethod.isPublic())
                // Cannot reduce visibility of a public method specified by an interface
-               this.problemReporter().inheritedMethodReducesVisibility(type, concreteMethod, abstractMethods);
+               problemReporter().inheritedMethodReducesVisibility(type, concreteMethod, abstractMethods);
        if (concreteMethod.thrownExceptions != NoExceptions)
                for (int i = abstractMethods.length; --i >= 0;)
-                       this.checkExceptions(concreteMethod, abstractMethods[i]);
+                       checkExceptions(concreteMethod, abstractMethods[i]);
 }
 /*
 For each inherited method identifier (message pattern - vm signature minus the return type)
@@ -209,10 +244,9 @@ For each inherited method identifier (message pattern - vm signature minus the r
                                else
                                        complain about missing implementation only if type is NOT an interface or abstract
 */
-private void checkMethods() {
-       boolean mustImplementAbstractMethods = this.type.isClass() && !this.type.isAbstract();
-       boolean skipInheritedMethods = mustImplementAbstractMethods && this.type.superInterfaces() == NoSuperInterfaces
-               && this.type.superclass() != null && !this.type.superclass().isAbstract(); // have a single concrete superclass so only check overridden methods
+void checkMethods() {
+       boolean mustImplementAbstractMethods = ((this.type.modifiers & IConstants.AccInterface) == 0) && !this.type.isAbstract();
+       boolean skipInheritedMethods = mustImplementAbstractMethods && canSkipInheritedMethods(); // have a single concrete superclass so only check overridden methods
        char[][] methodSelectors = this.inheritedMethods.keyTable;
        nextSelector : for (int s = methodSelectors.length; --s >= 0;) {
                if (methodSelectors[s] == null) continue nextSelector;
@@ -236,13 +270,16 @@ private void checkMethods() {
                                MethodBinding currentMethod = current[i];
                                for (int j = 0, length2 = inherited.length; j < length2; j++) {
                                        MethodBinding inheritedMethod = inherited[j];
-                                       if (inheritedMethod != null && areParametersEqual(currentMethod, inheritedMethod)) {
-                                               matchingInherited[++index] = inheritedMethod;
-                                               inherited[j] = null; // do not want to find it again
+                                       if (inheritedMethod != null) {
+                                               inheritedMethod = computeSubstituteMethod(inheritedMethod, currentMethod);
+                                               if (areMethodsEqual(currentMethod, inheritedMethod)) {
+                                                       matchingInherited[++index] = inheritedMethod;
+                                                       inherited[j] = null; // do not want to find it again
+                                               }
                                        }
                                }
                                if (index >= 0)
-                                       this.checkAgainstInheritedMethods(currentMethod, matchingInherited, index + 1); // pass in the length of matching
+                                       checkAgainstInheritedMethods(currentMethod, matchingInherited, index + 1); // pass in the length of matching
                        }
                }
 
@@ -252,39 +289,48 @@ private void checkMethods() {
                        if (inheritedMethod != null) {
                                matchingInherited[++index] = inheritedMethod;
                                for (int j = i + 1; j < length; j++) {
-                                       if (inherited[j] != null && areParametersEqual(inheritedMethod, inherited[j])) {
-                                               matchingInherited[++index] = inherited[j];
+                                       MethodBinding otherInheritedMethod = inherited[j];
+                                       if (canSkipInheritedMethods(inheritedMethod, otherInheritedMethod))
+                                               continue;
+                                       otherInheritedMethod = computeSubstituteMethod(otherInheritedMethod, inheritedMethod);
+                                       if (areMethodsEqual(inheritedMethod, otherInheritedMethod)) {
+                                               matchingInherited[++index] = otherInheritedMethod;
                                                inherited[j] = null; // do not want to find it again
                                        }
                                }
                        }
                        if (index > 0)
-                               this.checkInheritedMethods(matchingInherited, index + 1); // pass in the length of matching
+                               checkInheritedMethods(matchingInherited, index + 1); // pass in the length of matching
                        else if (mustImplementAbstractMethods && index == 0 && matchingInherited[0].isAbstract())
                                checkAbstractMethod(matchingInherited[0]);
                }
        }
 }
-private void checkPackagePrivateAbstractMethod(MethodBinding abstractMethod) {
+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
 
-               MethodBinding[] methods = superType.getMethods(selector);
-               nextMethod : for (int m = methods.length; --m >= 0;) {
-                       MethodBinding method = methods[m];
-                       if (!areReturnTypesEqual(method, abstractMethod) || !areParametersEqual(method, abstractMethod))
-                               continue nextMethod;
-                       if (method.isPrivate() || method.isConstructor() || method.isDefaultAbstract())
-                               continue nextMethod;
-                       if (superType.fPackage == abstractMethod.declaringClass.fPackage) return; // found concrete implementation of abstract method in same package
+               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);
+       problemReporter().abstractMethodCannotBeOverridden(this.type, abstractMethod);
 }
 /*
 Binding creation is responsible for reporting:
@@ -298,7 +344,10 @@ Binding creation is responsible for reporting:
        - check the type of any array is not void
        - check that each exception type is Throwable or a subclass of it
 */
-private void computeInheritedMethods() {
+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;
@@ -306,83 +355,81 @@ private void computeInheritedMethods() {
        if (itsInterfaces != NoSuperInterfaces)
                interfacesToVisit[++lastPosition] = itsInterfaces;
 
-       ReferenceBinding superType = this.type.isClass()
-               ? this.type.superclass()
+       ReferenceBinding superType = (this.type.modifiers & IConstants.AccInterface) == 0
+               ? this.type.superclass() // class or enum
                : 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;
+       while (superType != null && 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 method = methods[m];
-                               if (method.isPrivate() || method.isConstructor() || method.isDefaultAbstract())
-                                       continue nextMethod;
-                               MethodBinding[] existingMethods = (MethodBinding[]) this.inheritedMethods.get(method.selector);
-                               if (existingMethods != null) {
-                                       for (int i = 0, length = existingMethods.length; i < length; i++) {
-                                               if (areReturnTypesEqual(method, existingMethods[i]) && areParametersEqual(method, existingMethods[i])) {
-                                                       if (method.isDefault() && method.isAbstract() && method.declaringClass.fPackage != type.fPackage)
-                                                               checkPackagePrivateAbstractMethod(method);
-                                                       continue nextMethod;
-                                               }
+               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 && doesMethodOverride(existingMethods[i], inheritedMethod)) {
+                                               if (inheritedMethod.isDefault() && inheritedMethod.isAbstract())
+                                                       checkPackagePrivateAbstractMethod(inheritedMethod);
+                                               continue nextMethod;
                                        }
                                }
-                               MethodBinding[] nonVisible = (MethodBinding[]) nonVisibleDefaultMethods.get(method.selector);
-                               if (nonVisible != null)
-                                       for (int i = 0, l = nonVisible.length; i < l; i++)
-                                               if (areReturnTypesEqual(method, nonVisible[i]) && areParametersEqual(method, nonVisible[i])) 
-                                                       continue nextMethod;
-
-                               if (!method.isDefault() || method.declaringClass.fPackage == type.fPackage) {
-                                       if (existingMethods == null) {
-                                               existingMethods = new MethodBinding[] {method};
-                                       } else {
-                                               int length = existingMethods.length;
-                                               System.arraycopy(existingMethods, 0, existingMethods = new MethodBinding[length + 1], 0, length);
-                                               existingMethods[length] = method;
-                                       }
-                                       this.inheritedMethods.put(method.selector, existingMethods);
+                       }
+                       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 {
-                                       if (nonVisible == null) {
-                                               nonVisible = new MethodBinding[] {method};
-                                       } else {
-                                               int length = nonVisible.length;
-                                               System.arraycopy(nonVisible, 0, nonVisible = new MethodBinding[length + 1], 0, length);
-                                               nonVisible[length] = method;
-                                       }
-                                       nonVisibleDefaultMethods.put(method.selector, nonVisible);
-
-                                       if (method.isAbstract() && !this.type.isAbstract()) // non visible abstract methods cannot be overridden so the type must be defined abstract
-                                               this.problemReporter().abstractMethodCannotBeOverridden(this.type, method);
-
-                                       MethodBinding[] current = (MethodBinding[]) this.currentMethods.get(method.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 (areReturnTypesEqual(method, current[i]) && areParametersEqual(method, current[i])) {
-                                                               this.problemReporter().overridesPackageDefaultMethod(current[i], method);
-                                                               break foundMatch;
-                                                       }
+                                       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 (doesMethodOverride(current[i], inheritedMethod)) {
+                                                       problemReporter().overridesPackageDefaultMethod(current[i], inheritedMethod);
+                                                       break foundMatch;
                                                }
                                        }
                                }
                        }
-                       superType = superType.superclass();
                }
+               superType = superType.superclass();
        }
 
        for (int i = 0; i <= lastPosition; i++) {
@@ -400,21 +447,20 @@ private void computeInheritedMethods() {
 
                                        MethodBinding[] methods = superType.unResolvedMethods();
                                        nextMethod : for (int m = methods.length; --m >= 0;) { // Interface methods are all abstract public
-                                               MethodBinding method = methods[m];
-                                               MethodBinding[] existingMethods = (MethodBinding[]) this.inheritedMethods.get(method.selector);
+                                               MethodBinding inheritedMethod = methods[m];
+                                               MethodBinding[] existingMethods = (MethodBinding[]) this.inheritedMethods.get(inheritedMethod.selector);
                                                if (existingMethods == null) {
-                                                       existingMethods = new MethodBinding[] {method};
+                                                       existingMethods = new MethodBinding[] {inheritedMethod};
                                                } else {
                                                        int length = existingMethods.length;
-                                                       for (int e = 0; e < length; e++) {
-                                                               MethodBinding existing = existingMethods[e];
-                                                               if (areParametersEqual(method, existing) && existing.declaringClass.implementsInterface(superType, true))
+                                                       // look to see if any of the existingMethods implement this inheritedMethod
+                                                       for (int e = 0; e < length; e++)
+                                                               if (isInterfaceMethodImplemented(inheritedMethod, existingMethods[e], superType))
                                                                        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] = method;
+                                                       existingMethods[length] = inheritedMethod;
                                                }
-                                               this.inheritedMethods.put(method.selector, existingMethods);
+                                               this.inheritedMethods.put(inheritedMethod.selector, existingMethods);
                                        }
                                }
                        }
@@ -428,7 +474,7 @@ private void computeInheritedMethods() {
                        interfaces[j].tagBits &= ~InterfaceVisited;
        }
 }
-private void computeMethods() {
+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
@@ -446,12 +492,18 @@ private void computeMethods() {
                }
        }
 }
-private ReferenceBinding errorException() {
+MethodBinding computeSubstituteMethod(MethodBinding inheritedMethod, MethodBinding currentMethod) {
+       return inheritedMethod;
+}
+public boolean doesMethodOverride(MethodBinding method, MethodBinding inheritedMethod) {
+       return areReturnTypesEqual(method, inheritedMethod) && areParametersEqual(method, inheritedMethod);
+}
+ReferenceBinding errorException() {
        if (errorException == null)
                this.errorException = this.type.scope.getJavaLangError();
        return errorException;
 }
-private boolean isAsVisible(MethodBinding newMethod, MethodBinding inheritedMethod) {
+boolean isAsVisible(MethodBinding newMethod, MethodBinding inheritedMethod) {
        if (inheritedMethod.modifiers == newMethod.modifiers) return true;
 
        if (newMethod.isPublic()) return true;          // Covers everything
@@ -462,17 +514,20 @@ private boolean isAsVisible(MethodBinding newMethod, MethodBinding inheritedMeth
 
        return !newMethod.isPrivate();          // The inheritedMethod cannot be private since it would not be visible
 }
-private boolean isSameClassOrSubclassOf(ReferenceBinding testClass, ReferenceBinding superclass) {
+boolean isInterfaceMethodImplemented(MethodBinding inheritedMethod, MethodBinding existingMethod, ReferenceBinding superType) {
+       // skip interface method with the same signature if visible to its declaringClass
+       return areParametersEqual(existingMethod, inheritedMethod) && existingMethod.declaringClass.implementsInterface(superType, true);
+}
+boolean isSameClassOrSubclassOf(ReferenceBinding testClass, ReferenceBinding superclass) {
        do {
                if (testClass == superclass) return true;
        } while ((testClass = testClass.superclass()) != null);
        return false;
 }
-private boolean mustImplementAbstractMethod(MethodBinding abstractMethod) {
+boolean mustImplementAbstractMethod(ReferenceBinding declaringClass) {
        // if the type's superclass is an abstract class, then all abstract methods must be implemented
        // otherwise, skip it if the type's superclass must implement any of the inherited methods
        ReferenceBinding superclass = this.type.superclass();
-       ReferenceBinding declaringClass = abstractMethod.declaringClass;
        if (declaringClass.isClass()) {
                while (superclass.isAbstract() && superclass != declaringClass)
                        superclass = superclass.superclass(); // find the first concrete superclass or the abstract declaringClass
@@ -487,10 +542,10 @@ private boolean mustImplementAbstractMethod(MethodBinding abstractMethod) {
        }
        return superclass.isAbstract();         // if it is a concrete class then we have already reported problem against it
 }
-private ProblemReporter problemReporter() {
+ProblemReporter problemReporter() {
        return this.type.scope.problemReporter();
 }
-private ProblemReporter problemReporter(MethodBinding currentMethod) {
+ProblemReporter problemReporter(MethodBinding currentMethod) {
        ProblemReporter reporter = problemReporter();
        if (currentMethod.declaringClass == type)       // only report against the currentMethod if its implemented by the type
                reporter.referenceContext = currentMethod.sourceMethod();
@@ -503,23 +558,21 @@ ReferenceBinding[] resolvedExceptionTypesFor(MethodBinding method) {
 
        if (!(method.declaringClass instanceof BinaryTypeBinding))
                return TypeConstants.NoExceptions; // safety check
-       BinaryTypeBinding binaryType = (BinaryTypeBinding) method.declaringClass;
 
        for (int i = exceptions.length; --i >= 0;)
-               if (exceptions[i] instanceof UnresolvedReferenceBinding)
-                       exceptions[i] = (ReferenceBinding) binaryType.resolveType(exceptions[i]);
+               exceptions[i] = BinaryTypeBinding.resolveType(exceptions[i], this.environment, true);
        return exceptions;
 }
-private ReferenceBinding runtimeException() {
+ReferenceBinding runtimeException() {
        if (runtimeException == null)
                this.runtimeException = this.type.scope.getJavaLangRuntimeException();
        return runtimeException;
 }
-public void verify(SourceTypeBinding someType) {
+void verify(SourceTypeBinding someType) {
        this.type = someType;
-       this.computeMethods();
-       this.computeInheritedMethods();
-       this.checkMethods();
+       computeMethods();
+       computeInheritedMethods();
+       checkMethods();
 }
 public String toString() {
        StringBuffer buffer = new StringBuffer(10);