cec3089368375e09f9a1f2b09da00842335433a1
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / lookup / MethodVerifier15.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 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 org.eclipse.jdt.internal.compiler.lookup;
12
13 import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
14
15 class MethodVerifier15 extends MethodVerifier {
16
17 MethodVerifier15(LookupEnvironment environment) {
18         super(environment);
19 }
20 boolean areMethodsEqual(MethodBinding one, MethodBinding substituteTwo) {
21         TypeBinding[] oneParams = one.parameters;
22         TypeBinding[] twoParams = substituteTwo.parameters;
23         boolean checkParameters = false;
24         if (oneParams != twoParams) {
25                 int length = oneParams.length;
26                 if (length != twoParams.length) return false; // no match
27
28                 for (int i = 0; i < length; i++) {
29                         if (oneParams[i] != twoParams[i]) {
30                                 checkParameters |= oneParams[i].leafComponentType().isParameterizedType();
31                                 if (!areTypesEqual(oneParams[i], twoParams[i])) {
32                                         while (!checkParameters && ++i < length)
33                                                 checkParameters |= oneParams[i].leafComponentType().isParameterizedType();
34                                         if (one.areParameterErasuresEqual(substituteTwo)) // at least one parameter may cause a name clash
35                                                 detectNameClash(one, substituteTwo, checkParameters);
36                                         return false; // no match but needed to check for a name clash
37                                 }
38                         }
39                 }
40         }
41         return !detectNameClash(one, substituteTwo, checkParameters);
42 }
43 boolean areReturnTypesEqual(MethodBinding one, MethodBinding substituteTwo) {
44         if (one.returnType == substituteTwo.returnType) return true;
45
46         // methods from classes are always before methods from interfaces
47         if (one.declaringClass.isClass() || one.declaringClass.implementsInterface(substituteTwo.declaringClass, true))
48                 return one.returnType.isCompatibleWith(substituteTwo.returnType);
49
50         if (substituteTwo.declaringClass.implementsInterface(one.declaringClass, true))
51                 return substituteTwo.returnType.isCompatibleWith(one.returnType);
52
53         // unrelated interfaces... one must be a subtype of the other
54         return one.returnType.isCompatibleWith(substituteTwo.returnType)
55                 || substituteTwo.returnType.isCompatibleWith(one.returnType);
56 }
57 boolean areTypesEqual(TypeBinding one, TypeBinding two) {
58         if (one == two) return true;
59
60         switch (one.kind()) {
61                 case Binding.PARAMETERIZED_TYPE :
62                 case Binding.RAW_TYPE :
63                         return one.isEquivalentTo(two);
64 //              case Binding.TYPE_PARAMETER : // won't work for variables from different classes - need substitution
65         }
66
67         // Can skip this since we resolved each method before comparing it, see computeSubstituteMethod()
68         //      if (one instanceof UnresolvedReferenceBinding)
69         //              return ((UnresolvedReferenceBinding) one).resolvedType == two;
70         //      if (two instanceof UnresolvedReferenceBinding)
71         //              return ((UnresolvedReferenceBinding) two).resolvedType == one;
72         return false; // all other type bindings are identical
73 }
74 boolean canSkipInheritedMethods() {
75         if (this.type.superclass() != null)
76                 if (this.type.superclass().isAbstract() || this.type.superclass().isParameterizedType())
77                         return false;
78         return this.type.superInterfaces() == NoSuperInterfaces;
79 }
80 boolean canSkipInheritedMethods(MethodBinding one, MethodBinding two) {
81         return two == null // already know one is not null
82                 || (one.declaringClass == two.declaringClass && !one.declaringClass.isParameterizedType());
83 }
84 void checkForBridgeMethod(MethodBinding currentMethod, MethodBinding inheritedMethod) {
85         MethodBinding originalInherited = inheritedMethod.original();
86         if (inheritedMethod != originalInherited) {
87                 MethodBinding[] toCheck = (MethodBinding[]) this.currentMethods.get(currentMethod.selector);
88                 if (toCheck.length > 1) {
89                         // must check to see if a bridge method will collide with another current method (see 77861)
90                         for (int i = 0, length = toCheck.length; i < length; i++) {
91                                 if (currentMethod != toCheck[i] && toCheck[i].areParameterErasuresEqual(originalInherited)) {
92                                         problemReporter(toCheck[i]).methodNameClash(toCheck[i], originalInherited); // bridge method will collide
93                                         return;
94                                 }
95                         }
96                 }
97         }
98
99         // so the parameters are equal and the return type is compatible b/w the currentMethod & the substituted inheritedMethod
100         // then when do you need a bridge method?
101         if (originalInherited.returnType != currentMethod.returnType) {
102                 switch (originalInherited.returnType.leafComponentType().kind()) {
103                         case Binding.PARAMETERIZED_TYPE :
104                                 if (!currentMethod.returnType.leafComponentType().isParameterizedType())
105                                         problemReporter(currentMethod).unsafeReturnTypeOverride(currentMethod, originalInherited, ((MethodDeclaration) currentMethod.sourceMethod()).returnType);
106                                 break;
107                         case Binding.TYPE_PARAMETER :
108                                 if (!currentMethod.returnType.leafComponentType().isTypeVariable())
109                                         problemReporter(currentMethod).unsafeReturnTypeOverride(currentMethod, originalInherited, ((MethodDeclaration) currentMethod.sourceMethod()).returnType);
110                                 break;
111                 }   
112         }
113         this.type.addSyntheticBridgeMethod(originalInherited, currentMethod);
114 }
115 void checkInheritedMethods(MethodBinding[] methods, int length) {
116         int count = length;
117         nextMethod : for (int i = 0, l = length - 1; i < l;) {
118                 MethodBinding method = methods[i++];
119                 for (int j = i; j <= l; j++) {
120                         if (method.declaringClass == methods[j].declaringClass && doesMethodOverride(method, methods[j])) {
121                                 // found an inherited ParameterizedType that defines duplicate methods
122                                 problemReporter().duplicateInheritedMethods(this.type, method, methods[j]);
123                                 count--;
124                                 methods[i - 1] = null;
125                                 continue nextMethod;
126                         }
127                 }
128         }
129         if (count < length) {
130                 if (count == 1) return; // no need to continue since only 1 inherited method is left
131                 MethodBinding[] newMethods = new MethodBinding[count];
132                 for (int i = length; --i >= 0;)
133                         if (methods[i] != null)
134                                 newMethods[--count] = methods[i];
135                 methods = newMethods;
136                 length = newMethods.length;
137         }
138
139         super.checkInheritedMethods(methods, length);
140 }
141 MethodBinding computeSubstituteMethod(MethodBinding inheritedMethod, MethodBinding currentMethod) {
142         if (inheritedMethod == null) return null;
143
144         // due to hierarchy & compatibility checks, we need to ensure these 2 methods are resolved
145         // should we push these tests to where they're needed? returnType.isCompatibleWith && parameter isEquivalentTo ?
146         if (currentMethod.declaringClass instanceof BinaryTypeBinding)
147                 ((BinaryTypeBinding) currentMethod.declaringClass).resolveTypesFor(currentMethod);
148         if (inheritedMethod.declaringClass instanceof BinaryTypeBinding)
149                 ((BinaryTypeBinding) inheritedMethod.declaringClass).resolveTypesFor(inheritedMethod);
150
151         TypeVariableBinding[] inheritedTypeVariables = inheritedMethod.typeVariables();
152         if (inheritedTypeVariables == NoTypeVariables) return inheritedMethod;
153         TypeVariableBinding[] typeVariables = currentMethod == null ? NoTypeVariables : currentMethod.typeVariables;
154
155         int inheritedLength = inheritedTypeVariables.length;
156         int length = typeVariables.length;
157         TypeBinding[] arguments = new TypeBinding[inheritedLength];
158         if (inheritedLength <= length) {
159                 System.arraycopy(typeVariables, 0, arguments, 0, inheritedLength);
160         } else {
161                 System.arraycopy(typeVariables, 0, arguments, 0, length);
162                 for (int i = length; i < inheritedLength; i++)
163                         arguments[i] = inheritedTypeVariables[i].erasure();
164         }
165         ParameterizedGenericMethodBinding substitute =
166                 new ParameterizedGenericMethodBinding(inheritedMethod, arguments, this.environment);
167         for (int i = 0; i < inheritedLength; i++)
168             if (!inheritedTypeVariables[i].boundCheck(substitute, arguments[i]))
169                 return inheritedMethod; // incompatible due to bound check
170    return substitute;
171 }
172 boolean detectNameClash(MethodBinding one, MethodBinding substituteTwo, boolean checkParameters) {
173         if (doTypeVariablesClash(one, substituteTwo) || (checkParameters && doParametersClash(one, substituteTwo))) {
174                 if (this.type == one.declaringClass)
175                         problemReporter(one).methodNameClash(one, substituteTwo);
176                 else
177                         problemReporter().inheritedMethodsHaveNameClash(this.type, one, substituteTwo);
178                 return true;
179         }
180         return false;
181 }
182 public boolean doesMethodOverride(MethodBinding method, MethodBinding inheritedMethod) {
183         return super.doesMethodOverride(method, computeSubstituteMethod(inheritedMethod, method));
184 }
185 boolean doParametersClash(MethodBinding one, MethodBinding substituteTwo) {
186         // must check each parameter pair to see if parameterized types are compatible
187         TypeBinding[] oneParams = one.parameters;
188         TypeBinding[] twoParams = substituteTwo.parameters;
189         for (int i = 0, l = oneParams.length; i < l; i++) {
190                 if (oneParams[i] == twoParams[i]) continue;
191                 if (!oneParams[i].leafComponentType().isParameterizedType()) continue;
192
193                 if (!twoParams[i].leafComponentType().isParameterizedType()
194                         || !oneParams[i].isEquivalentTo(twoParams[i])
195                         || !twoParams[i].isEquivalentTo(oneParams[i])) {
196                                 return true;
197                 }
198         }
199         return false;
200 }
201 boolean doTypeVariablesClash(MethodBinding one, MethodBinding substituteTwo) {
202         TypeBinding[] currentVars = one.typeVariables;
203         TypeBinding[] inheritedVars = substituteTwo.original().typeVariables;
204         return currentVars.length != inheritedVars.length
205                 && currentVars.length > 0 && inheritedVars.length > 0; // must match unless all are replaced
206 //              && currentVars.length > 0;
207 }
208 boolean hasBoundedParameters(ParameterizedTypeBinding parameterizedType) {
209         TypeBinding[] arguments = parameterizedType.arguments;
210         if (arguments == null) return false;
211
212         nextArg : for (int i = 0, l = arguments.length; i < l; i++) {
213                 if (arguments[i].isWildcard())
214                         if (((WildcardBinding) arguments[i]).kind == org.eclipse.jdt.internal.compiler.ast.Wildcard.UNBOUND)
215                                 continue nextArg;
216                 if (arguments[i].isTypeVariable())
217                         if (((TypeVariableBinding) arguments[i]).firstBound == null)
218                                 continue nextArg;
219                 return true;
220         }
221         return false;
222 }
223 boolean isInterfaceMethodImplemented(MethodBinding inheritedMethod, MethodBinding existingMethod, ReferenceBinding superType) {
224         inheritedMethod = computeSubstituteMethod(inheritedMethod, existingMethod);
225         return inheritedMethod.returnType == existingMethod.returnType
226                 && super.isInterfaceMethodImplemented(inheritedMethod, existingMethod, superType);
227 }
228 }