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