Fix nasty bug #706. See trac.
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / lookup / MethodVerifier.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.lookup;
12
13 import net.sourceforge.phpdt.core.compiler.CharOperation;
14 import net.sourceforge.phpdt.internal.compiler.ast.MethodDeclaration;
15 import net.sourceforge.phpdt.internal.compiler.ast.TypeDeclaration;
16 import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter;
17 import net.sourceforge.phpdt.internal.compiler.util.HashtableOfObject;
18
19 public final class MethodVerifier implements TagBits, TypeConstants {
20         SourceTypeBinding type;
21
22         HashtableOfObject inheritedMethods;
23
24         HashtableOfObject currentMethods;
25
26         ReferenceBinding runtimeException;
27
28         ReferenceBinding errorException;
29
30         LookupEnvironment environment;
31
32         /*
33          * Binding creation is responsible for reporting all problems with types: -
34          * all modifier problems (duplicates & multiple visibility modifiers +
35          * incompatible combinations - abstract/final) - plus invalid modifiers
36          * given the context (the verifier did not do this before) - qualified name
37          * collisions between a type and a package (types in default packages are
38          * excluded) - all type hierarchy problems: - cycles in the superclass or
39          * superinterface hierarchy - an ambiguous, invisible or missing superclass
40          * or superinterface - extending a final class - extending an interface
41          * instead of a class - implementing a class instead of an interface -
42          * implementing the same interface more than once (ie. duplicate interfaces) -
43          * with nested types: - shadowing an enclosing type's source name - defining
44          * a static class or interface inside a non-static nested class - defining
45          * an interface as a local type (local types can only be classes)
46          */
47         public MethodVerifier(LookupEnvironment environment) {
48                 this.type = null; // Initialized with the public method
49                                                         // verify(SourceTypeBinding)
50                 this.inheritedMethods = null;
51                 this.currentMethods = null;
52                 this.runtimeException = null;
53                 this.errorException = null;
54                 this.environment = environment;
55         }
56
57         private void checkAgainstInheritedMethods(MethodBinding currentMethod,
58                         MethodBinding[] methods, int length) {
59                 currentMethod.modifiers |= CompilerModifiers.AccOverriding;
60                 for (int i = length; --i >= 0;) {
61                         MethodBinding inheritedMethod = methods[i];
62                         if (!currentMethod.isAbstract() && inheritedMethod.isAbstract())
63                                 currentMethod.modifiers |= CompilerModifiers.AccImplementing;
64
65                         if (currentMethod.returnType != inheritedMethod.returnType) {
66                                 this.problemReporter(currentMethod).incompatibleReturnType(
67                                                 currentMethod, inheritedMethod);
68                         } else if (currentMethod.isStatic() != inheritedMethod.isStatic()) { // Cannot
69                                                                                                                                                                         // override
70                                                                                                                                                                         // a
71                                                                                                                                                                         // static
72                                                                                                                                                                         // method
73                                                                                                                                                                         // or
74                                                                                                                                                                         // hide
75                                                                                                                                                                         // an
76                                                                                                                                                                         // instance
77                                                                                                                                                                         // method
78                                 this.problemReporter(currentMethod).staticAndInstanceConflict(
79                                                 currentMethod, inheritedMethod);
80                         } else {
81                                 if (currentMethod.thrownExceptions != NoExceptions)
82                                         this.checkExceptions(currentMethod, inheritedMethod);
83                                 if (inheritedMethod.isFinal())
84                                         this.problemReporter(currentMethod)
85                                                         .finalMethodCannotBeOverridden(currentMethod,
86                                                                         inheritedMethod);
87                                 if (!this.isAsVisible(currentMethod, inheritedMethod))
88                                         this.problemReporter(currentMethod).visibilityConflict(
89                                                         currentMethod, inheritedMethod);
90                                 // if (inheritedMethod.isViewedAsDeprecated())
91                                 // if (!currentMethod.isViewedAsDeprecated() ||
92                                 // environment.options.reportDeprecationInsideDeprecatedCode)
93                                 // this.problemReporter(currentMethod).overridesDeprecatedMethod(currentMethod,
94                                 // inheritedMethod);
95                         }
96                 }
97         }
98
99         /*
100          * "8.4.4" Verify that newExceptions are all included in
101          * inheritedExceptions. Assumes all exceptions are valid and throwable.
102          * Unchecked exceptions (compatible with runtime & error) are ignored (see
103          * the spec on pg. 203).
104          */
105         private void checkExceptions(MethodBinding newMethod,
106                         MethodBinding inheritedMethod) {
107                 ReferenceBinding[] newExceptions = newMethod.thrownExceptions;
108                 ReferenceBinding[] inheritedExceptions = inheritedMethod.thrownExceptions;
109                 for (int i = newExceptions.length; --i >= 0;) {
110                         ReferenceBinding newException = newExceptions[i];
111                         int j = inheritedExceptions.length;
112                         while (--j > -1
113                                         && !this.isSameClassOrSubclassOf(newException,
114                                                         inheritedExceptions[j]))
115                                 ;
116                         if (j == -1)
117                                 if (!(newException.isCompatibleWith(this.runtimeException()) || newException
118                                                 .isCompatibleWith(this.errorException())))
119                                         this.problemReporter(newMethod)
120                                                         .incompatibleExceptionInThrowsClause(this.type,
121                                                                         newMethod, inheritedMethod, newException);
122                 }
123         }
124
125         private void checkInheritedMethods(MethodBinding[] methods, int length) {
126                 TypeBinding returnType = methods[0].returnType;
127                 int index = length;
128                 while (--index > 0 && returnType == methods[index].returnType)
129                         ;
130                 if (index > 0) { // All inherited methods do NOT have the same
131                                                         // vmSignature
132                         this.problemReporter().inheritedMethodsHaveIncompatibleReturnTypes(
133                                         this.type, methods, length);
134                         return;
135                 }
136
137                 MethodBinding concreteMethod = null;
138                 if (!type.isInterface()) { // ignore concrete methods for interfaces
139                         for (int i = length; --i >= 0;) { // Remember that only one of the
140                                                                                                 // methods can be non-abstract
141                                 if (!methods[i].isAbstract()) {
142                                         concreteMethod = methods[i];
143                                         break;
144                                 }
145                         }
146                 }
147                 if (concreteMethod == null) {
148                         if (this.type.isClass() && !this.type.isAbstract()) {
149                                 for (int i = length; --i >= 0;)
150                                         if (!mustImplementAbstractMethod(methods[i]))
151                                                 return; // have already reported problem against the
152                                                                 // concrete superclass
153
154                                 TypeDeclaration typeDeclaration = this.type.scope.referenceContext;
155                                 if (typeDeclaration != null) {
156                                         MethodDeclaration missingAbstractMethod = typeDeclaration
157                                                         .addMissingAbstractMethodFor(methods[0]);
158                                         missingAbstractMethod.scope.problemReporter()
159                                                         .abstractMethodMustBeImplemented(this.type,
160                                                                         methods[0]);
161                                 } else {
162                                         this.problemReporter().abstractMethodMustBeImplemented(
163                                                         this.type, methods[0]);
164                                 }
165                         }
166                         return;
167                 }
168
169                 MethodBinding[] abstractMethods = new MethodBinding[length - 1];
170                 index = 0;
171                 for (int i = length; --i >= 0;)
172                         if (methods[i] != concreteMethod)
173                                 abstractMethods[index++] = methods[i];
174
175                 // Remember that interfaces can only define public instance methods
176                 if (concreteMethod.isStatic())
177                         // Cannot inherit a static method which is specified as an instance
178                         // method by an interface
179                         this.problemReporter().staticInheritedMethodConflicts(type,
180                                         concreteMethod, abstractMethods);
181                 if (!concreteMethod.isPublic())
182                         // Cannot reduce visibility of a public method specified by an
183                         // interface
184                         this.problemReporter().inheritedMethodReducesVisibility(type,
185                                         concreteMethod, abstractMethods);
186                 if (concreteMethod.thrownExceptions != NoExceptions)
187                         for (int i = abstractMethods.length; --i >= 0;)
188                                 this.checkExceptions(concreteMethod, abstractMethods[i]);
189         }
190
191         /*
192          * For each inherited method identifier (message pattern - vm signature
193          * minus the return type) if current method exists if current's vm signature
194          * does not match an inherited signature then complain else compare
195          * current's exceptions & visibility against each inherited method else if
196          * inherited methods = 1 if inherited is abstract && type is NOT an
197          * interface or abstract, complain else if vm signatures do not match
198          * complain else find the concrete implementation amongst the abstract
199          * methods (can only be 1) if one exists then it must be a public instance
200          * method compare concrete's exceptions against each abstract method else
201          * complain about missing implementation only if type is NOT an interface or
202          * abstract
203          */
204         private void checkMethods() {
205                 boolean mustImplementAbstractMethods = this.type.isClass()
206                                 && !this.type.isAbstract();
207                 char[][] methodSelectors = this.inheritedMethods.keyTable;
208                 for (int s = methodSelectors.length; --s >= 0;) {
209                         if (methodSelectors[s] != null) {
210                                 MethodBinding[] current = (MethodBinding[]) this.currentMethods
211                                                 .get(methodSelectors[s]);
212                                 MethodBinding[] inherited = (MethodBinding[]) this.inheritedMethods.valueTable[s];
213
214                                 int index = -1;
215                                 MethodBinding[] matchingInherited = new MethodBinding[inherited.length];
216                                 if (current != null) {
217                                         for (int i = 0, length1 = current.length; i < length1; i++) {
218                                                 while (index >= 0)
219                                                         matchingInherited[index--] = null; // clear the
220                                                                                                                                 // previous
221                                                                                                                                 // contents of
222                                                                                                                                 // the matching
223                                                                                                                                 // methods
224                                                 MethodBinding currentMethod = current[i];
225                                                 for (int j = 0, length2 = inherited.length; j < length2; j++) {
226                                                         if (inherited[j] != null
227                                                                         && currentMethod
228                                                                                         .areParametersEqual(inherited[j])) {
229                                                                 matchingInherited[++index] = inherited[j];
230                                                                 inherited[j] = null; // do not want to find
231                                                                                                                 // it again
232                                                         }
233                                                 }
234                                                 if (index >= 0)
235                                                         this.checkAgainstInheritedMethods(currentMethod,
236                                                                         matchingInherited, index + 1); // pass in
237                                                                                                                                         // the
238                                                                                                                                         // length of
239                                                                                                                                         // matching
240                                         }
241                                 }
242                                 for (int i = 0, length = inherited.length; i < length; i++) {
243                                         while (index >= 0)
244                                                 matchingInherited[index--] = null; // clear the
245                                                                                                                         // previous contents
246                                                                                                                         // of the matching
247                                                                                                                         // methods
248                                         if (inherited[i] != null) {
249                                                 matchingInherited[++index] = inherited[i];
250                                                 for (int j = i + 1; j < length; j++) {
251                                                         if (inherited[j] != null
252                                                                         && inherited[i]
253                                                                                         .areParametersEqual(inherited[j])) {
254                                                                 matchingInherited[++index] = inherited[j];
255                                                                 inherited[j] = null; // do not want to find
256                                                                                                                 // it again
257                                                         }
258                                                 }
259                                         }
260                                         if (index > 0) {
261                                                 this
262                                                                 .checkInheritedMethods(matchingInherited,
263                                                                                 index + 1); // pass in the length of
264                                                                                                         // matching
265                                         } else if (mustImplementAbstractMethods && index == 0
266                                                         && matchingInherited[0].isAbstract()) {
267                                                 if (mustImplementAbstractMethod(matchingInherited[0])) {
268                                                         TypeDeclaration typeDeclaration = this.type.scope.referenceContext;
269                                                         if (typeDeclaration != null) {
270                                                                 MethodDeclaration missingAbstractMethod = typeDeclaration
271                                                                                 .addMissingAbstractMethodFor(matchingInherited[0]);
272                                                                 missingAbstractMethod.scope
273                                                                                 .problemReporter()
274                                                                                 .abstractMethodMustBeImplemented(
275                                                                                                 this.type, matchingInherited[0]);
276                                                         } else {
277                                                                 this
278                                                                                 .problemReporter()
279                                                                                 .abstractMethodMustBeImplemented(
280                                                                                                 this.type, matchingInherited[0]);
281                                                         }
282                                                 }
283                                         }
284                                 }
285                         }
286                 }
287         }
288
289         private void checkPackagePrivateAbstractMethod(MethodBinding abstractMethod) {
290                 ReferenceBinding superType = this.type.superclass();
291                 char[] selector = abstractMethod.selector;
292                 do {
293                         if (!superType.isValidBinding())
294                                 return;
295                         if (!superType.isAbstract())
296                                 return; // closer non abstract super type will be flagged
297                                                 // instead
298
299                         MethodBinding[] methods = superType.getMethods(selector);
300                         nextMethod: for (int m = methods.length; --m >= 0;) {
301                                 MethodBinding method = methods[m];
302                                 if (method.returnType != abstractMethod.returnType
303                                                 || !method.areParametersEqual(abstractMethod))
304                                         continue nextMethod;
305                                 if (method.isPrivate() || method.isConstructor()
306                                                 || method.isDefaultAbstract())
307                                         continue nextMethod;
308                                 if (superType.fPackage == abstractMethod.declaringClass.fPackage)
309                                         return; // found concrete implementation of abstract method
310                                                         // in same package
311                         }
312                 } while ((superType = superType.superclass()) != abstractMethod.declaringClass);
313
314                 // non visible abstract methods cannot be overridden so the type must be
315                 // defined abstract
316                 this.problemReporter().abstractMethodCannotBeOverridden(this.type,
317                                 abstractMethod);
318         }
319
320         /*
321          * Binding creation is responsible for reporting: - all modifier problems
322          * (duplicates & multiple visibility modifiers + incompatible combinations) -
323          * plus invalid modifiers given the context... examples: - interface methods
324          * can only be public - abstract methods can only be defined by abstract
325          * classes - collisions... 2 methods with identical vmSelectors - multiple
326          * methods with the same message pattern but different return types -
327          * ambiguous, invisible or missing return/argument/exception types - check
328          * the type of any array is not void - check that each exception type is
329          * Throwable or a subclass of it
330          */
331         private void computeInheritedMethods() {
332                 this.inheritedMethods = new HashtableOfObject(51); // maps method
333                                                                                                                         // selectors to an
334                                                                                                                         // array of
335                                                                                                                         // methods... must
336                                                                                                                         // search to match
337                                                                                                                         // paramaters &
338                                                                                                                         // return type
339                 ReferenceBinding[][] interfacesToVisit = new ReferenceBinding[5][];
340                 int lastPosition = 0;
341                 interfacesToVisit[lastPosition] = type.superInterfaces();
342
343                 ReferenceBinding superType = this.type.isClass() ? this.type
344                                 .superclass() : this.type.scope.getJavaLangObject(); // check
345                                                                                                                                                 // interface
346                                                                                                                                                 // methods
347                                                                                                                                                 // against
348                                                                                                                                                 // Object
349                 MethodBinding[] nonVisibleDefaultMethods = null;
350                 int nonVisibleCount = 0;
351
352                 while (superType != null) {
353                         if (superType.isValidBinding()) {
354                                 ReferenceBinding[] itsInterfaces = superType.superInterfaces();
355                                 if (itsInterfaces != NoSuperInterfaces) {
356                                         if (++lastPosition == interfacesToVisit.length)
357                                                 System
358                                                                 .arraycopy(
359                                                                                 interfacesToVisit,
360                                                                                 0,
361                                                                                 interfacesToVisit = new ReferenceBinding[lastPosition * 2][],
362                                                                                 0, lastPosition);
363                                         interfacesToVisit[lastPosition] = itsInterfaces;
364                                 }
365
366                                 MethodBinding[] methods = superType.methods();
367                                 nextMethod: for (int m = methods.length; --m >= 0;) {
368                                         MethodBinding method = methods[m];
369                                         if (!(method.isPrivate() || method.isConstructor() || method
370                                                         .isDefaultAbstract())) { // look at all methods
371                                                                                                                 // which are NOT private
372                                                                                                                 // or constructors or
373                                                                                                                 // default abstract
374                                                 MethodBinding[] existingMethods = (MethodBinding[]) this.inheritedMethods
375                                                                 .get(method.selector);
376                                                 if (existingMethods != null) {
377                                                         for (int i = 0, length = existingMethods.length; i < length; i++) {
378                                                                 if (method.returnType == existingMethods[i].returnType
379                                                                                 && method
380                                                                                                 .areParametersEqual(existingMethods[i])) {
381                                                                         if (method.isDefault()
382                                                                                         && method.isAbstract()
383                                                                                         && method.declaringClass.fPackage != type.fPackage)
384                                                                                 checkPackagePrivateAbstractMethod(method);
385                                                                         continue nextMethod;
386                                                                 }
387                                                         }
388                                                 }
389                                                 if (nonVisibleDefaultMethods != null)
390                                                         for (int i = 0; i < nonVisibleCount; i++)
391                                                                 if (method.returnType == nonVisibleDefaultMethods[i].returnType
392                                                                                 && CharOperation
393                                                                                                 .equals(
394                                                                                                                 method.selector,
395                                                                                                                 nonVisibleDefaultMethods[i].selector)
396                                                                                 && method
397                                                                                                 .areParametersEqual(nonVisibleDefaultMethods[i]))
398                                                                         continue nextMethod;
399
400                                                 if (!(method.isDefault() && method.declaringClass.fPackage != type.fPackage)) { // ignore
401                                                                                                                                                                                                                 // methods
402                                                                                                                                                                                                                 // which
403                                                                                                                                                                                                                 // have
404                                                                                                                                                                                                                 // default
405                                                                                                                                                                                                                 // visibility
406                                                                                                                                                                                                                 // and
407                                                                                                                                                                                                                 // are
408                                                                                                                                                                                                                 // NOT
409                                                                                                                                                                                                                 // defined
410                                                                                                                                                                                                                 // in
411                                                                                                                                                                                                                 // another
412                                                                                                                                                                                                                 // package
413                                                         if (existingMethods == null)
414                                                                 existingMethods = new MethodBinding[1];
415                                                         else
416                                                                 System
417                                                                                 .arraycopy(
418                                                                                                 existingMethods,
419                                                                                                 0,
420                                                                                                 (existingMethods = new MethodBinding[existingMethods.length + 1]),
421                                                                                                 0, existingMethods.length - 1);
422                                                         existingMethods[existingMethods.length - 1] = method;
423                                                         this.inheritedMethods.put(method.selector,
424                                                                         existingMethods);
425                                                 } else {
426                                                         if (nonVisibleDefaultMethods == null)
427                                                                 nonVisibleDefaultMethods = new MethodBinding[10];
428                                                         else if (nonVisibleCount == nonVisibleDefaultMethods.length)
429                                                                 System
430                                                                                 .arraycopy(
431                                                                                                 nonVisibleDefaultMethods,
432                                                                                                 0,
433                                                                                                 (nonVisibleDefaultMethods = new MethodBinding[nonVisibleCount * 2]),
434                                                                                                 0, nonVisibleCount);
435                                                         nonVisibleDefaultMethods[nonVisibleCount++] = method;
436
437                                                         if (method.isAbstract() && !this.type.isAbstract()) // non
438                                                                                                                                                                 // visible
439                                                                                                                                                                 // abstract
440                                                                                                                                                                 // methods
441                                                                                                                                                                 // cannot
442                                                                                                                                                                 // be
443                                                                                                                                                                 // overridden
444                                                                                                                                                                 // so
445                                                                                                                                                                 // the
446                                                                                                                                                                 // type
447                                                                                                                                                                 // must
448                                                                                                                                                                 // be
449                                                                                                                                                                 // defined
450                                                                                                                                                                 // abstract
451                                                                 this.problemReporter()
452                                                                                 .abstractMethodCannotBeOverridden(
453                                                                                                 this.type, method);
454
455                                                         MethodBinding[] current = (MethodBinding[]) this.currentMethods
456                                                                         .get(method.selector);
457                                                         if (current != null) { // non visible methods
458                                                                                                         // cannot be overridden so a
459                                                                                                         // warning is issued
460                                                                 foundMatch: for (int i = 0, length = current.length; i < length; i++) {
461                                                                         if (method.returnType == current[i].returnType
462                                                                                         && method
463                                                                                                         .areParametersEqual(current[i])) {
464                                                                                 this.problemReporter()
465                                                                                                 .overridesPackageDefaultMethod(
466                                                                                                                 current[i], method);
467                                                                                 break foundMatch;
468                                                                         }
469                                                                 }
470                                                         }
471                                                 }
472                                         }
473                                 }
474                                 superType = superType.superclass();
475                         }
476                 }
477
478                 for (int i = 0; i <= lastPosition; i++) {
479                         ReferenceBinding[] interfaces = interfacesToVisit[i];
480                         if (interfaces == null) {
481                                 interfaces = new ReferenceBinding[0];
482                         }
483                         for (int j = 0, length = interfaces.length; j < length; j++) {
484                                 superType = interfaces[j];
485                                 if ((superType.tagBits & InterfaceVisited) == 0) {
486                                         superType.tagBits |= InterfaceVisited;
487                                         if (superType.isValidBinding()) {
488                                                 ReferenceBinding[] itsInterfaces = superType
489                                                                 .superInterfaces();
490                                                 if (itsInterfaces != NoSuperInterfaces) {
491                                                         if (++lastPosition == interfacesToVisit.length)
492                                                                 System
493                                                                                 .arraycopy(
494                                                                                                 interfacesToVisit,
495                                                                                                 0,
496                                                                                                 interfacesToVisit = new ReferenceBinding[lastPosition * 2][],
497                                                                                                 0, lastPosition);
498                                                         interfacesToVisit[lastPosition] = itsInterfaces;
499                                                 }
500
501                                                 MethodBinding[] methods = superType.methods();
502                                                 for (int m = methods.length; --m >= 0;) { // Interface
503                                                                                                                                         // methods
504                                                                                                                                         // are all
505                                                                                                                                         // abstract
506                                                                                                                                         // public
507                                                         MethodBinding method = methods[m];
508                                                         MethodBinding[] existingMethods = (MethodBinding[]) this.inheritedMethods
509                                                                         .get(method.selector);
510                                                         if (existingMethods == null)
511                                                                 existingMethods = new MethodBinding[1];
512                                                         else
513                                                                 System
514                                                                                 .arraycopy(
515                                                                                                 existingMethods,
516                                                                                                 0,
517                                                                                                 (existingMethods = new MethodBinding[existingMethods.length + 1]),
518                                                                                                 0, existingMethods.length - 1);
519                                                         existingMethods[existingMethods.length - 1] = method;
520                                                         this.inheritedMethods.put(method.selector,
521                                                                         existingMethods);
522                                                 }
523                                         }
524                                 }
525                         }
526                 }
527
528                 // bit reinitialization
529                 for (int i = 0; i <= lastPosition; i++) {
530                         ReferenceBinding[] interfaces = interfacesToVisit[i];
531                         if (interfaces == null) {
532                                 interfaces = new ReferenceBinding[0];
533                         }
534                         for (int j = 0, length = interfaces.length; j < length; j++)
535                                 interfaces[j].tagBits &= ~InterfaceVisited;
536                 }
537         }
538
539         private void computeMethods() {
540                 MethodBinding[] methods = type.methods();
541                 if (methods == null) {
542                         methods = new MethodBinding[0];
543                 }
544                 int size = methods.length;
545                 this.currentMethods = new HashtableOfObject(size == 0 ? 1 : size); // maps
546                                                                                                                                                         // method
547                                                                                                                                                         // selectors
548                                                                                                                                                         // to
549                                                                                                                                                         // an
550                                                                                                                                                         // array
551                                                                                                                                                         // of
552                                                                                                                                                         // methods...
553                                                                                                                                                         // must
554                                                                                                                                                         // search
555                                                                                                                                                         // to
556                                                                                                                                                         // match
557                                                                                                                                                         // paramaters
558                                                                                                                                                         // &
559                                                                                                                                                         // return
560                                                                                                                                                         // type
561                 for (int m = size; --m >= 0;) {
562                         MethodBinding method = methods[m];
563                         if (!(method.isConstructor() || method.isDefaultAbstract())) { // keep
564                                                                                                                                                         // all
565                                                                                                                                                         // methods
566                                                                                                                                                         // which
567                                                                                                                                                         // are
568                                                                                                                                                         // NOT
569                                                                                                                                                         // constructors
570                                                                                                                                                         // or
571                                                                                                                                                         // default
572                                                                                                                                                         // abstract
573                                 MethodBinding[] existingMethods = (MethodBinding[]) this.currentMethods
574                                                 .get(method.selector);
575                                 if (existingMethods == null)
576                                         existingMethods = new MethodBinding[1];
577                                 else
578                                         System
579                                                         .arraycopy(
580                                                                         existingMethods,
581                                                                         0,
582                                                                         (existingMethods = new MethodBinding[existingMethods.length + 1]),
583                                                                         0, existingMethods.length - 1);
584                                 existingMethods[existingMethods.length - 1] = method;
585                                 this.currentMethods.put(method.selector, existingMethods);
586                         }
587                 }
588         }
589
590         private ReferenceBinding errorException() {
591                 if (errorException == null)
592                         this.errorException = this.type.scope.getJavaLangError();
593                 return errorException;
594         }
595
596         private boolean isAsVisible(MethodBinding newMethod,
597                         MethodBinding inheritedMethod) {
598                 if (inheritedMethod.modifiers == newMethod.modifiers)
599                         return true;
600
601                 if (newMethod.isPublic())
602                         return true; // Covers everything
603                 if (inheritedMethod.isPublic())
604                         return false;
605
606                 if (newMethod.isProtected())
607                         return true;
608                 if (inheritedMethod.isProtected())
609                         return false;
610
611                 return !newMethod.isPrivate(); // The inheritedMethod cannot be private
612                                                                                 // since it would not be visible
613         }
614
615         private boolean isSameClassOrSubclassOf(ReferenceBinding testClass,
616                         ReferenceBinding superclass) {
617                 do {
618                         if (testClass == superclass)
619                                 return true;
620                 } while ((testClass = testClass.superclass()) != null);
621                 return false;
622         }
623
624         private boolean mustImplementAbstractMethod(MethodBinding abstractMethod) {
625                 // if the type's superclass is an abstract class, then all abstract
626                 // methods must be implemented
627                 // otherwise, skip it if the type's superclass must implement any of the
628                 // inherited methods
629                 ReferenceBinding superclass = this.type.superclass();
630                 ReferenceBinding declaringClass = abstractMethod.declaringClass;
631                 if (declaringClass.isClass()) {
632                         while (superclass.isAbstract() && superclass != declaringClass)
633                                 superclass = superclass.superclass(); // find the first
634                                                                                                                 // concrete superclass
635                                                                                                                 // or the abstract
636                                                                                                                 // declaringClass
637                 } else {
638                         if (this.type.implementsInterface(declaringClass, false)) {
639                                 if (this.type.isAbstract())
640                                         return false; // leave it for the subclasses
641                                 if (!superclass.implementsInterface(declaringClass, true)) // only
642                                                                                                                                                         // if a
643                                                                                                                                                         // superclass
644                                                                                                                                                         // does
645                                                                                                                                                         // not
646                                                                                                                                                         // also
647                                                                                                                                                         // implement
648                                                                                                                                                         // the
649                                                                                                                                                         // interface
650                                         return true;
651                         }
652                         while (superclass.isAbstract()
653                                         && !superclass.implementsInterface(declaringClass, false))
654                                 superclass = superclass.superclass(); // find the first
655                                                                                                                 // concrete superclass
656                                                                                                                 // or the superclass
657                                                                                                                 // which implements the
658                                                                                                                 // interface
659                 }
660                 return superclass.isAbstract(); // if it is a concrete class then we
661                                                                                 // have already reported problem against
662                                                                                 // it
663         }
664
665         private ProblemReporter problemReporter() {
666                 return this.type.scope.problemReporter();
667         }
668
669         private ProblemReporter problemReporter(MethodBinding currentMethod) {
670                 ProblemReporter reporter = problemReporter();
671                 if (currentMethod.declaringClass == type) // only report against the
672                                                                                                         // currentMethod if its
673                                                                                                         // implemented by the type
674                         reporter.referenceContext = currentMethod.sourceMethod();
675                 return reporter;
676         }
677
678         private ReferenceBinding runtimeException() {
679                 if (runtimeException == null)
680                         this.runtimeException = this.type.scope
681                                         .getJavaLangRuntimeException();
682                 return runtimeException;
683         }
684
685         public void verify(SourceTypeBinding type) {
686                 this.type = type;
687                 this.computeMethods();
688                 this.computeInheritedMethods();
689                 this.checkMethods();
690         }
691
692         public String toString() {
693                 StringBuffer buffer = new StringBuffer(10);
694                 buffer.append("MethodVerifier for type: "); //$NON-NLS-1$
695                 buffer.append(type.readableName());
696                 buffer.append('\n');
697                 buffer.append("\t-inherited methods: "); //$NON-NLS-1$
698                 buffer.append(this.inheritedMethods);
699                 return buffer.toString();
700         }
701 }