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