1
2
3
4
5
6 package mockit.internal.reflection;
7
8 import static java.lang.reflect.Modifier.isPrivate;
9 import static java.lang.reflect.Modifier.isStatic;
10
11 import static mockit.internal.reflection.ParameterReflection.*;
12
13 import edu.umd.cs.findbugs.annotations.NonNull;
14 import edu.umd.cs.findbugs.annotations.Nullable;
15
16 import java.lang.reflect.InvocationTargetException;
17 import java.lang.reflect.Method;
18 import java.util.regex.Pattern;
19
20 import mockit.Delegate;
21 import mockit.internal.util.StackTrace;
22 import mockit.internal.util.Utilities;
23
24 public final class MethodReflection {
25 @NonNull
26 public static final Pattern JAVA_LANG = Pattern.compile("java.lang.", Pattern.LITERAL);
27
28 private MethodReflection() {
29 }
30
31 @Nullable
32 public static <T> T invoke(@NonNull Class<?> theClass, @Nullable Object targetInstance, @NonNull String methodName,
33 @NonNull Class<?>[] paramTypes, @NonNull Object... methodArgs) {
34 Method method = findSpecifiedMethod(theClass, methodName, paramTypes);
35 return invoke(targetInstance, method, methodArgs);
36 }
37
38 @NonNull
39 private static Method findSpecifiedMethod(@NonNull Class<?> theClass, @NonNull String methodName,
40 @NonNull Class<?>[] paramTypes) {
41 while (true) {
42 Method declaredMethod = findSpecifiedMethodInGivenClass(theClass, methodName, paramTypes);
43
44 if (declaredMethod != null) {
45 return declaredMethod;
46 }
47
48 Class<?> superClass = theClass.getSuperclass();
49
50 if (superClass == null || superClass == Object.class) {
51 String paramTypesDesc = getParameterTypesDescription(paramTypes);
52 throw new IllegalArgumentException("Specified method not found: " + methodName + paramTypesDesc);
53 }
54
55
56 theClass = superClass;
57 }
58 }
59
60 @Nullable
61 private static Method findSpecifiedMethodInGivenClass(@NonNull Class<?> theClass, @NonNull String methodName,
62 @NonNull Class<?>[] paramTypes) {
63 for (Method declaredMethod : theClass.getDeclaredMethods()) {
64 if (declaredMethod.getName().equals(methodName)) {
65 Class<?>[] declaredParameterTypes = declaredMethod.getParameterTypes();
66 int firstRealParameter = indexOfFirstRealParameter(declaredParameterTypes, paramTypes);
67
68 if (firstRealParameter >= 0
69 && matchesParameterTypes(declaredMethod.getParameterTypes(), paramTypes, firstRealParameter)) {
70 return declaredMethod;
71 }
72 }
73 }
74
75 return null;
76 }
77
78 @Nullable
79 public static <T> T invokePublicIfAvailable(@NonNull Class<?> aClass, @Nullable Object targetInstance,
80 @NonNull String methodName, @NonNull Class<?>[] parameterTypes, @NonNull Object... methodArgs) {
81 Method publicMethod;
82 try {
83 publicMethod = aClass.getMethod(methodName, parameterTypes);
84 } catch (NoSuchMethodException ignore) {
85 return null;
86 }
87
88 return invoke(targetInstance, publicMethod, methodArgs);
89 }
90
91 @Nullable
92 public static <T> T invokeWithCheckedThrows(@NonNull Class<?> theClass, @Nullable Object targetInstance,
93 @NonNull String methodName, @NonNull Class<?>[] paramTypes, @NonNull Object... methodArgs)
94 throws Throwable {
95 Method method = findSpecifiedMethod(theClass, methodName, paramTypes);
96 return invokeWithCheckedThrows(targetInstance, method, methodArgs);
97 }
98
99 @Nullable
100 public static <T> T invoke(@Nullable Object targetInstance, @NonNull Method method, @NonNull Object... methodArgs) {
101 Utilities.ensureThatMemberIsAccessible(method);
102
103 try {
104
105 return (T) method.invoke(targetInstance, methodArgs);
106 } catch (IllegalAccessException e) {
107 throw new RuntimeException(e);
108 } catch (IllegalArgumentException e) {
109 StackTrace.filterStackTrace(e);
110 throw new IllegalArgumentException("Failure to invoke method: " + method, e);
111 } catch (InvocationTargetException e) {
112 Throwable cause = e.getCause();
113
114 if (cause instanceof Error) {
115 throw (Error) cause;
116 } else if (cause instanceof RuntimeException) {
117 throw (RuntimeException) cause;
118 } else {
119 ThrowOfCheckedException.doThrow((Exception) cause);
120 return null;
121 }
122 }
123 }
124
125 @Nullable
126 public static <T> T invokeWithCheckedThrows(@Nullable Object targetInstance, @NonNull Method method,
127 @NonNull Object... methodArgs) throws Throwable {
128 Utilities.ensureThatMemberIsAccessible(method);
129
130 try {
131
132 return (T) method.invoke(targetInstance, methodArgs);
133 } catch (IllegalArgumentException e) {
134 StackTrace.filterStackTrace(e);
135 throw new IllegalArgumentException("Failure to invoke method: " + method, e);
136 } catch (InvocationTargetException e) {
137 throw e.getCause();
138 }
139 }
140
141 @Nullable
142 public static <T> T invoke(@NonNull Class<?> theClass, @Nullable Object targetInstance, @NonNull String methodName,
143 @NonNull Object... methodArgs) {
144 boolean staticMethod = targetInstance == null;
145 Class<?>[] argTypes = getArgumentTypesFromArgumentValues(methodArgs);
146 Method method = staticMethod ? findCompatibleStaticMethod(theClass, methodName, argTypes)
147 : findCompatibleMethod(theClass, methodName, argTypes);
148
149 if (staticMethod && !isStatic(method.getModifiers())) {
150 throw new IllegalArgumentException(
151 "Attempted to invoke non-static method without an instance to invoke it on");
152 }
153
154 T result = invoke(targetInstance, method, methodArgs);
155 return result;
156 }
157
158 @NonNull
159 private static Method findCompatibleStaticMethod(@NonNull Class<?> theClass, @NonNull String methodName,
160 @NonNull Class<?>[] argTypes) {
161 Method methodFound = findCompatibleMethodInClass(theClass, methodName, argTypes);
162
163 if (methodFound != null) {
164 return methodFound;
165 }
166
167 String argTypesDesc = getParameterTypesDescription(argTypes);
168 throw new IllegalArgumentException("No compatible static method found: " + methodName + argTypesDesc);
169 }
170
171 @NonNull
172 public static Method findCompatibleMethod(@NonNull Class<?> theClass, @NonNull String methodName,
173 @NonNull Class<?>[] argTypes) {
174 Method methodFound = findCompatibleMethodIfAvailable(theClass, methodName, argTypes);
175
176 if (methodFound != null) {
177 return methodFound;
178 }
179
180 String argTypesDesc = getParameterTypesDescription(argTypes);
181 throw new IllegalArgumentException("No compatible method found: " + methodName + argTypesDesc);
182 }
183
184 @Nullable
185 private static Method findCompatibleMethodIfAvailable(@NonNull Class<?> theClass, @NonNull String methodName,
186 @NonNull Class<?>[] argTypes) {
187 Method methodFound = null;
188
189 while (true) {
190 Method compatibleMethod = findCompatibleMethodInClass(theClass, methodName, argTypes);
191
192 if (compatibleMethod != null && (methodFound == null
193 || hasMoreSpecificTypes(compatibleMethod.getParameterTypes(), methodFound.getParameterTypes()))) {
194 methodFound = compatibleMethod;
195 }
196
197 Class<?> superClass = theClass.getSuperclass();
198
199 if (superClass == null || superClass == Object.class) {
200 break;
201 }
202
203
204 theClass = superClass;
205 }
206
207 return methodFound;
208 }
209
210 @Nullable
211 private static Method findCompatibleMethodInClass(@NonNull Class<?> theClass, @NonNull String methodName,
212 @NonNull Class<?>[] argTypes) {
213 Method found = null;
214 Class<?>[] foundParamTypes = null;
215
216 for (Method declaredMethod : theClass.getDeclaredMethods()) {
217 if (declaredMethod.getName().equals(methodName)) {
218 Class<?>[] declaredParamTypes = declaredMethod.getParameterTypes();
219 int firstRealParameter = indexOfFirstRealParameter(declaredParamTypes, argTypes);
220
221 if (firstRealParameter >= 0
222 && (matchesParameterTypes(declaredParamTypes, argTypes, firstRealParameter)
223 || acceptsArgumentTypes(declaredParamTypes, argTypes, firstRealParameter))
224 && (foundParamTypes == null || hasMoreSpecificTypes(declaredParamTypes, foundParamTypes))) {
225 found = declaredMethod;
226 foundParamTypes = declaredParamTypes;
227 }
228 }
229 }
230
231 return found;
232 }
233
234 @NonNull
235 public static Method findNonPrivateHandlerMethod(@NonNull Object handler) {
236 Class<?> handlerClass = handler.getClass();
237 Method nonPrivateMethod;
238
239 do {
240 nonPrivateMethod = findNonPrivateHandlerMethod(handlerClass);
241
242 if (nonPrivateMethod != null) {
243 break;
244 }
245
246 handlerClass = handlerClass.getSuperclass();
247 } while (handlerClass != null && handlerClass != Object.class);
248
249 if (nonPrivateMethod == null) {
250 throw new IllegalArgumentException("No non-private instance method found");
251 }
252
253 return nonPrivateMethod;
254 }
255
256 @Nullable
257 private static Method findNonPrivateHandlerMethod(@NonNull Class<?> handlerClass) {
258 Method[] declaredMethods = handlerClass.getDeclaredMethods();
259 Method found = null;
260
261 for (Method declaredMethod : declaredMethods) {
262 int methodModifiers = declaredMethod.getModifiers();
263
264 if (!isPrivate(methodModifiers) && !isStatic(methodModifiers)) {
265 if (found != null) {
266 String methodType = Delegate.class.isAssignableFrom(handlerClass) ? "delegate"
267 : "invocation handler";
268 throw new IllegalArgumentException("More than one candidate " + methodType + " method found: "
269 + methodSignature(found) + ", " + methodSignature(declaredMethod));
270 }
271
272 found = declaredMethod;
273 }
274 }
275
276 return found;
277 }
278
279 @NonNull
280 private static String methodSignature(@NonNull Method method) {
281 String signature = JAVA_LANG.matcher(method.toGenericString()).replaceAll("");
282 int p = signature.lastIndexOf('(');
283 int q = signature.lastIndexOf('.', p);
284
285 return signature.substring(q + 1);
286 }
287 }