KickJava   Java API By Example, From Geeks To Geeks.

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


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

11 package org.eclipse.jdt.internal.compiler.lookup;
12
13 import org.eclipse.jdt.internal.compiler.ast.MessageSend;
14
15 /**
16  * Binding denoting a generic method after type parameter substitutions got performed.
17  * On parameterized type bindings, all methods got substituted, regardless whether
18  * their signature did involve generics or not, so as to get the proper declaringClass for
19  * these methods.
20  */

21 public class ParameterizedGenericMethodBinding extends ParameterizedMethodBinding implements Substitution {
22
23     public TypeBinding[] typeArguments;
24     private LookupEnvironment environment;
25     public boolean inferredReturnType;
26     public boolean wasInferred; // only set to true for instances resulting from method invocation inferrence
27
public boolean isRaw; // set to true for method behaving as raw for substitution purpose
28
private MethodBinding tiebreakMethod;
29     public boolean isUnchecked; // transient flag set during inference (warning: bindings are shared, so flag cannot be trusted beyond)
30

31     /**
32      * Perform inference of generic method type parameters and/or expected type
33      */

34     public static MethodBinding computeCompatibleMethod(MethodBinding originalMethod, TypeBinding[] arguments, Scope scope, InvocationSite invocationSite) {
35         
36         ParameterizedGenericMethodBinding methodSubstitute;
37         TypeVariableBinding[] typeVariables = originalMethod.typeVariables;
38         TypeBinding[] substitutes = invocationSite.genericTypeArguments();
39         
40         computeSubstitutes: {
41             if (substitutes != null) {
42                 // explicit type arguments got supplied
43
if (substitutes.length != typeVariables.length) {
44                     // incompatible due to wrong arity
45
return new ProblemMethodBinding(originalMethod, originalMethod.selector, substitutes, ProblemReasons.TypeParameterArityMismatch);
46                 }
47                 methodSubstitute = scope.environment().createParameterizedGenericMethod(originalMethod, substitutes);
48                 break computeSubstitutes;
49             }
50             
51             // perform type argument inference (15.12.2.7)
52

53             // initializes the map of substitutes (var --> type[][]{ equal, extends, super}
54
TypeBinding[] parameters = originalMethod.parameters;
55             InferenceContext inferenceContext = new InferenceContext(originalMethod);
56             methodSubstitute = inferFromArgumentTypes(scope, originalMethod, arguments, parameters, inferenceContext);
57             if (methodSubstitute == null)
58                 return null;
59             // substitutes may hold null to denote unresolved vars, but null arguments got replaced with respective original variable in param method
60

61             // 15.12.2.8 - inferring unresolved type arguments
62
if (inferenceContext.hasUnresolvedTypeArgument()) {
63                 if (methodSubstitute.returnType != TypeBinding.VOID) {
64                     TypeBinding expectedType = null;
65                     // if message invocation has expected type
66
if (invocationSite instanceof MessageSend) {
67                         MessageSend message = (MessageSend) invocationSite;
68                         expectedType = message.expectedType;
69                     }
70                     if (expectedType != null) {
71                         // record it was explicit from context, as opposed to assumed by default (see below)
72
inferenceContext.hasExplicitExpectedType = true;
73                     } else {
74                         expectedType = scope.getJavaLangObject(); // assume Object by default
75
}
76                     inferenceContext.expectedType = expectedType;
77                 }
78                 methodSubstitute = methodSubstitute.inferFromExpectedType(scope, inferenceContext);
79                 if (methodSubstitute == null)
80                     return null;
81             }
82         }
83
84         // bounds check
85
if (!methodSubstitute.isRaw) {
86             for (int i = 0, length = typeVariables.length; i < length; i++) {
87                 TypeVariableBinding typeVariable = typeVariables[i];
88                 TypeBinding substitute = methodSubstitute.typeArguments[i];
89                 switch (typeVariable.boundCheck(methodSubstitute, substitute)) {
90                     case TypeConstants.MISMATCH :
91                         // incompatible due to bound check
92
int argLength = arguments.length;
93                         TypeBinding[] augmentedArguments = new TypeBinding[argLength + 2]; // append offending substitute and typeVariable
94
System.arraycopy(arguments, 0, augmentedArguments, 0, argLength);
95                         augmentedArguments[argLength] = substitute;
96                         augmentedArguments[argLength+1] = typeVariable;
97                         return new ProblemMethodBinding(methodSubstitute, originalMethod.selector, augmentedArguments, ProblemReasons.ParameterBoundMismatch);
98                     case TypeConstants.UNCHECKED :
99                         // tolerate unchecked bounds
100
methodSubstitute.isUnchecked = true;
101                         break;
102                 }
103             }
104         }
105         return methodSubstitute;
106     }
107
108     /**
109      * Collect argument type mapping, handling varargs
110      */

111     private static ParameterizedGenericMethodBinding inferFromArgumentTypes(Scope scope, MethodBinding originalMethod, TypeBinding[] arguments, TypeBinding[] parameters, InferenceContext inferenceContext) {
112
113         if (originalMethod.isVarargs()) {
114             int paramLength = parameters.length;
115             int minArgLength = paramLength - 1;
116             int argLength = arguments.length;
117             // process mandatory arguments
118
for (int i = 0; i < minArgLength; i++) {
119                 parameters[i].collectSubstitutes(scope, arguments[i], inferenceContext, TypeConstants.CONSTRAINT_EXTENDS);
120                 if (inferenceContext.status == InferenceContext.FAILED) return null; // impossible substitution
121
}
122             // process optional arguments
123
if (minArgLength < argLength) {
124                 TypeBinding varargType = parameters[minArgLength]; // last arg type - as is ?
125
TypeBinding lastArgument = arguments[minArgLength];
126                 checkVarargDimension: {
127                     if (paramLength == argLength) {
128                         if (lastArgument == TypeBinding.NULL) break checkVarargDimension;
129                         switch (lastArgument.dimensions()) {
130                             case 0 :
131                                 break; // will remove one dim
132
case 1 :
133                                 if (!lastArgument.leafComponentType().isBaseType()) break checkVarargDimension;
134                                 break; // will remove one dim
135
default :
136                                 break checkVarargDimension;
137                         }
138                     }
139                     // eliminate one array dimension
140
varargType = ((ArrayBinding)varargType).elementsType();
141                 }
142                 for (int i = minArgLength; i < argLength; i++) {
143                     varargType.collectSubstitutes(scope, arguments[i], inferenceContext, TypeConstants.CONSTRAINT_EXTENDS);
144                     if (inferenceContext.status == InferenceContext.FAILED) return null; // impossible substitution
145
}
146             }
147         } else {
148             int paramLength = parameters.length;
149             for (int i = 0; i < paramLength; i++) {
150                 parameters[i].collectSubstitutes(scope, arguments[i], inferenceContext, TypeConstants.CONSTRAINT_EXTENDS);
151                 if (inferenceContext.status == InferenceContext.FAILED) return null; // impossible substitution
152
}
153         }
154         if (inferenceContext.status == InferenceContext.RAW_SUBSTITUTION) {
155             // raw generic method inferred
156
return scope.environment().createParameterizedGenericMethod(originalMethod, (RawTypeBinding)null);
157         }
158         TypeVariableBinding[] originalVariables = originalMethod.typeVariables;
159         if (!resolveSubstituteConstraints(scope, originalVariables , inferenceContext, false/*ignore Ti<:Uk*/))
160             return null; // impossible substitution
161

162         // apply inferred variable substitutions - replacing unresolved variable with original ones in param method
163
TypeBinding[] inferredSustitutes = inferenceContext.substitutes;
164         TypeBinding[] actualSubstitutes = inferredSustitutes;
165         for (int i = 0, varLength = originalVariables.length; i < varLength; i++) {
166             if (inferredSustitutes[i] == null) {
167                 if (actualSubstitutes == inferredSustitutes) {
168                     System.arraycopy(inferredSustitutes, 0, actualSubstitutes = new TypeBinding[varLength], 0, i); // clone to replace null with original variable in param method
169
}
170                 actualSubstitutes[i] = originalVariables[i];
171             } else if (actualSubstitutes != inferredSustitutes) {
172                 actualSubstitutes[i] = inferredSustitutes[i];
173             }
174         }
175         ParameterizedGenericMethodBinding paramMethod = scope.environment().createParameterizedGenericMethod(originalMethod, actualSubstitutes);
176         return paramMethod;
177     }
178     
179     private static boolean resolveSubstituteConstraints(Scope scope, TypeVariableBinding[] typeVariables, InferenceContext inferenceContext, boolean considerEXTENDSConstraints) {
180         TypeBinding[] substitutes = inferenceContext.substitutes;
181         int varLength = typeVariables.length;
182         // check Tj=U constraints
183
nextTypeParameter:
184             for (int i = 0; i < varLength; i++) {
185                 TypeVariableBinding current = typeVariables[i];
186                 TypeBinding substitute = substitutes[i];
187                 if (substitute != null) continue nextTypeParameter; // already inferred previously
188
TypeBinding [] equalSubstitutes = inferenceContext.getSubstitutes(current, TypeConstants.CONSTRAINT_EQUAL);
189                 if (equalSubstitutes != null) {
190                     nextConstraint:
191                         for (int j = 0, equalLength = equalSubstitutes.length; j < equalLength; j++) {
192                             TypeBinding equalSubstitute = equalSubstitutes[j];
193                             if (equalSubstitute == null) continue nextConstraint;
194                             if (equalSubstitute == current) {
195                                 // try to find a better different match if any in subsequent equal candidates
196
for (int k = j+1; k < equalLength; k++) {
197                                     equalSubstitute = equalSubstitutes[k];
198                                     if (equalSubstitute != current && equalSubstitute != null) {
199                                         substitutes[i] = equalSubstitute;
200                                         continue nextTypeParameter;
201                                     }
202                                 }
203                                 substitutes[i] = current;
204                                 continue nextTypeParameter;
205                             }
206 // if (equalSubstitute.isTypeVariable()) {
207
// TypeVariableBinding variable = (TypeVariableBinding) equalSubstitute;
208
// // substituted by a variable of the same method, ignore
209
// if (variable.rank < varLength && typeVariables[variable.rank] == variable) {
210
// // TODO (philippe) rewrite all other constraints to use current instead.
211
// continue nextConstraint;
212
// }
213
// }
214
substitutes[i] = equalSubstitute;
215                             continue nextTypeParameter; // pick first match, applicability check will rule out invalid scenario where others were present
216
}
217                 }
218             }
219         if (inferenceContext.hasUnresolvedTypeArgument()) {
220             // check Tj>:U constraints
221
nextTypeParameter:
222                 for (int i = 0; i < varLength; i++) {
223                     TypeVariableBinding current = typeVariables[i];
224                     TypeBinding substitute = substitutes[i];
225                     if (substitute != null) continue nextTypeParameter; // already inferred previously
226
TypeBinding [] bounds = inferenceContext.getSubstitutes(current, TypeConstants.CONSTRAINT_SUPER);
227                     if (bounds == null) continue nextTypeParameter;
228                     TypeBinding mostSpecificSubstitute = scope.lowerUpperBound(bounds);
229                     if (mostSpecificSubstitute == null) {
230                         return false; // incompatible
231
}
232                     if (mostSpecificSubstitute != TypeBinding.VOID) {
233                         substitutes[i] = mostSpecificSubstitute;
234                     }
235                 }
236         }
237         if (considerEXTENDSConstraints && inferenceContext.hasUnresolvedTypeArgument()) {
238             // check Tj<:U constraints
239
nextTypeParameter:
240                 for (int i = 0; i < varLength; i++) {
241                     TypeVariableBinding current = typeVariables[i];
242                     TypeBinding substitute = substitutes[i];
243                     if (substitute != null) continue nextTypeParameter; // already inferred previously
244
TypeBinding [] bounds = inferenceContext.getSubstitutes(current, TypeConstants.CONSTRAINT_EXTENDS);
245                     if (bounds == null) continue nextTypeParameter;
246                     TypeBinding[] glb = Scope.greaterLowerBound(bounds);
247                     TypeBinding mostSpecificSubstitute = null;
248                     if (glb != null) mostSpecificSubstitute = glb[0]; // TODO (philippe) need to improve
249
//TypeBinding mostSpecificSubstitute = scope.greaterLowerBound(bounds);
250
if (mostSpecificSubstitute != null) {
251                             substitutes[i] = mostSpecificSubstitute;
252                         }
253                     }
254         }
255         return true;
256     }
257     
258     /**
259      * Create raw generic method for raw type (double substitution from type vars with raw type arguments, and erasure of method variables)
260      * Only invoked for non-static generic methods of raw type
261      */

262     public ParameterizedGenericMethodBinding(MethodBinding originalMethod, RawTypeBinding rawType, LookupEnvironment environment) {
263
264         TypeVariableBinding[] originalVariables = originalMethod.typeVariables;
265         int length = originalVariables.length;
266         TypeBinding[] rawArguments = new TypeBinding[length];
267         for (int i = 0; i < length; i++) {
268             rawArguments[i] = environment.convertToRawType(originalVariables[i].erasure());
269         }
270         this.isRaw = true;
271         this.tagBits = originalMethod.tagBits;
272         this.environment = environment;
273         this.modifiers = originalMethod.modifiers;
274         this.selector = originalMethod.selector;
275         this.declaringClass = rawType == null ? originalMethod.declaringClass : rawType;
276         this.typeVariables = Binding.NO_TYPE_VARIABLES;
277         this.typeArguments = rawArguments;
278         this.originalMethod = originalMethod;
279         boolean ignoreRawTypeSubstitution = rawType == null || originalMethod.isStatic();
280         this.parameters = Scope.substitute(this, ignoreRawTypeSubstitution
281                                             ? originalMethod.parameters // no substitution if original was static
282
: Scope.substitute(rawType, originalMethod.parameters));
283         this.thrownExceptions = Scope.substitute(this, ignoreRawTypeSubstitution
284                                             ? originalMethod.thrownExceptions // no substitution if original was static
285
: Scope.substitute(rawType, originalMethod.thrownExceptions));
286         this.returnType = Scope.substitute(this, ignoreRawTypeSubstitution
287                                             ? originalMethod.returnType // no substitution if original was static
288
: Scope.substitute(rawType, originalMethod.returnType));
289         this.wasInferred = false; // not resulting from method invocation inferrence
290
}
291     
292     /**
293      * Create method of parameterized type, substituting original parameters with type arguments.
294      */

295     public ParameterizedGenericMethodBinding(MethodBinding originalMethod, TypeBinding[] typeArguments, LookupEnvironment environment) {
296
297         this.environment = environment;
298         this.modifiers = originalMethod.modifiers;
299         this.selector = originalMethod.selector;
300         this.declaringClass = originalMethod.declaringClass;
301         this.typeVariables = Binding.NO_TYPE_VARIABLES;
302         this.typeArguments = typeArguments;
303         this.isRaw = false;
304         this.tagBits = originalMethod.tagBits;
305         this.originalMethod = originalMethod;
306         this.parameters = Scope.substitute(this, originalMethod.parameters);
307         this.thrownExceptions = Scope.substitute(this, originalMethod.thrownExceptions);
308         this.returnType = Scope.substitute(this, originalMethod.returnType);
309         this.wasInferred = true;// resulting from method invocation inferrence
310
}
311
312     /*
313      * parameterizedDeclaringUniqueKey dot selector originalMethodGenericSignature percent typeArguments
314      * 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;Ljava/lang/String;)V%<Lp/X;>
315      */

316     public char[] computeUniqueKey(boolean isLeaf) {
317         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
318         buffer.append(this.originalMethod.computeUniqueKey(false/*not a leaf*/));
319         buffer.append('%');
320         buffer.append('<');
321         if (!this.isRaw) {
322             int length = this.typeArguments.length;
323             for (int i = 0; i < length; i++) {
324                 TypeBinding typeArgument = this.typeArguments[i];
325                 buffer.append(typeArgument.computeUniqueKey(false/*not a leaf*/));
326             }
327         }
328         buffer.append('>');
329         int resultLength = buffer.length();
330         char[] result = new char[resultLength];
331         buffer.getChars(0, resultLength, result, 0);
332         return result;
333         
334     }
335     
336     /**
337      * @see org.eclipse.jdt.internal.compiler.lookup.Substitution#environment()
338      */

339     public LookupEnvironment environment() {
340         return this.environment;
341     }
342     /**
343      * Returns true if some parameters got substituted.
344      * NOTE: generic method invocation delegates to its declaring method (could be a parameterized one)
345      */

346     public boolean hasSubstitutedParameters() {
347         // generic parameterized method can represent either an invocation or a raw generic method
348
if (this.wasInferred)
349             return this.originalMethod.hasSubstitutedParameters();
350         return super.hasSubstitutedParameters();
351     }
352     /**
353      * Returns true if the return type got substituted.
354      * NOTE: generic method invocation delegates to its declaring method (could be a parameterized one)
355      */

356     public boolean hasSubstitutedReturnType() {
357         if (this.inferredReturnType)
358             return this.originalMethod.hasSubstitutedReturnType();
359         return super.hasSubstitutedReturnType();
360     }
361     /**
362      * Given some type expectation, and type variable bounds, perform some inference.
363      * Returns true if still had unresolved type variable at the end of the operation
364      */

365     private ParameterizedGenericMethodBinding inferFromExpectedType(Scope scope, InferenceContext inferenceContext) {
366         TypeVariableBinding[] originalVariables = this.originalMethod.typeVariables; // immediate parent (could be a parameterized method)
367
int varLength = originalVariables.length;
368         
369         computeSubstitutes: {
370             // infer from expected return type
371
if (inferenceContext.expectedType != null) {
372                 this.returnType.collectSubstitutes(scope, inferenceContext.expectedType, inferenceContext, TypeConstants.CONSTRAINT_SUPER);
373                 if (inferenceContext.status == InferenceContext.FAILED) return null; // impossible substitution
374
}
375             // infer from bounds of type parameters
376
for (int i = 0; i < varLength; i++) {
377                 TypeVariableBinding originalVariable = originalVariables[i];
378                 TypeBinding argument = this.typeArguments[i];
379                 boolean argAlreadyInferred = argument != originalVariable;
380                 if (originalVariable.firstBound == originalVariable.superclass) {
381                     TypeBinding substitutedBound = Scope.substitute(this, originalVariable.superclass);
382                     argument.collectSubstitutes(scope, substitutedBound, inferenceContext, TypeConstants.CONSTRAINT_SUPER);
383                     if (inferenceContext.status == InferenceContext.FAILED) return null; // impossible substitution
384
// JLS 15.12.2.8 claims reverse inference shouldn't occur, however it improves inference
385
// e.g. given: <E extends Object, S extends Collection<E>> S test1(S param)
386
// invocation: test1(new Vector<String>()) will infer: S=Vector<String> and with code below: E=String
387
if (argAlreadyInferred) {
388                         substitutedBound.collectSubstitutes(scope, argument, inferenceContext, TypeConstants.CONSTRAINT_EXTENDS);
389                         if (inferenceContext.status == InferenceContext.FAILED) return null; // impossible substitution
390
}
391                 }
392                 for (int j = 0, max = originalVariable.superInterfaces.length; j < max; j++) {
393                     TypeBinding substitutedBound = Scope.substitute(this, originalVariable.superInterfaces[j]);
394                     argument.collectSubstitutes(scope, substitutedBound, inferenceContext, TypeConstants.CONSTRAINT_SUPER);
395                     if (inferenceContext.status == InferenceContext.FAILED) return null; // impossible substitution
396
// JLS 15.12.2.8 claims reverse inference shouldn't occur, however it improves inference
397
if (argAlreadyInferred) {
398                         substitutedBound.collectSubstitutes(scope, argument, inferenceContext, TypeConstants.CONSTRAINT_EXTENDS);
399                         if (inferenceContext.status == InferenceContext.FAILED) return null; // impossible substitution
400
}
401                 }
402             }
403             if (inferenceContext.status == InferenceContext.RAW_SUBSTITUTION) {
404                 // raw generic method inferred
405
this.isRaw = true;
406                 this.isUnchecked = false;
407                 for (int i = 0; i < varLength; i++) {
408                     this.typeArguments[i] = originalVariables[i].upperBound();
409                 }
410                 break computeSubstitutes;
411             }
412             if (!resolveSubstituteConstraints(scope, originalVariables, inferenceContext, true/*consider Ti<:Uk*/))
413                 return null; // incompatible
414
// this.typeArguments = substitutes; - no op since side effects got performed during #resolveSubstituteConstraints
415
for (int i = 0; i < varLength; i++) {
416                 TypeBinding substitute = inferenceContext.substitutes[i];
417                 if (substitute != null) {
418                     this.typeArguments[i] = inferenceContext.substitutes[i];
419                 } else {
420                     // remaining unresolved variable are considered to be Object (or their bound actually)
421
this.typeArguments[i] = originalVariables[i].upperBound();
422                 }
423             }
424         }
425         // may still need an extra substitution at the end (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=121369)
426
// to properly substitute a remaining unresolved variable which also appear in a formal bound
427
this.typeArguments = Scope.substitute(this, this.typeArguments);
428         // adjust method types to reflect latest inference
429
TypeBinding oldReturnType = this.returnType;
430         this.returnType = Scope.substitute(this, this.returnType);
431         this.inferredReturnType = inferenceContext.hasExplicitExpectedType && this.returnType != oldReturnType;
432         this.parameters = Scope.substitute(this, this.parameters);
433         this.thrownExceptions = Scope.substitute(this, this.thrownExceptions);
434         return this;
435     }
436
437     /**
438      * @see org.eclipse.jdt.internal.compiler.lookup.Substitution#isRawSubstitution()
439      */

440     public boolean isRawSubstitution() {
441         return this.isRaw;
442     }
443     
444     /**
445      * @see org.eclipse.jdt.internal.compiler.lookup.Substitution#substitute(org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding)
446      */

447     public TypeBinding substitute(TypeVariableBinding originalVariable) {
448         TypeVariableBinding[] variables = this.originalMethod.typeVariables;
449         int length = variables.length;
450         // check this variable can be substituted given parameterized type
451
if (originalVariable.rank < length && variables[originalVariable.rank] == originalVariable) {
452             return this.typeArguments[originalVariable.rank];
453         }
454         if (!this.isStatic() && this.declaringClass instanceof Substitution) {
455             return ((Substitution)this.declaringClass).substitute(originalVariable);
456         }
457         return originalVariable;
458     }
459     /**
460      * @see org.eclipse.jdt.internal.compiler.lookup.MethodBinding#tiebreakMethod()
461      */

462     public MethodBinding tiebreakMethod() {
463         if (this.tiebreakMethod == null) {
464             TypeVariableBinding[] originalVariables = originalMethod.typeVariables;
465             int length = originalVariables.length;
466             TypeBinding[] newArguments = new TypeBinding[length];
467             for (int i = 0; i < length; i++)
468                 newArguments[i] = environment.convertToRawType(originalVariables[i].upperBound());
469             this.tiebreakMethod = this.environment.createParameterizedGenericMethod(this.originalMethod, newArguments);
470         }
471         return this.tiebreakMethod;
472     }
473 }
474
Popular Tags