1
2
3
4
5
6 package mockit.internal.expectations.invocation;
7
8 import static mockit.internal.reflection.MethodReflection.JAVA_LANG;
9 import static mockit.internal.reflection.MethodReflection.findNonPrivateHandlerMethod;
10 import static mockit.internal.reflection.MethodReflection.invoke;
11
12 import edu.umd.cs.findbugs.annotations.NonNull;
13 import edu.umd.cs.findbugs.annotations.Nullable;
14
15 import java.lang.reflect.Method;
16 import java.util.concurrent.locks.ReentrantLock;
17
18 import mockit.Delegate;
19 import mockit.Invocation;
20 import mockit.asm.types.JavaType;
21 import mockit.internal.expectations.RecordAndReplayExecution;
22 import mockit.internal.reflection.ParameterReflection;
23 import mockit.internal.state.TestRun;
24 import mockit.internal.util.MethodFormatter;
25 import mockit.internal.util.TypeDescriptor;
26
27 final class DelegatedResult extends InvocationResult {
28 private static final Object[] NO_ARGS = {};
29
30 @NonNull
31 private final ExpectedInvocation recordedInvocation;
32 @NonNull
33 private final Object targetObject;
34 @NonNull
35 private final Method methodToInvoke;
36 @NonNull
37 private final Class<?> targetReturnType;
38 private final boolean hasInvocationParameter;
39 private final int numberOfRegularParameters;
40
41 DelegatedResult(@NonNull ExpectedInvocation recordedInvocation, @NonNull Delegate<?> delegate) {
42 this.recordedInvocation = recordedInvocation;
43 targetObject = delegate;
44 methodToInvoke = findNonPrivateHandlerMethod(delegate);
45
46 JavaType returnType = JavaType.getReturnType(recordedInvocation.getMethodNameAndDescription());
47 targetReturnType = TypeDescriptor.getClassForType(returnType);
48
49 Class<?>[] parameters = methodToInvoke.getParameterTypes();
50 int n = parameters.length;
51
52 hasInvocationParameter = n > 0 && parameters[0] == Invocation.class;
53 numberOfRegularParameters = hasInvocationParameter ? n - 1 : n;
54 }
55
56 @Nullable
57 @Override
58 Object produceResult(@Nullable Object invokedObject, @NonNull ExpectedInvocation invocation,
59 @NonNull InvocationConstraints constraints, @NonNull Object[] args) {
60 Object[] delegateArgs = numberOfRegularParameters == 0 ? NO_ARGS : args;
61 return hasInvocationParameter
62 ? invokeMethodWithContext(invokedObject, invocation, constraints, args, delegateArgs)
63 : executeMethodToInvoke(delegateArgs);
64 }
65
66 @Nullable
67 private Object invokeMethodWithContext(@Nullable Object mockOrRealObject,
68 @NonNull ExpectedInvocation expectedInvocation, @NonNull InvocationConstraints constraints,
69 @NonNull Object[] invokedArgs, @NonNull Object[] delegateArgs) {
70 Invocation delegateInvocation = new DelegateInvocation(mockOrRealObject, invokedArgs, expectedInvocation,
71 constraints);
72 Object[] delegateArgsWithInvocation = ParameterReflection.argumentsWithExtraFirstValue(delegateArgs,
73 delegateInvocation);
74 Object result = executeMethodToInvoke(delegateArgsWithInvocation);
75
76 return expectedInvocation.isConstructor() && TestRun.getExecutingTest().isProceedingIntoRealImplementation()
77 ? Void.class
78 : result;
79 }
80
81 @Nullable
82 private Object executeMethodToInvoke(@NonNull Object[] args) {
83 ReentrantLock reentrantLock = RecordAndReplayExecution.RECORD_OR_REPLAY_LOCK;
84
85 if (!reentrantLock.isHeldByCurrentThread()) {
86 return executeTargetMethod(args);
87 }
88
89 reentrantLock.unlock();
90
91 try {
92 return executeTargetMethod(args);
93 } finally {
94
95 reentrantLock.lock();
96 }
97 }
98
99 @Nullable
100 private Object executeTargetMethod(@NonNull Object[] args) {
101 Object returnValue = invoke(targetObject, methodToInvoke, args);
102 Class<?> fromReturnType = methodToInvoke.getReturnType();
103
104 if (returnValue == null || targetReturnType.isInstance(returnValue)) {
105 if (fromReturnType == void.class && fromReturnType != targetReturnType && targetReturnType.isPrimitive()) {
106 String returnTypeName = JAVA_LANG.matcher(targetReturnType.getName()).replaceAll("");
107 MethodFormatter methodDesc = new MethodFormatter(recordedInvocation.getClassDesc(),
108 recordedInvocation.getMethodNameAndDescription());
109 String msg = "void return type incompatible with return type " + returnTypeName + " of " + methodDesc;
110 throw new IllegalArgumentException(msg);
111 }
112
113 return returnValue;
114 }
115
116 ReturnTypeConversion typeConversion = new ReturnTypeConversion(recordedInvocation, targetReturnType,
117 returnValue);
118 return typeConversion.getConvertedValue();
119 }
120 }