added -J option to preserve unmodified files in preexisting jarfile
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / lookup / ParameterizedGenericMethodBinding.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 java.util.HashMap;
14 import java.util.Map;
15 import org.eclipse.jdt.internal.compiler.ast.MessageSend;
16 import org.eclipse.jdt.internal.compiler.ast.Wildcard;
17
18 /**
19  * Binding denoting a generic method after type parameter substitutions got performed.
20  * On parameterized type bindings, all methods got substituted, regardless whether
21  * their signature did involve generics or not, so as to get the proper declaringClass for
22  * these methods.
23  */
24 public class ParameterizedGenericMethodBinding extends ParameterizedMethodBinding implements Substitution {
25
26     public TypeBinding[] typeArguments; 
27     private LookupEnvironment environment;
28     public boolean inferredReturnType;
29     public boolean wasInferred; // only set to true for instances resulting from method invocation inferrence
30     public boolean isRaw; // set to true for method behaving as raw for substitution purpose
31     public MethodBinding tiebreakMethod;
32     
33     /**
34      * Create method of parameterized type, substituting original parameters with type arguments.
35      */
36         public ParameterizedGenericMethodBinding(MethodBinding originalMethod, TypeBinding[] typeArguments, LookupEnvironment environment) {
37
38             this.environment = environment;
39                 this.modifiers = originalMethod.modifiers;
40                 this.selector = originalMethod.selector;
41                 this.declaringClass = originalMethod.declaringClass;
42             this.typeVariables = NoTypeVariables;
43             this.typeArguments = typeArguments;
44             this.isRaw = false;
45             this.originalMethod = originalMethod;
46             this.parameters = Scope.substitute(this, originalMethod.parameters);
47             this.thrownExceptions = Scope.substitute(this, originalMethod.thrownExceptions);
48             this.returnType = this.substitute(originalMethod.returnType);
49             this.wasInferred = true;// resulting from method invocation inferrence
50         }
51         
52         /**
53          * Create raw generic method for raw type (double substitution from type vars with raw type arguments, and erasure of method variables)
54          * Only invoked for non-static generic methods of raw type
55          */
56         public ParameterizedGenericMethodBinding(MethodBinding originalMethod, RawTypeBinding rawType, LookupEnvironment environment) {
57
58                 TypeVariableBinding[] originalVariables = originalMethod.typeVariables;
59                 int length = originalVariables.length;
60                 TypeBinding[] rawArguments = new TypeBinding[length];
61                 for (int i = 0; i < length; i++) {
62                         rawArguments[i] = originalVariables[i].erasure();
63                 }               
64             this.isRaw = true;
65             this.environment = environment;
66                 this.modifiers = originalMethod.modifiers;
67                 this.selector = originalMethod.selector;
68                 this.declaringClass = rawType == null ? originalMethod.declaringClass : rawType;
69             this.typeVariables = NoTypeVariables;
70             this.typeArguments = rawArguments;
71             this.originalMethod = originalMethod;
72                 boolean ignoreRawTypeSubstitution = rawType == null || originalMethod.isStatic();
73             this.parameters = Scope.substitute(this, ignoreRawTypeSubstitution
74                                                                                 ? originalMethod.parameters // no substitution if original was static
75                                                                                 : Scope.substitute(rawType, originalMethod.parameters));
76             this.thrownExceptions = Scope.substitute(this,      ignoreRawTypeSubstitution 
77                                                                                 ? originalMethod.thrownExceptions // no substitution if original was static
78                                                                                 : Scope.substitute(rawType, originalMethod.thrownExceptions));
79             this.returnType = this.substitute(ignoreRawTypeSubstitution 
80                                                                                 ? originalMethod.returnType // no substitution if original was static
81                                                                                 : rawType.substitute(originalMethod.returnType));
82             this.wasInferred = false; // not resulting from method invocation inferrence
83         }
84         
85         /**
86          * Perform inference of generic method type parameters and/or expected type
87          */     
88         public static MethodBinding computeCompatibleMethod(MethodBinding originalMethod, TypeBinding[] arguments, Scope scope, InvocationSite invocationSite) {
89                 
90                 ParameterizedGenericMethodBinding methodSubstitute;
91                 TypeVariableBinding[] typeVariables = originalMethod.typeVariables;
92                 TypeBinding[] substitutes = invocationSite.genericTypeArguments();
93                 
94                 if (substitutes != null) {
95                         if (substitutes.length != typeVariables.length) {
96                         // incompatible due to wrong arity
97                         return new ProblemMethodBinding(originalMethod, originalMethod.selector, substitutes, TypeParameterArityMismatch);
98                         }
99                         methodSubstitute = new ParameterizedGenericMethodBinding(originalMethod, substitutes, scope.environment());
100                 } else {
101                         // perform type inference based on argument types and expected type
102                         
103                         // collect substitutes by pattern matching parameters and arguments
104                         TypeBinding[] parameters = originalMethod.parameters;
105                         int varLength = typeVariables.length;
106                         HashMap collectedSubstitutes = new HashMap(varLength);
107                         for (int i = 0; i < varLength; i++)
108                                 collectedSubstitutes.put(typeVariables[i], new TypeBinding[1]);
109                         
110                         // collect argument type mapping, handling varargs
111                         if (originalMethod.isVarargs()) {
112                                 int paramLength = parameters.length;
113                                 int minArgLength = paramLength - 1;
114                                 int argLength = arguments.length;
115                                 // process mandatory arguments
116                                 for (int i = 0; i < minArgLength; i++)
117                                         parameters[i].collectSubstitutes(arguments[i], collectedSubstitutes);
118                                 // process optional arguments
119                                 if (minArgLength < argLength) {
120                                         TypeBinding varargType = parameters[minArgLength]; // last arg type - as is ?
121                                         if (paramLength != argLength // argument is passed as is ?
122                                                         ||  (arguments[minArgLength] != NullBinding
123                                                                         && (arguments[minArgLength].dimensions() != varargType.dimensions()))) { 
124                                                 varargType = ((ArrayBinding)varargType).elementsType(); // eliminate one array dimension
125                                         }
126                                         for (int i = minArgLength; i < argLength; i++)
127                                                 varargType.collectSubstitutes(arguments[i], collectedSubstitutes);
128                                 }
129                         } else {
130                                 int paramLength = parameters.length;
131                                 for (int i = 0; i < paramLength; i++)
132                                         parameters[i].collectSubstitutes(arguments[i], collectedSubstitutes);
133                         }
134                         boolean needReturnTypeInference = false;
135                         if (collectedSubstitutes.isEmpty()) {
136                                 // raw generic method inferred
137                                 methodSubstitute = new ParameterizedGenericMethodBinding(originalMethod, (RawTypeBinding)null, scope.environment());
138                         } else {
139                                 substitutes = new TypeBinding[varLength];
140                                 for (int i = 0; i < varLength; i++) {
141                                         TypeBinding[] variableSubstitutes = (TypeBinding[]) collectedSubstitutes.get(typeVariables[i]);
142                                         TypeBinding mostSpecificSubstitute = scope.lowerUpperBound(variableSubstitutes);
143                                         if (mostSpecificSubstitute == null)
144                                                 return null; // incompatible
145                                         if (mostSpecificSubstitute == VoidBinding) {
146                                                 needReturnTypeInference = true;
147                                             mostSpecificSubstitute = typeVariables[i];
148                                         }                               
149                                         substitutes[i] = mostSpecificSubstitute;
150                                 }
151                                 // apply inferred variable substitutions
152                                 methodSubstitute = new ParameterizedGenericMethodBinding(originalMethod, substitutes, scope.environment());
153                         }
154         
155                         if (needReturnTypeInference && invocationSite instanceof MessageSend) {
156                                 MessageSend message = (MessageSend) invocationSite;
157                                 TypeBinding expectedType = message.expectedType;
158                                 if (expectedType == null) {
159                                         // 15.12.2.8 - if no expected type, then assume Object
160                                         // actually it rather seems to handle the returned variable case by expecting its erasure instead
161                                         if (methodSubstitute.returnType.isTypeVariable()) {
162                                                 expectedType = methodSubstitute.returnType.erasure();
163                                         } else {
164                                                 expectedType =scope.getJavaLangObject(); 
165                                         }
166                                 }
167                                 methodSubstitute.inferFromExpectedType(expectedType, scope);
168                         }
169                 }
170                 // check bounds
171                 if (!methodSubstitute.isRaw) {
172                         for (int i = 0, length = typeVariables.length; i < length; i++) {
173                             TypeVariableBinding typeVariable = typeVariables[i];
174                             TypeBinding substitute = substitutes[i];
175                             if (!typeVariable.boundCheck(methodSubstitute, substitute))
176                                 // incompatible due to bound check
177                                 return new ProblemMethodBinding(methodSubstitute, originalMethod.selector, new TypeBinding[]{substitutes[i], typeVariables[i] }, ParameterBoundMismatch);
178                         }
179                 }
180
181                 return methodSubstitute;
182         }
183
184         /*
185          * parameterizedDeclaringUniqueKey dot selector originalMethodGenericSignature percent typeArguments
186          * p.X<U> { <T> void bar(T t, U u) { new X<String>().bar(this, "") } } --> Lp/X<Ljava/lang/String;>;.bar<T:Ljava/lang/Object;>(TT;TU;)V%<Lp/X;>
187          */
188         public char[] computeUniqueKey() {
189                 if (this.isRaw)
190                         return super.computeUniqueKey();
191                 StringBuffer buffer = new StringBuffer();
192                 buffer.append(super.computeUniqueKey());
193                 buffer.append('%');
194                 buffer.append('<');
195                 int length = this.typeArguments.length;
196                 for (int i = 0; i < length; i++) {
197                         TypeBinding typeArgument = this.typeArguments[i];
198                         buffer.append(typeArgument.computeUniqueKey());
199                 }
200                 buffer.append('>');
201                 int resultLength = buffer.length();
202                 char[] result = new char[resultLength];
203                 buffer.getChars(0, resultLength, result, 0);    
204                 return result;
205                 
206         }
207         
208         /**
209          * Returns true if some parameters got substituted.
210          * NOTE: generic method invocation delegates to its declaring method (could be a parameterized one)
211          */
212         public boolean hasSubstitutedParameters() {
213                 // generic parameterized method can represent either an invocation or a raw generic method
214                 if (this.wasInferred) 
215                         return this.originalMethod.hasSubstitutedParameters();
216                 return super.hasSubstitutedParameters();
217         }
218         /**
219          * Returns true if the return type got substituted.
220          * NOTE: generic method invocation delegates to its declaring method (could be a parameterized one)
221          */
222         public boolean hasSubstitutedReturnType() {
223                 if (this.wasInferred) 
224                         return this.originalMethod.hasSubstitutedReturnType();
225                 return super.hasSubstitutedReturnType();
226         }
227         
228         public void inferFromExpectedType(TypeBinding expectedType, Scope scope) {
229             if (this.returnType == expectedType) 
230                 return;
231             if ((this.returnType.tagBits & TagBits.HasTypeVariable) == 0) 
232                 return;
233             Map substitutes = new HashMap(1);
234             int length = this.typeArguments.length;
235             TypeVariableBinding[] originalVariables = this.original().typeVariables;
236             boolean hasUnboundParameters = false;
237             for (int i = 0; i < length; i++) {
238                 if (this.typeArguments[i] == originalVariables[i]) {
239                     hasUnboundParameters = true;
240                         substitutes.put(originalVariables[i], new TypeBinding[1]);
241                 } else {
242                         substitutes.put(originalVariables[i], new TypeBinding[] { this.typeArguments[i] });
243                 }
244             }
245             if (!hasUnboundParameters)
246                 return;
247             returnType.collectSubstitutes(expectedType, substitutes);
248             if (substitutes.isEmpty()) {
249                 // raw generic method inferred
250                 this.isRaw = true;
251                 for (int i = 0; i < length; i++) {
252                         this.typeArguments[i] = originalVariables[i].erasure();
253                 }
254             } else {
255                         for (int i = 0; i < length; i++) {
256                                 TypeBinding[] variableSubstitutes = (TypeBinding[]) substitutes.get(originalVariables[i]);
257                                 TypeBinding mostSpecificSubstitute = scope.lowerUpperBound(variableSubstitutes);
258                                 if (mostSpecificSubstitute == null) {
259                                     return; // TODO (philippe) should report no way to infer type
260                                 }
261                                 if (mostSpecificSubstitute == VoidBinding) {
262                                         // 15.12.2.8 - any remaining variable is assumed to be its erasure
263                                         mostSpecificSubstitute = originalVariables[i].erasure();
264                                 }                               
265                                 this.typeArguments[i] = mostSpecificSubstitute;
266                         }
267             }
268                 TypeBinding oldReturnType = this.returnType;
269                 this.returnType = this.substitute(this.returnType);
270                 this.inferredReturnType = this.returnType != oldReturnType;
271             this.parameters = Scope.substitute(this, this.parameters);
272             this.thrownExceptions = Scope.substitute(this, this.thrownExceptions);
273         }
274         
275     /**
276          * Returns a type, where original type was substituted using the receiver
277          * parameterized method.
278          */
279         public TypeBinding substitute(TypeBinding originalType) {
280             
281                 switch (originalType.kind()) {
282                         
283                         case Binding.TYPE_PARAMETER:
284                         TypeVariableBinding originalVariable = (TypeVariableBinding) originalType;
285                         TypeVariableBinding[] variables = this.originalMethod.typeVariables;
286                         int length = variables.length;
287                         // check this variable can be substituted given parameterized type
288                         if (originalVariable.rank < length && variables[originalVariable.rank] == originalVariable) {
289                                         return this.typeArguments[originalVariable.rank];
290                         }
291                         if (this.declaringClass instanceof Substitution) {
292                                 return ((Substitution)this.declaringClass).substitute(originalType);
293                         }
294                         break;
295                                
296                         case Binding.PARAMETERIZED_TYPE:
297                                 ParameterizedTypeBinding originalParameterizedType = (ParameterizedTypeBinding) originalType;
298                                 ReferenceBinding originalEnclosing = originalType.enclosingType();
299                                 ReferenceBinding substitutedEnclosing = originalEnclosing;
300                                 if (originalEnclosing != null) {
301                                         substitutedEnclosing = (ReferenceBinding) this.substitute(originalEnclosing);
302                                 }
303                                 if (this.isRaw) {
304                                         return this.environment.createRawType(originalParameterizedType.type, substitutedEnclosing);                                    
305                                 }
306                                 TypeBinding[] originalArguments = originalParameterizedType.arguments;
307                                 TypeBinding[] substitutedArguments = originalArguments;
308                                 if (originalArguments != null) {
309                                         substitutedArguments = Scope.substitute(this, originalArguments);
310                                 }
311                                 if (substitutedArguments != originalArguments || substitutedEnclosing != originalEnclosing) {
312                                         identicalVariables: { // if substituted with original variables, then answer the generic type itself
313                                                 if (substitutedEnclosing != originalEnclosing) break identicalVariables;
314                                                 TypeVariableBinding[] originalVariables = originalParameterizedType.type.typeVariables();
315                                                 length = originalVariables.length;
316                                                 for (int i = 0; i < length; i++) {
317                                                         if (substitutedArguments[i] != originalVariables[i]) break identicalVariables;
318                                                 }
319                                                 return originalParameterizedType.type;
320                                         }
321                                         return this.environment.createParameterizedType(
322                                                         originalParameterizedType.type, substitutedArguments, substitutedEnclosing);
323                                 }
324                                 break;                          
325                         
326                         case Binding.ARRAY_TYPE:
327                                 TypeBinding originalLeafComponentType = originalType.leafComponentType();
328                                 TypeBinding substitute = substitute(originalLeafComponentType); // substitute could itself be array type
329                                 if (substitute != originalLeafComponentType) {
330                                         return this.environment.createArrayType(substitute.leafComponentType(), substitute.dimensions() + originalType.dimensions());
331                                 }
332                                 break;
333                                 
334                         case Binding.WILDCARD_TYPE:
335                         WildcardBinding wildcard = (WildcardBinding) originalType;
336                         if (wildcard.kind != Wildcard.UNBOUND) {
337                                 TypeBinding originalBound = wildcard.bound;
338                                 TypeBinding substitutedBound = substitute(originalBound);
339                                 if (substitutedBound != originalBound) {
340                                         return this.environment.createWildcard(wildcard.genericType, wildcard.rank, substitutedBound, wildcard.kind);
341                                 }
342                         }
343                         break;
344         
345         
346                         case Binding.GENERIC_TYPE:
347                             // treat as if parameterized with its type variables
348                                 ReferenceBinding originalGenericType = (ReferenceBinding) originalType;
349                                 originalEnclosing = originalType.enclosingType();
350                                 substitutedEnclosing = originalEnclosing;
351                                 if (originalEnclosing != null) {
352                                         substitutedEnclosing = (ReferenceBinding) this.substitute(originalEnclosing);
353                                 }
354                                 if (this.isRaw) {
355                                         return this.environment.createRawType(originalGenericType, substitutedEnclosing);                                       
356                                 }                               
357                                 TypeVariableBinding[] originalVariables = originalGenericType.typeVariables();
358                                 length = originalVariables.length;
359                                 System.arraycopy(originalVariables, 0, originalArguments = new TypeBinding[length], 0, length);
360                                 substitutedArguments = Scope.substitute(this, originalArguments);
361                                 if (substitutedArguments != originalArguments || substitutedEnclosing != originalEnclosing) {
362                                         return this.environment.createParameterizedType(
363                                                         originalGenericType, substitutedArguments, substitutedEnclosing);
364                                 }
365                                 break;
366             }
367             return originalType;
368         }
369         /**
370          * Returns the method to use during tiebreak (usually the method itself).
371          * For generic method invocations, tiebreak needs to use generic method with erasure substitutes.
372          */
373         public MethodBinding tiebreakMethod() {
374                 if (this.tiebreakMethod == null) {
375                         this.tiebreakMethod = new ParameterizedGenericMethodBinding(this.originalMethod, (RawTypeBinding)null, this.environment);
376                 } 
377                 return this.tiebreakMethod;
378         }       
379 }