| /******************************************************************************* |
| * Copyright (c) 2000, 2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.compiler.lookup; |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| import org.eclipse.jdt.internal.compiler.ast.MessageSend; |
| import org.eclipse.jdt.internal.compiler.ast.Wildcard; |
| |
| /** |
| * Binding denoting a generic method after type parameter substitutions got performed. |
| * On parameterized type bindings, all methods got substituted, regardless whether |
| * their signature did involve generics or not, so as to get the proper declaringClass for |
| * these methods. |
| */ |
| public class ParameterizedGenericMethodBinding extends ParameterizedMethodBinding implements Substitution { |
| |
| public TypeBinding[] typeArguments; |
| private LookupEnvironment environment; |
| public boolean inferredReturnType; |
| |
| /** |
| * Create method of parameterized type, substituting original parameters with type arguments. |
| */ |
| public ParameterizedGenericMethodBinding(MethodBinding originalMethod, TypeBinding[] typeArguments, LookupEnvironment environment) { |
| |
| this.environment = environment; |
| this.modifiers = originalMethod.modifiers; |
| this.selector = originalMethod.selector; |
| this.declaringClass = originalMethod.declaringClass; |
| this.typeVariables = NoTypeVariables; |
| this.typeArguments = typeArguments; |
| this.originalMethod = originalMethod; |
| this.parameters = Scope.substitute(this, originalMethod.parameters); |
| this.thrownExceptions = Scope.substitute(this, originalMethod.thrownExceptions); |
| this.returnType = this.substitute(originalMethod.returnType); |
| } |
| /** |
| * Create raw generic method for raw type (double substitution from type vars with raw type arguments, and erasure of method variables) |
| */ |
| public ParameterizedGenericMethodBinding(MethodBinding originalMethod, RawTypeBinding rawType, LookupEnvironment environment) { |
| |
| TypeVariableBinding[] originalVariables = originalMethod.typeVariables; |
| int length = originalVariables.length; |
| TypeBinding[] rawArguments = new TypeBinding[length]; |
| for (int i = 0; i < length; i++) { |
| rawArguments[i] = originalVariables[i].erasure(); |
| } |
| this.environment = environment; |
| this.modifiers = originalMethod.modifiers; |
| this.selector = originalMethod.selector; |
| this.declaringClass = rawType; |
| this.typeVariables = NoTypeVariables; |
| this.typeArguments = rawArguments; |
| this.originalMethod = originalMethod; |
| boolean isStatic = originalMethod.isStatic(); |
| this.parameters = Scope.substitute(this, |
| isStatic ? originalMethod.parameters : Scope.substitute(rawType, originalMethod.parameters)); |
| this.thrownExceptions = Scope.substitute(this, |
| isStatic ? originalMethod.thrownExceptions : Scope.substitute(rawType, originalMethod.thrownExceptions)); |
| this.returnType = this.substitute(isStatic ? originalMethod.returnType : rawType.substitute(originalMethod.returnType)); |
| } |
| |
| /** |
| * Perform inference of generic method type parameters and/or expected type |
| */ |
| public static MethodBinding computeCompatibleMethod(MethodBinding originalMethod, TypeBinding[] arguments, Scope scope, InvocationSite invocationSite) { |
| |
| ParameterizedGenericMethodBinding methodSubstitute; |
| TypeVariableBinding[] typeVariables = originalMethod.typeVariables; |
| TypeBinding[] substitutes = invocationSite.genericTypeArguments(); |
| |
| if (substitutes != null) { |
| if (substitutes.length != typeVariables.length) { |
| // incompatible due to wrong arity |
| return new ProblemMethodBinding(originalMethod, originalMethod.selector, substitutes, TypeParameterArityMismatch); |
| } |
| methodSubstitute = new ParameterizedGenericMethodBinding(originalMethod, substitutes, scope.environment()); |
| } else { |
| // perform type inference based on argument types and expected type |
| |
| // collect substitutes by pattern matching parameters and arguments |
| int argLength = arguments.length; |
| TypeBinding[] parameters = originalMethod.parameters; |
| int varLength = typeVariables.length; |
| HashMap collectedSubstitutes = new HashMap(varLength); |
| for (int i = 0; i < varLength; i++) |
| collectedSubstitutes.put(typeVariables[i], new TypeBinding[1]); |
| for (int i = 0; i < argLength; i++) |
| parameters[i].collectSubstitutes(arguments[i], collectedSubstitutes); |
| substitutes = new TypeBinding[varLength]; |
| boolean needReturnTypeInference = false; |
| for (int i = 0; i < varLength; i++) { |
| TypeBinding[] variableSubstitutes = (TypeBinding[]) collectedSubstitutes.get(typeVariables[i]); |
| TypeBinding mostSpecificSubstitute = scope.lowerUpperBound(variableSubstitutes); |
| //TypeBinding mostSpecificSubstitute = scope.mostSpecificCommonType(variableSubstitutes); |
| if (mostSpecificSubstitute == null) |
| return null; // incompatible |
| if (mostSpecificSubstitute == VoidBinding) { |
| needReturnTypeInference = true; |
| mostSpecificSubstitute = typeVariables[i]; |
| } |
| substitutes[i] = mostSpecificSubstitute; |
| } |
| // apply inferred variable substitutions |
| methodSubstitute = new ParameterizedGenericMethodBinding(originalMethod, substitutes, scope.environment()); |
| |
| if (needReturnTypeInference && invocationSite instanceof MessageSend) { |
| MessageSend message = (MessageSend) invocationSite; |
| TypeBinding expectedType = message.expectedType; |
| if (expectedType != null) |
| methodSubstitute.inferFromExpectedType(message.expectedType, scope); |
| } |
| } |
| // check bounds |
| for (int i = 0, length = typeVariables.length; i < length; i++) { |
| TypeVariableBinding typeVariable = typeVariables[i]; |
| if (!typeVariable.boundCheck(methodSubstitute, substitutes[i])) |
| // incompatible due to bound check |
| return new ProblemMethodBinding(methodSubstitute, originalMethod.selector, new TypeBinding[]{substitutes[i], typeVariables[i] }, ParameterBoundMismatch); |
| } |
| |
| return methodSubstitute; |
| } |
| |
| public void inferFromExpectedType(TypeBinding expectedType, Scope scope) { |
| if (this.returnType == expectedType) |
| return; |
| if ((this.returnType.tagBits & TagBits.HasTypeVariable) == 0) |
| return; |
| Map substitutes = new HashMap(1); |
| int length = this.typeArguments.length; |
| TypeVariableBinding[] originalVariables = this.original().typeVariables; |
| boolean hasUnboundParameters = false; |
| for (int i = 0; i < length; i++) { |
| if (this.typeArguments[i] == originalVariables[i]) { |
| hasUnboundParameters = true; |
| substitutes.put(originalVariables[i], new TypeBinding[1]); |
| } else { |
| substitutes.put(originalVariables[i], new TypeBinding[] { this.typeArguments[i] }); |
| } |
| } |
| if (!hasUnboundParameters) |
| return; |
| returnType.collectSubstitutes(expectedType, substitutes); |
| for (int i = 0; i < length; i++) { |
| TypeBinding[] variableSubstitutes = (TypeBinding[]) substitutes.get(originalVariables[i]); |
| TypeBinding mostSpecificSubstitute = scope.lowerUpperBound(variableSubstitutes); |
| //TypeBinding mostSpecificSubstitute = scope.mostSpecificCommonType(variableSubstitutes); |
| if (mostSpecificSubstitute == null) { |
| return; // TODO (philippe) should report no way to infer type |
| } |
| if (mostSpecificSubstitute != VoidBinding) |
| this.typeArguments[i] = mostSpecificSubstitute; |
| } |
| TypeBinding oldReturnType = this.returnType; |
| this.returnType = this.substitute(this.returnType); |
| this.inferredReturnType = this.returnType != oldReturnType; |
| this.parameters = Scope.substitute(this, this.parameters); |
| this.thrownExceptions = Scope.substitute(this, this.thrownExceptions); |
| } |
| |
| /** |
| * Returns a type, where original type was substituted using the receiver |
| * parameterized method. |
| */ |
| public TypeBinding substitute(TypeBinding originalType) { |
| |
| if ((originalType.tagBits & TagBits.HasTypeVariable) != 0) { |
| if (originalType.isTypeVariable()) { |
| TypeVariableBinding originalVariable = (TypeVariableBinding) originalType; |
| TypeVariableBinding[] variables = this.originalMethod.typeVariables; |
| int length = variables.length; |
| // check this variable can be substituted given parameterized type |
| if (originalVariable.rank < length && variables[originalVariable.rank] == originalVariable) { |
| return this.typeArguments[originalVariable.rank]; |
| } |
| } else if (originalType.isParameterizedType()) { |
| ParameterizedTypeBinding originalParameterizedType = (ParameterizedTypeBinding) originalType; |
| TypeBinding[] originalArguments = originalParameterizedType.arguments; |
| TypeBinding[] substitutedArguments = Scope.substitute(this, originalArguments); |
| if (substitutedArguments != originalArguments) { |
| identicalVariables: { // if substituted with original variables, then answer the generic type itself |
| TypeVariableBinding[] originalVariables = originalParameterizedType.type.typeVariables(); |
| for (int i = 0, length = originalVariables.length; i < length; i++) { |
| if (substitutedArguments[i] != originalVariables[i]) break identicalVariables; |
| } |
| return originalParameterizedType.type; |
| } |
| return this.environment.createParameterizedType( |
| originalParameterizedType.type, substitutedArguments, originalParameterizedType.enclosingType()); |
| } |
| } else if (originalType.isArrayType()) { |
| TypeBinding originalLeafComponentType = originalType.leafComponentType(); |
| TypeBinding substitute = substitute(originalLeafComponentType); // substitute could itself be array type |
| if (substitute != originalLeafComponentType) { |
| return this.environment.createArrayType(substitute.leafComponentType(), substitute.dimensions() + originalType.dimensions()); |
| } |
| } else if (originalType.isWildcard()) { |
| WildcardBinding wildcard = (WildcardBinding) originalType; |
| if (wildcard.kind != Wildcard.UNBOUND) { |
| TypeBinding originalBound = wildcard.bound; |
| TypeBinding substitutedBound = substitute(originalBound); |
| if (substitutedBound != originalBound) { |
| return this.environment.createWildcard(wildcard.genericType, wildcard.rank, substitutedBound, wildcard.kind); |
| } |
| } |
| } |
| } else if (originalType.isGenericType()) { |
| // treat as if parameterized with its type variables |
| ReferenceBinding originalGenericType = (ReferenceBinding) originalType; |
| TypeVariableBinding[] originalVariables = originalGenericType.typeVariables(); |
| int length = originalVariables.length; |
| TypeBinding[] originalArguments; |
| System.arraycopy(originalVariables, 0, originalArguments = new TypeBinding[length], 0, length); |
| TypeBinding[] substitutedArguments = Scope.substitute(this, originalArguments); |
| if (substitutedArguments != originalArguments) { |
| return this.environment.createParameterizedType( |
| originalGenericType, substitutedArguments, null); |
| } |
| } |
| return originalType; |
| } |
| } |