com.strobel.reflection.Binder Maven / Gradle / Ivy
/*
* Binder.java
*
* Copyright (c) 2012 Mike Strobel
*
* This source code is subject to terms and conditions of the Apache License, Version 2.0.
* A copy of the license can be found in the License.html file at the root of this distribution.
* By using this source code in any fashion, you are agreeing to be bound by the terms of the
* Apache License, Version 2.0.
*
* You must not remove this notice, or any other, from this software.
*/
package com.strobel.reflection;
import com.strobel.core.ArrayUtilities;
import com.strobel.util.TypeUtils;
import java.util.Set;
/**
* @author Mike Strobel
*/
public abstract class Binder {
static boolean compareMethodSignatureAndName(final MethodBase m1, final MethodBase m2) {
final ParameterList p1 = m1.getParameters();
final ParameterList p2 = m2.getParameters();
if (p1.size() != p2.size()) {
return false;
}
for (int i = 0, n = p1.size(); i < n; i++) {
if (!TypeUtils.areEquivalent(p1.get(i).getParameterType(), p2.get(i).getParameterType())) {
return false;
}
}
return true;
}
static int getHierarchyDepth(final Type t) {
int depth = 0;
Type currentType = t;
do {
depth++;
currentType = currentType.getBaseType();
} while (currentType != null);
return depth;
}
static MethodBase findMostDerivedNewSlotMethod(final MethodBase[] match, final int cMatches) {
int deepestHierarchy = 0;
MethodBase methodWithDeepestHierarchy = null;
for (int i = 0; i < cMatches; i++) {
// Calculate the depth of the hierarchy of the declaring type of the
// current method.
final int currentHierarchyDepth = getHierarchyDepth(match[i].getDeclaringType());
// The two methods have the same name, signature, and hierarchy depth.
// This can only happen if at least one is vararg or generic.
if (currentHierarchyDepth == deepestHierarchy) {
throw Error.ambiguousMatch();
}
// Check to see if this method is on the most derived class.
if (currentHierarchyDepth > deepestHierarchy) {
deepestHierarchy = currentHierarchyDepth;
methodWithDeepestHierarchy = match[i];
}
}
return methodWithDeepestHierarchy;
}
public abstract MethodBase selectMethod(final Set bindingFlags, final MethodBase[] matches, final Type[] parameterTypes);
}
@SuppressWarnings("ConstantConditions")
final class DefaultBinder extends Binder {
@Override
public MethodBase selectMethod(final Set bindingFlags, final MethodBase[] matches, final Type[] types) {
if (ArrayUtilities.isNullOrEmpty(matches)) {
return null;
}
final MethodBase[] candidates = matches.clone();
//
// Find all the methods that can be described by the parameter types.
// Remove all of them that cannot.
//
int stop;
int currentIndex = 0;
for (int i = 0, n = candidates.length; i < n; i++) {
final MethodBase candidate = candidates[i];
final ParameterList parameters = candidate.getParameters();
final int parameterCount = parameters.size();
final boolean isVarArgs = candidate.getCallingConvention() == CallingConvention.VarArgs;
if (parameterCount != types.length && !isVarArgs) {
continue;
}
for (stop = 0; stop < Math.min(parameterCount, types.length); stop++) {
final Type parameterType = parameters.get(stop).getParameterType();
if (parameterType == types[stop] || parameterType == Types.Object) {
continue;
}
if (parameterType.isAssignableFrom(types[stop])) {
continue;
}
if (!isVarArgs || stop != parameterCount - 1) {
break;
}
if (!parameterType.getElementType().isAssignableFrom(types[stop])) {
break;
}
}
if (stop == parameterCount ||
stop == parameterCount - 1 && isVarArgs) {
candidates[currentIndex++] = candidate;
}
}
if (currentIndex == 0) {
return null;
}
if (currentIndex == 1) {
return candidates[0];
}
// Walk all of the methods looking the most specific method to invoke
int currentMin = 0;
boolean ambiguous = false;
final int[] parameterOrder = new int[types.length];
for (int i = 0, n = types.length; i < n; i++) {
parameterOrder[i] = i;
}
for (int i = 1; i < currentIndex; i++) {
final MethodBase m1 = candidates[currentMin];
final MethodBase m2 = candidates[i];
final Type> varArgType1;
final Type> varArgType2;
if (m1.getCallingConvention() == CallingConvention.VarArgs) {
final TypeList pt1 = m1.getParameters().getParameterTypes();
varArgType1 = pt1.get(pt1.size() - 1).getElementType();
}
else {
varArgType1 = null;
}
if (m2.getCallingConvention() == CallingConvention.VarArgs) {
final TypeList pt2 = m2.getParameters().getParameterTypes();
varArgType2 = pt2.get(pt2.size() - 1).getElementType();
}
else {
varArgType2 = null;
}
final int newMin = findMostSpecificMethod(
m1,
parameterOrder,
varArgType1,
candidates[i],
parameterOrder,
varArgType2,
types,
null
);
if (newMin == 0) {
ambiguous = true;
}
else {
if (newMin == 2) {
ambiguous = false;
currentMin = i;
}
}
}
if (ambiguous) {
throw Error.ambiguousMatch();
}
return candidates[currentMin];
}
private static int findMostSpecificMethod(
final MethodBase m1,
final int[] varArgOrder1,
final Type varArgArrayType1,
final MethodBase m2,
final int[] varArgOrder2,
final Type varArgArrayType2,
final Type[] types,
final Object[] args) {
//
// Find the most specific method based on the parameters.
//
final int result = findMostSpecific(
m1.getParameters(),
varArgOrder1,
varArgArrayType1,
m2.getParameters(),
varArgOrder2,
varArgArrayType2,
types,
args
);
//
// If the match was not ambiguous then return the result.
//
if (result != 0) {
return result;
}
//
// Check to see if the methods have the exact same name and signature.
//
if (compareMethodSignatureAndName(m1, m2)) {
//
// Determine the depth of the declaring types for both methods.
//
final int hierarchyDepth1 = getHierarchyDepth(m1.getDeclaringType());
final int hierarchyDepth2 = getHierarchyDepth(m2.getDeclaringType());
//
// The most derived method is the most specific one.
//
if (hierarchyDepth1 == hierarchyDepth2) {
return 0;
}
else if (hierarchyDepth1 < hierarchyDepth2) {
return 2;
}
else {
return 1;
}
}
//
// The match is ambiguous.
//
return 0;
}
private static int findMostSpecific(
final ParameterList p1,
final int[] varArgOrder1,
final Type varArgArrayType1,
final ParameterList p2,
final int[] varArgOrder2,
final Type varArgArrayType2,
final Type[] types,
final Object[] args) {
//
// A method using varargs is always less specific than one not using varargs.
//
if (varArgArrayType1 != null && varArgArrayType2 == null) {
return 2;
}
if (varArgArrayType2 != null && varArgArrayType1 == null) {
return 1;
}
//
// Now either p1 and p2 both use params or neither does.
//
boolean p1Less = false;
boolean p2Less = false;
for (int i = 0, n = types.length; i < n; i++) {
if (args != null && args[i] == Missing.Value) {
continue;
}
final Type c1;
final Type c2;
//
// If a vararg array is present, then either
// the user re-ordered the parameters, in which case
// the argument to the vararg array is either an array
// in which case the params is conceptually ignored and so varArgArrayType1 == null
// or the argument to the vararg array is a single element
// in which case varArgOrder[i] == p1.Length - 1 for that element
// or the user did not re-order the parameters in which case
// the varArgOrder array could contain indexes larger than p.Length - 1 (see VSW 577286)
// so any index >= p.Length - 1 is being put in the vararg array
//
if (varArgArrayType1 != null && varArgOrder1[i] >= p1.size() - 1) {
c1 = varArgArrayType1;
}
else {
c1 = p1.get(varArgOrder1[i]).getParameterType();
}
if (varArgArrayType2 != null && varArgOrder2[i] >= p2.size() - 1) {
c2 = varArgArrayType2;
}
else {
c2 = p2.get(varArgOrder2[i]).getParameterType();
}
if (c1 == c2) {
continue;
}
switch (findMostSpecificType(c1, c2, types[i])) {
case 0:
return 0;
case 1:
p1Less = true;
break;
case 2:
p2Less = true;
break;
}
}
// Two way p1Less and p2Less can be equal. All the arguments are the
// same they both equal false, otherwise there were things that both
// were the most specific type on....
if (p1Less == p2Less) {
// if we cannot tell which is a better match based on parameter types (p1Less == p2Less),
// let's see which one has the most matches without using the params array (the longer one wins).
if (!p1Less && args != null) {
if (p1.size() > p2.size()) {
return 1;
}
else if (p2.size() > p1.size()) {
return 2;
}
}
return 0;
}
else {
return p1Less ? 1 : 2;
}
}
private static int findMostSpecificType(final Type c1, final Type c2, final Type t) {
//
// If the two types are exact move on...
//
if (TypeUtils.areEquivalent(c1, c2)) {
return 0;
}
if (TypeUtils.areEquivalent(c1, t)) {
return 1;
}
if (TypeUtils.areEquivalent(c2, t)) {
return 2;
}
final boolean c1FromC2 = c1.isAssignableFrom(c2);
final boolean c2FromC1 = c2.isAssignableFrom(c1);
if (c1FromC2 == c2FromC1) {
return 0;
}
return c1FromC2 ? 2 : 1;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy