KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > compiler > lookup > MethodVerifier


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jdt.internal.compiler.lookup;
12
13 import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
14 import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
15 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
16 import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
17 import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
18 import org.eclipse.jdt.internal.compiler.util.HashtableOfObject;
19 import org.eclipse.jdt.internal.compiler.util.SimpleSet;
20
21 public class MethodVerifier {
22     SourceTypeBinding type;
23     HashtableOfObject inheritedMethods;
24     HashtableOfObject currentMethods;
25     LookupEnvironment environment;
26     private boolean allowCompatibleReturnTypes;
27 /*
28 Binding creation is responsible for reporting all problems with types:
29     - all modifier problems (duplicates & multiple visibility modifiers + incompatible combinations - abstract/final)
30         - plus invalid modifiers given the context (the verifier did not do this before)
31     - qualified name collisions between a type and a package (types in default packages are excluded)
32     - all type hierarchy problems:
33         - cycles in the superclass or superinterface hierarchy
34         - an ambiguous, invisible or missing superclass or superinterface
35         - extending a final class
36         - extending an interface instead of a class
37         - implementing a class instead of an interface
38         - implementing the same interface more than once (ie. duplicate interfaces)
39     - with nested types:
40         - shadowing an enclosing type's source name
41         - defining a static class or interface inside a non-static nested class
42         - defining an interface as a local type (local types can only be classes)
43 */

44 MethodVerifier(LookupEnvironment environment) {
45     this.type = null; // Initialized with the public method verify(SourceTypeBinding)
46
this.inheritedMethods = null;
47     this.currentMethods = null;
48     this.environment = environment;
49     this.allowCompatibleReturnTypes =
50         environment.globalOptions.complianceLevel >= ClassFileConstants.JDK1_5
51             && environment.globalOptions.sourceLevel < ClassFileConstants.JDK1_5;
52 }
53 boolean areMethodsCompatible(MethodBinding one, MethodBinding two) {
54     return doesMethodOverride(one, two) && areReturnTypesCompatible(one, two);
55 }
56 boolean areParametersEqual(MethodBinding one, MethodBinding two) {
57     TypeBinding[] oneArgs = one.parameters;
58     TypeBinding[] twoArgs = two.parameters;
59     if (oneArgs == twoArgs) return true;
60
61     int length = oneArgs.length;
62     if (length != twoArgs.length) return false;
63
64     for (int i = 0; i < length; i++)
65         if (!areTypesEqual(oneArgs[i], twoArgs[i])) return false;
66     return true;
67 }
68 boolean areReturnTypesCompatible(MethodBinding one, MethodBinding two) {
69     if (one.returnType == two.returnType) return true;
70
71     if (areTypesEqual(one.returnType, two.returnType)) return true;
72     
73     // when sourceLevel < 1.5 but compliance >= 1.5, allow return types in binaries to be compatible instead of just equal
74
if (this.allowCompatibleReturnTypes &&
75             one.declaringClass instanceof BinaryTypeBinding &&
76             two.declaringClass instanceof BinaryTypeBinding) {
77         return areReturnTypesCompatible0(one, two);
78     }
79     return false;
80 }
81 boolean areReturnTypesCompatible0(MethodBinding one, MethodBinding two) {
82     // short is compatible with int, but as far as covariance is concerned, its not
83
if (one.returnType.isBaseType()) return false;
84
85     if (!one.declaringClass.isInterface()) {
86         if (one.declaringClass.id == TypeIds.T_JavaLangObject)
87             return two.returnType.isCompatibleWith(one.returnType); // interface methods inherit from Object
88
return one.returnType.isCompatibleWith(two.returnType);
89     }
90
91     // check for methods from Object, every interface inherits from Object
92
if (two.declaringClass.id == TypeIds.T_JavaLangObject)
93         return one.returnType.isCompatibleWith(two.returnType);
94
95     // both are interfaces, see if they're related
96
if (one.declaringClass.implementsInterface(two.declaringClass, true))
97         return one.returnType.isCompatibleWith(two.returnType);
98     if (two.declaringClass.implementsInterface(one.declaringClass, true))
99         return two.returnType.isCompatibleWith(one.returnType);
100
101     // unrelated interfaces... one must be a subtype of the other
102
return one.returnType.isCompatibleWith(two.returnType)
103         || two.returnType.isCompatibleWith(one.returnType);
104 }
105 boolean areTypesEqual(TypeBinding one, TypeBinding two) {
106     if (one == two) return true;
107
108     // its possible that an UnresolvedReferenceBinding can be compared to its resolved type
109
// when they're both UnresolvedReferenceBindings then they must be identical like all other types
110
// all wrappers of UnresolvedReferenceBindings are converted as soon as the type is resolved
111
// so its not possible to have 2 arrays where one is UnresolvedX[] and the other is X[]
112
if (one instanceof UnresolvedReferenceBinding)
113         return ((UnresolvedReferenceBinding) one).resolvedType == two;
114     if (two instanceof UnresolvedReferenceBinding)
115         return ((UnresolvedReferenceBinding) two).resolvedType == one;
116     return false; // all other type bindings are identical
117
}
118 boolean canSkipInheritedMethods() {
119     if (this.type.superclass() != null && this.type.superclass().isAbstract())
120         return false;
121     return this.type.superInterfaces() == Binding.NO_SUPERINTERFACES;
122 }
123 boolean canSkipInheritedMethods(MethodBinding one, MethodBinding two) {
124     return two == null // already know one is not null
125
|| one.declaringClass == two.declaringClass;
126 }
127 void checkAbstractMethod(MethodBinding abstractMethod) {
128     if (mustImplementAbstractMethod(abstractMethod.declaringClass)) {
129         TypeDeclaration typeDeclaration = this.type.scope.referenceContext;
130         if (typeDeclaration != null) {
131             MethodDeclaration missingAbstractMethod = typeDeclaration.addMissingAbstractMethodFor(abstractMethod);
132             missingAbstractMethod.scope.problemReporter().abstractMethodMustBeImplemented(this.type, abstractMethod);
133         } else {
134             problemReporter().abstractMethodMustBeImplemented(this.type, abstractMethod);
135         }
136     }
137 }
138 void checkAgainstInheritedMethods(MethodBinding currentMethod, MethodBinding[] methods, int length, MethodBinding[] allInheritedMethods) {
139     if (this.type.isAnnotationType()) { // annotation cannot override any method
140
problemReporter().annotationCannotOverrideMethod(currentMethod, methods[length - 1]);
141         return; // do not repoort against subsequent inherited methods
142
}
143     CompilerOptions options = type.scope.compilerOptions();
144     // need to find the overridden methods to avoid blaming this type for issues which are already reported against a supertype
145
// but cannot ignore an overridden inherited method completely when it comes to checking for bridge methods
146
int[] overriddenInheritedMethods = length > 1 ? findOverriddenInheritedMethods(methods, length) : null;
147     nextMethod : for (int i = length; --i >= 0;) {
148         MethodBinding inheritedMethod = methods[i];
149         if (overriddenInheritedMethods == null || overriddenInheritedMethods[i] == 0) {
150             if (currentMethod.isStatic() != inheritedMethod.isStatic()) { // Cannot override a static method or hide an instance method
151
problemReporter(currentMethod).staticAndInstanceConflict(currentMethod, inheritedMethod);
152                 continue nextMethod;
153             }
154
155             // want to tag currentMethod even if return types are not equal
156
if (inheritedMethod.isAbstract()) {
157                 if (inheritedMethod.declaringClass.isInterface()) {
158                     currentMethod.modifiers |= ExtraCompilerModifiers.AccImplementing;
159                 } else {
160                     currentMethod.modifiers |= ExtraCompilerModifiers.AccImplementing | ExtraCompilerModifiers.AccOverriding;
161                 }
162 // with the above change an abstract method is tagged as implementing the inherited abstract method
163
// if (!currentMethod.isAbstract() && inheritedMethod.isAbstract()) {
164
// if ((currentMethod.modifiers & CompilerModifiers.AccOverriding) == 0)
165
// currentMethod.modifiers |= CompilerModifiers.AccImplementing;
166
} else {
167                 currentMethod.modifiers |= ExtraCompilerModifiers.AccOverriding;
168             }
169     
170             if (!areReturnTypesCompatible(currentMethod, inheritedMethod))
171                 if (reportIncompatibleReturnTypeError(currentMethod, inheritedMethod))
172                     continue nextMethod;
173     
174             if (currentMethod.thrownExceptions != Binding.NO_EXCEPTIONS)
175                 checkExceptions(currentMethod, inheritedMethod);
176             if (inheritedMethod.isFinal())
177                 problemReporter(currentMethod).finalMethodCannotBeOverridden(currentMethod, inheritedMethod);
178             if (!isAsVisible(currentMethod, inheritedMethod))
179                 problemReporter(currentMethod).visibilityConflict(currentMethod, inheritedMethod);
180             if (options.reportDeprecationWhenOverridingDeprecatedMethod && inheritedMethod.isViewedAsDeprecated()) {
181                 if (!currentMethod.isViewedAsDeprecated() || options.reportDeprecationInsideDeprecatedCode) {
182                     // check against the other inherited methods to see if they hide this inheritedMethod
183
ReferenceBinding declaringClass = inheritedMethod.declaringClass;
184                     if (declaringClass.isInterface())
185                         for (int j = length; --j >= 0;)
186                             if (i != j && methods[j].declaringClass.implementsInterface(declaringClass, false))
187                                 continue nextMethod;
188     
189                     problemReporter(currentMethod).overridesDeprecatedMethod(currentMethod, inheritedMethod);
190                 }
191             }
192         }
193         checkForBridgeMethod(currentMethod, inheritedMethod, allInheritedMethods);
194     }
195 }
196 void checkConcreteInheritedMethod(MethodBinding concreteMethod, MethodBinding[] abstractMethods) {
197     // Remember that interfaces can only define public instance methods
198
if (concreteMethod.isStatic())
199         // Cannot inherit a static method which is specified as an instance method by an interface
200
problemReporter().staticInheritedMethodConflicts(type, concreteMethod, abstractMethods);
201     if (!concreteMethod.isPublic()) {
202         int index = 0, length = abstractMethods.length;
203         if (concreteMethod.isProtected()) {
204             for (; index < length; index++)
205                 if (abstractMethods[index].isPublic()) break;
206         } else if (concreteMethod.isDefault()) {
207             for (; index < length; index++)
208                 if (!abstractMethods[index].isDefault()) break;
209         }
210         if (index < length)
211             problemReporter().inheritedMethodReducesVisibility(type, concreteMethod, abstractMethods);
212     }
213     if (concreteMethod.thrownExceptions != Binding.NO_EXCEPTIONS)
214         for (int i = abstractMethods.length; --i >= 0;)
215             checkExceptions(concreteMethod, abstractMethods[i]);
216 }
217 /*
218 "8.4.4"
219 Verify that newExceptions are all included in inheritedExceptions.
220 Assumes all exceptions are valid and throwable.
221 Unchecked exceptions (compatible with runtime & error) are ignored (see the spec on pg. 203).
222 */

223 void checkExceptions(MethodBinding newMethod, MethodBinding inheritedMethod) {
224     ReferenceBinding[] newExceptions = resolvedExceptionTypesFor(newMethod);
225     ReferenceBinding[] inheritedExceptions = resolvedExceptionTypesFor(inheritedMethod);
226     for (int i = newExceptions.length; --i >= 0;) {
227         ReferenceBinding newException = newExceptions[i];
228         int j = inheritedExceptions.length;
229         while (--j > -1 && !isSameClassOrSubclassOf(newException, inheritedExceptions[j])){/*empty*/}
230         if (j == -1)
231             if (!newException.isUncheckedException(false))
232                 problemReporter(newMethod).incompatibleExceptionInThrowsClause(this.type, newMethod, inheritedMethod, newException);
233     }
234 }
235 void checkForBridgeMethod(MethodBinding currentMethod, MethodBinding inheritedMethod, MethodBinding[] allInheritedMethods) {
236     // no op before 1.5
237
}
238 void checkInheritedMethods(MethodBinding[] methods, int length) {
239     if (length > 1) {
240         int[] overriddenInheritedMethods = findOverriddenInheritedMethods(methods, length);
241         if (overriddenInheritedMethods != null) {
242             // detected some overridden methods that can be ignored when checking return types
243
// but cannot ignore an overridden inherited method completely when it comes to checking for bridge methods
244
int index = 0;
245             MethodBinding[] closestMethods = new MethodBinding[length];
246             for (int i = 0; i < length; i++)
247                 if (overriddenInheritedMethods[i] == 0)
248                     closestMethods[index++] = methods[i];
249             if (index > 1 && !checkInheritedReturnTypes(closestMethods, index))
250                 return;
251         } else if (!checkInheritedReturnTypes(methods, length)) {
252             return;
253         }
254     }
255
256     MethodBinding concreteMethod = null;
257     if (!type.isInterface()) { // ignore concrete methods for interfaces
258
for (int i = length; --i >= 0;) { // Remember that only one of the methods can be non-abstract
259
if (!methods[i].isAbstract()) {
260                 concreteMethod = methods[i];
261                 break;
262             }
263         }
264     }
265     if (concreteMethod == null) {
266         if (!this.type.isAbstract()) {
267             for (int i = length; --i >= 0;) {
268                 if (mustImplementAbstractMethod(methods[i].declaringClass)) {
269                     TypeDeclaration typeDeclaration = this.type.scope.referenceContext;
270                     if (typeDeclaration != null) {
271                         MethodDeclaration missingAbstractMethod = typeDeclaration.addMissingAbstractMethodFor(methods[0]);
272                         missingAbstractMethod.scope.problemReporter().abstractMethodMustBeImplemented(this.type, methods[0]);
273                     } else {
274                         problemReporter().abstractMethodMustBeImplemented(this.type, methods[0]);
275                     }
276                     return;
277                 }
278             }
279         }
280         return;
281     }
282
283     if (length > 1) {
284         MethodBinding[] abstractMethods = new MethodBinding[length - 1];
285         int index = 0;
286         for (int i = length; --i >= 0;)
287             if (methods[i] != concreteMethod)
288                 abstractMethods[index++] = methods[i];
289         checkConcreteInheritedMethod(concreteMethod, abstractMethods);
290     }
291 }
292 boolean checkInheritedReturnTypes(MethodBinding[] methods, int length) {
293     MethodBinding first = methods[0];
294     int index = length;
295     while (--index > 0 && areReturnTypesCompatible(first, methods[index])){/*empty*/}
296     if (index == 0)
297         return true;
298
299     // All inherited methods do NOT have the same vmSignature
300
if (this.type.isInterface())
301         for (int i = length; --i >= 0;)
302             if (methods[i].declaringClass.id == TypeIds.T_JavaLangObject)
303                 return false; // do not complain since the super interface already got blamed
304
problemReporter().inheritedMethodsHaveIncompatibleReturnTypes(this.type, methods, length);
305     return false;
306 }
307 /*
308 For each inherited method identifier (message pattern - vm signature minus the return type)
309     if current method exists
310         if current's vm signature does not match an inherited signature then complain
311         else compare current's exceptions & visibility against each inherited method
312     else
313         if inherited methods = 1
314             if inherited is abstract && type is NOT an interface or abstract, complain
315         else
316             if vm signatures do not match complain
317             else
318                 find the concrete implementation amongst the abstract methods (can only be 1)
319                 if one exists then
320                     it must be a public instance method
321                     compare concrete's exceptions against each abstract method
322                 else
323                     complain about missing implementation only if type is NOT an interface or abstract
324 */

325 void checkMethods() {
326     boolean mustImplementAbstractMethods = mustImplementAbstractMethods();
327     boolean skipInheritedMethods = mustImplementAbstractMethods && canSkipInheritedMethods(); // have a single concrete superclass so only check overridden methods
328
char[][] methodSelectors = this.inheritedMethods.keyTable;
329     nextSelector : for (int s = methodSelectors.length; --s >= 0;) {
330         if (methodSelectors[s] == null) continue nextSelector;
331
332         MethodBinding[] current = (MethodBinding[]) this.currentMethods.get(methodSelectors[s]);
333         if (current == null && skipInheritedMethods)
334             continue nextSelector;
335
336         MethodBinding[] inherited = (MethodBinding[]) this.inheritedMethods.valueTable[s];
337         if (inherited.length == 1 && current == null) { // handle the common case
338
if (mustImplementAbstractMethods && inherited[0].isAbstract())
339                 checkAbstractMethod(inherited[0]);
340             continue nextSelector;
341         }
342
343         int index = -1;
344         MethodBinding[] matchingInherited = new MethodBinding[inherited.length];
345         if (current != null) {
346             for (int i = 0, length1 = current.length; i < length1; i++) {
347                 MethodBinding currentMethod = current[i];
348                 for (int j = 0, length2 = inherited.length; j < length2; j++) {
349                     MethodBinding inheritedMethod = computeSubstituteMethod(inherited[j], currentMethod);
350                     if (inheritedMethod != null) {
351                         if (doesMethodOverride(currentMethod, inheritedMethod)) {
352                             matchingInherited[++index] = inheritedMethod;
353                             inherited[j] = null; // do not want to find it again
354
}
355                     }
356                 }
357                 if (index >= 0) {
358                     checkAgainstInheritedMethods(currentMethod, matchingInherited, index + 1, inherited); // pass in the length of matching
359
while (index >= 0) matchingInherited[index--] = null; // clear the contents of the matching methods
360
}
361             }
362         }
363
364         for (int i = 0, length = inherited.length; i < length; i++) {
365             MethodBinding inheritedMethod = inherited[i];
366             if (inheritedMethod == null) continue;
367
368             matchingInherited[++index] = inheritedMethod;
369             for (int j = i + 1; j < length; j++) {
370                 MethodBinding otherInheritedMethod = inherited[j];
371                 if (canSkipInheritedMethods(inheritedMethod, otherInheritedMethod))
372                     continue;
373                 otherInheritedMethod = computeSubstituteMethod(otherInheritedMethod, inheritedMethod);
374                 if (otherInheritedMethod != null) {
375                     if (doesMethodOverride(inheritedMethod, otherInheritedMethod)) {
376                         matchingInherited[++index] = otherInheritedMethod;
377                         inherited[j] = null; // do not want to find it again
378
}
379                 }
380             }
381             if (index == -1) continue;
382             if (index > 0)
383                 checkInheritedMethods(matchingInherited, index + 1); // pass in the length of matching
384
else if (mustImplementAbstractMethods && matchingInherited[0].isAbstract())
385                 checkAbstractMethod(matchingInherited[0]);
386             while (index >= 0) matchingInherited[index--] = null; // clear the contents of the matching methods
387
}
388     }
389 }
390 void checkPackagePrivateAbstractMethod(MethodBinding abstractMethod) {
391     // check that the inherited abstract method (package private visibility) is implemented within the same package
392
PackageBinding necessaryPackage = abstractMethod.declaringClass.fPackage;
393     if (necessaryPackage == this.type.fPackage) return; // not a problem
394

395     ReferenceBinding superType = this.type.superclass();
396     char[] selector = abstractMethod.selector;
397     do {
398         if (!superType.isValidBinding()) return;
399         if (!superType.isAbstract()) return; // closer non abstract super type will be flagged instead
400

401         if (necessaryPackage == superType.fPackage) {
402             MethodBinding[] methods = superType.getMethods(selector);
403             nextMethod : for (int m = methods.length; --m >= 0;) {
404                 MethodBinding method = methods[m];
405                 if (method.isPrivate() || method.isConstructor() || method.isDefaultAbstract())
406                     continue nextMethod;
407                 if (areMethodsCompatible(method, abstractMethod))
408                     return; // found concrete implementation of abstract method in same package
409
}
410         }
411     } while ((superType = superType.superclass()) != abstractMethod.declaringClass);
412
413     // non visible abstract methods cannot be overridden so the type must be defined abstract
414
problemReporter().abstractMethodCannotBeOverridden(this.type, abstractMethod);
415 }
416 void computeInheritedMethods() {
417     ReferenceBinding superclass = this.type.isInterface()
418         ? this.type.scope.getJavaLangObject() // check interface methods against Object
419
: this.type.superclass(); // class or enum
420
computeInheritedMethods(superclass, type.superInterfaces());
421 }
422 /*
423 Binding creation is responsible for reporting:
424     - all modifier problems (duplicates & multiple visibility modifiers + incompatible combinations)
425         - plus invalid modifiers given the context... examples:
426             - interface methods can only be public
427             - abstract methods can only be defined by abstract classes
428     - collisions... 2 methods with identical vmSelectors
429     - multiple methods with the same message pattern but different return types
430     - ambiguous, invisible or missing return/argument/exception types
431     - check the type of any array is not void
432     - check that each exception type is Throwable or a subclass of it
433 */

434 void computeInheritedMethods(ReferenceBinding superclass, ReferenceBinding[] superInterfaces) {
435     // only want to remember inheritedMethods that can have an impact on the current type
436
// if an inheritedMethod has been 'replaced' by a supertype's method then skip it
437

438     this.inheritedMethods = new HashtableOfObject(51); // maps method selectors to an array of methods... must search to match paramaters & return type
439
ReferenceBinding[] interfacesToVisit = null;
440     int nextPosition = 0;
441     ReferenceBinding[] itsInterfaces = superInterfaces;
442     if (itsInterfaces != Binding.NO_SUPERINTERFACES) {
443         nextPosition = itsInterfaces.length;
444         interfacesToVisit = itsInterfaces;
445     }
446
447     ReferenceBinding superType = superclass;
448     HashtableOfObject nonVisibleDefaultMethods = new HashtableOfObject(3); // maps method selectors to an array of methods
449
boolean allSuperclassesAreAbstract = true;
450
451     while (superType != null && superType.isValidBinding()) {
452         if (allSuperclassesAreAbstract) {
453             if (superType.isAbstract()) {
454                 // only need to include superinterfaces if immediate superclasses are abstract
455
if ((itsInterfaces = superType.superInterfaces()) != Binding.NO_SUPERINTERFACES) {
456                     if (interfacesToVisit == null) {
457                         interfacesToVisit = itsInterfaces;
458                         nextPosition = interfacesToVisit.length;
459                     } else {
460                         int itsLength = itsInterfaces.length;
461                         if (nextPosition + itsLength >= interfacesToVisit.length)
462                             System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition);
463                         nextInterface : for (int a = 0; a < itsLength; a++) {
464                             ReferenceBinding next = itsInterfaces[a];
465                             for (int b = 0; b < nextPosition; b++)
466                                 if (next == interfacesToVisit[b]) continue nextInterface;
467                             interfacesToVisit[nextPosition++] = next;
468                         }
469                     }
470                 }
471             } else {
472                 allSuperclassesAreAbstract = false;
473             }
474         }
475
476         MethodBinding[] methods = superType.unResolvedMethods();
477         nextMethod : for (int m = methods.length; --m >= 0;) {
478             MethodBinding inheritedMethod = methods[m];
479             if (inheritedMethod.isPrivate() || inheritedMethod.isConstructor() || inheritedMethod.isDefaultAbstract())
480                 continue nextMethod;
481             MethodBinding[] existingMethods = (MethodBinding[]) this.inheritedMethods.get(inheritedMethod.selector);
482             if (existingMethods != null) {
483                 for (int i = 0, length = existingMethods.length; i < length; i++) {
484                     if (existingMethods[i].declaringClass != inheritedMethod.declaringClass && areMethodsCompatible(existingMethods[i], inheritedMethod)) {
485                         if (inheritedMethod.isDefault() && inheritedMethod.isAbstract())
486                             checkPackagePrivateAbstractMethod(inheritedMethod);
487                         continue nextMethod;
488                     }
489                 }
490             }
491             MethodBinding[] nonVisible = (MethodBinding[]) nonVisibleDefaultMethods.get(inheritedMethod.selector);
492             if (nonVisible != null)
493                 for (int i = 0, l = nonVisible.length; i < l; i++)
494                     if (areMethodsCompatible(nonVisible[i], inheritedMethod))
495                         continue nextMethod;
496
497             if (!inheritedMethod.isDefault() || inheritedMethod.declaringClass.fPackage == type.fPackage) {
498                 if (existingMethods == null) {
499                     existingMethods = new MethodBinding[] {inheritedMethod};
500                 } else {
501                     int length = existingMethods.length;
502                     System.arraycopy(existingMethods, 0, existingMethods = new MethodBinding[length + 1], 0, length);
503                     existingMethods[length] = inheritedMethod;
504                 }
505                 this.inheritedMethods.put(inheritedMethod.selector, existingMethods);
506             } else {
507                 if (nonVisible == null) {
508                     nonVisible = new MethodBinding[] {inheritedMethod};
509                 } else {
510                     int length = nonVisible.length;
511                     System.arraycopy(nonVisible, 0, nonVisible = new MethodBinding[length + 1], 0, length);
512                     nonVisible[length] = inheritedMethod;
513                 }
514                 nonVisibleDefaultMethods.put(inheritedMethod.selector, nonVisible);
515
516                 if (inheritedMethod.isAbstract() && !this.type.isAbstract()) // non visible abstract methods cannot be overridden so the type must be defined abstract
517
problemReporter().abstractMethodCannotBeOverridden(this.type, inheritedMethod);
518
519                 MethodBinding[] current = (MethodBinding[]) this.currentMethods.get(inheritedMethod.selector);
520                 if (current != null) { // non visible methods cannot be overridden so a warning is issued
521
foundMatch : for (int i = 0, length = current.length; i < length; i++) {
522                         if (areMethodsCompatible(current[i], inheritedMethod)) {
523                             problemReporter().overridesPackageDefaultMethod(current[i], inheritedMethod);
524                             break foundMatch;
525                         }
526                     }
527                 }
528             }
529         }
530         superType = superType.superclass();
531     }
532     if (nextPosition == 0) return;
533
534     SimpleSet skip = findSuperinterfaceCollisions(superclass, superInterfaces);
535     for (int i = 0; i < nextPosition; i++) {
536         superType = interfacesToVisit[i];
537         if (superType.isValidBinding()) {
538             if (skip != null && skip.includes(superType)) continue;
539             if ((itsInterfaces = superType.superInterfaces()) != Binding.NO_SUPERINTERFACES) {
540                 int itsLength = itsInterfaces.length;
541                 if (nextPosition + itsLength >= interfacesToVisit.length)
542                     System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition);
543                 nextInterface : for (int a = 0; a < itsLength; a++) {
544                     ReferenceBinding next = itsInterfaces[a];
545                     for (int b = 0; b < nextPosition; b++)
546                         if (next == interfacesToVisit[b]) continue nextInterface;
547                     interfacesToVisit[nextPosition++] = next;
548                 }
549             }
550
551             MethodBinding[] methods = superType.unResolvedMethods();
552             nextMethod : for (int m = methods.length; --m >= 0;) { // Interface methods are all abstract public
553
MethodBinding inheritedMethod = methods[m];
554                 MethodBinding[] existingMethods = (MethodBinding[]) this.inheritedMethods.get(inheritedMethod.selector);
555                 if (existingMethods == null) {
556                     existingMethods = new MethodBinding[] {inheritedMethod};
557                 } else {
558                     int length = existingMethods.length;
559                     // look to see if any of the existingMethods implement this inheritedMethod
560
for (int e = 0; e < length; e++)
561                         if (isInterfaceMethodImplemented(inheritedMethod, existingMethods[e], superType))
562                             continue nextMethod; // skip interface method with the same signature if visible to its declaringClass
563
System.arraycopy(existingMethods, 0, existingMethods = new MethodBinding[length + 1], 0, length);
564                     existingMethods[length] = inheritedMethod;
565                 }
566                 this.inheritedMethods.put(inheritedMethod.selector, existingMethods);
567             }
568         }
569     }
570 }
571 void computeMethods() {
572     MethodBinding[] methods = type.methods();
573     int size = methods.length;
574     this.currentMethods = new HashtableOfObject(size == 0 ? 1 : size); // maps method selectors to an array of methods... must search to match paramaters & return type
575
for (int m = size; --m >= 0;) {
576         MethodBinding method = methods[m];
577         if (!(method.isConstructor() || method.isDefaultAbstract())) { // keep all methods which are NOT constructors or default abstract
578
MethodBinding[] existingMethods = (MethodBinding[]) this.currentMethods.get(method.selector);
579             if (existingMethods == null)
580                 existingMethods = new MethodBinding[1];
581             else
582                 System.arraycopy(existingMethods, 0,
583                     (existingMethods = new MethodBinding[existingMethods.length + 1]), 0, existingMethods.length - 1);
584             existingMethods[existingMethods.length - 1] = method;
585             this.currentMethods.put(method.selector, existingMethods);
586         }
587     }
588 }
589 MethodBinding computeSubstituteMethod(MethodBinding inheritedMethod, MethodBinding currentMethod) {
590     if (inheritedMethod == null) return null;
591     if (currentMethod.parameters.length != inheritedMethod.parameters.length) return null; // no match
592
return inheritedMethod;
593 }
594 public boolean doesMethodOverride(MethodBinding method, MethodBinding inheritedMethod) {
595     return areParametersEqual(method, inheritedMethod);
596 }
597 SimpleSet findSuperinterfaceCollisions(ReferenceBinding superclass, ReferenceBinding[] superInterfaces) {
598     return null; // noop in 1.4
599
}
600 int[] findOverriddenInheritedMethods(MethodBinding[] methods, int length) {
601     // NOTE assumes length > 1
602
// inherited methods are added as we walk up the superclass hierarchy, then each superinterface
603
// so method[1] from a class can NOT override method[0], but methods from superinterfaces can
604
// since superinterfaces can be added from different superclasses or other superinterfaces
605
int[] toSkip = null;
606     int i = 0;
607     ReferenceBinding declaringClass = methods[i].declaringClass;
608     if (!declaringClass.isInterface()) {
609         // in the first pass, skip overridden methods from superclasses
610
// only keep methods from the closest superclass, all others from higher superclasses can be skipped
611
// NOTE: methods were added in order by walking up the superclass hierarchy
612
ReferenceBinding declaringClass2 = methods[++i].declaringClass;
613         while (declaringClass == declaringClass2) {
614             if (++i == length) return null;
615             declaringClass2 = methods[i].declaringClass;
616         }
617         if (!declaringClass2.isInterface()) {
618             // skip all methods from different superclasses
619
toSkip = new int[length];
620             do {
621                 toSkip[i] = -1;
622                 if (++i == length) return toSkip;
623                 declaringClass2 = methods[i].declaringClass;
624             } while (!declaringClass2.isInterface());
625         }
626     }
627     // in the second pass, skip overridden methods from superinterfaces
628
// NOTE: superinterfaces can appear in 'random' order
629
nextMethod : for (; i < length; i++) {
630         if (toSkip != null && toSkip[i] == -1) continue nextMethod;
631         declaringClass = methods[i].declaringClass;
632         for (int j = i + 1; j < length; j++) {
633             if (toSkip != null && toSkip[j] == -1) continue;
634             ReferenceBinding declaringClass2 = methods[j].declaringClass;
635             if (declaringClass == declaringClass2) continue;
636             if (declaringClass.implementsInterface(declaringClass2, true)) {
637                 if (toSkip == null)
638                     toSkip = new int[length];
639                 toSkip[j] = -1;
640             } else if (declaringClass2.implementsInterface(declaringClass, true)) {
641                 if (toSkip == null)
642                     toSkip = new int[length];
643                 toSkip[i] = -1;
644                 continue nextMethod;
645             }
646         }
647     }
648     return toSkip;
649 }
650 boolean isAsVisible(MethodBinding newMethod, MethodBinding inheritedMethod) {
651     if (inheritedMethod.modifiers == newMethod.modifiers) return true;
652
653     if (newMethod.isPublic()) return true; // Covers everything
654
if (inheritedMethod.isPublic()) return false;
655
656     if (newMethod.isProtected()) return true;
657     if (inheritedMethod.isProtected()) return false;
658
659     return !newMethod.isPrivate(); // The inheritedMethod cannot be private since it would not be visible
660
}
661 boolean isInterfaceMethodImplemented(MethodBinding inheritedMethod, MethodBinding existingMethod, ReferenceBinding superType) {
662     // skip interface method with the same signature if visible to its declaringClass
663
return areParametersEqual(existingMethod, inheritedMethod) && existingMethod.declaringClass.implementsInterface(superType, true);
664 }
665 boolean isSameClassOrSubclassOf(ReferenceBinding testClass, ReferenceBinding superclass) {
666     do {
667         if (testClass == superclass) return true;
668     } while ((testClass = testClass.superclass()) != null);
669     return false;
670 }
671 boolean mustImplementAbstractMethod(ReferenceBinding declaringClass) {
672     // if the type's superclass is an abstract class, then all abstract methods must be implemented
673
// otherwise, skip it if the type's superclass must implement any of the inherited methods
674
ReferenceBinding superclass = this.type.superclass();
675     if (declaringClass.isClass()) {
676         while (superclass.isAbstract() && superclass != declaringClass)
677             superclass = superclass.superclass(); // find the first concrete superclass or the abstract declaringClass
678
} else {
679         if (this.type.implementsInterface(declaringClass, false)) {
680             if (this.type.isAbstract()) return false; // leave it for the subclasses
681
if (!superclass.implementsInterface(declaringClass, true)) // only if a superclass does not also implement the interface
682
return true;
683         }
684         while (superclass.isAbstract() && !superclass.implementsInterface(declaringClass, false))
685             superclass = superclass.superclass(); // find the first concrete superclass or the superclass which implements the interface
686
}
687     return superclass.isAbstract(); // if it is a concrete class then we have already reported problem against it
688
}
689 boolean mustImplementAbstractMethods() {
690     return !this.type.isInterface() && !this.type.isAbstract();
691 }
692 ProblemReporter problemReporter() {
693     return this.type.scope.problemReporter();
694 }
695 ProblemReporter problemReporter(MethodBinding currentMethod) {
696     ProblemReporter reporter = problemReporter();
697     if (currentMethod.declaringClass == type && currentMethod.sourceMethod() != null) // only report against the currentMethod if its implemented by the type
698
reporter.referenceContext = currentMethod.sourceMethod();
699     return reporter;
700 }
701 /**
702  * Return true and report an incompatibleReturnType error if currentMethod's
703  * return type is strictly incompatible with inheritedMethod's, else return
704  * false and report an unchecked conversion warning. Do not call when
705  * areReturnTypesCompatible(currentMethod, inheritedMethod) returns true.
706  * @param currentMethod the (potentially) inheriting method
707  * @param inheritedMethod the inherited method
708  * @return true if currentMethod's return type is strictly incompatible with
709  * inheritedMethod's
710  */

711 boolean reportIncompatibleReturnTypeError(MethodBinding currentMethod, MethodBinding inheritedMethod) {
712     problemReporter(currentMethod).incompatibleReturnType(currentMethod, inheritedMethod);
713     return true;
714 }
715 ReferenceBinding[] resolvedExceptionTypesFor(MethodBinding method) {
716     ReferenceBinding[] exceptions = method.thrownExceptions;
717     if ((method.modifiers & ExtraCompilerModifiers.AccUnresolved) == 0)
718         return exceptions;
719
720     if (!(method.declaringClass instanceof BinaryTypeBinding))
721         return Binding.NO_EXCEPTIONS; // safety check
722

723     for (int i = exceptions.length; --i >= 0;)
724         exceptions[i] = BinaryTypeBinding.resolveType(exceptions[i], this.environment, true);
725     return exceptions;
726 }
727 void verify(SourceTypeBinding someType) {
728     this.type = someType;
729     computeMethods();
730     computeInheritedMethods();
731     checkMethods();
732 }
733 public String JavaDoc toString() {
734     StringBuffer JavaDoc buffer = new StringBuffer JavaDoc(10);
735     buffer.append("MethodVerifier for type: "); //$NON-NLS-1$
736
buffer.append(type.readableName());
737     buffer.append('\n');
738     buffer.append("\t-inherited methods: "); //$NON-NLS-1$
739
buffer.append(this.inheritedMethods);
740     return buffer.toString();
741 }
742 }
743
Popular Tags