1
2
3
4
5 package mockit.internal.faking;
6
7 import edu.umd.cs.findbugs.annotations.NonNull;
8 import edu.umd.cs.findbugs.annotations.Nullable;
9
10 import java.lang.reflect.Member;
11 import java.lang.reflect.Method;
12
13 import mockit.internal.faking.FakeMethods.FakeMethod;
14 import mockit.internal.reflection.MethodReflection;
15 import mockit.internal.reflection.RealMethodOrConstructor;
16 import mockit.internal.util.ClassLoad;
17
18 final class FakeState {
19 private static final ClassLoader THIS_CL = FakeState.class.getClassLoader();
20
21 @NonNull
22 final FakeMethod fakeMethod;
23 @Nullable
24 private Method actualFakeMethod;
25 @Nullable
26 private Member realMethodOrConstructor;
27 @Nullable
28 private Object realClass;
29
30
31 private int invocationCount;
32 @Nullable
33 private ThreadLocal<FakeInvocation> proceedingInvocation;
34
35
36 @NonNull
37 private final Object invocationCountLock;
38
39 FakeState(@NonNull FakeMethod fakeMethod) {
40 this.fakeMethod = fakeMethod;
41 invocationCountLock = new Object();
42
43 if (fakeMethod.canBeReentered()) {
44 makeReentrant();
45 }
46 }
47
48 FakeState(@NonNull FakeState fakeState) {
49 fakeMethod = fakeState.fakeMethod;
50 actualFakeMethod = fakeState.actualFakeMethod;
51 realMethodOrConstructor = fakeState.realMethodOrConstructor;
52 invocationCountLock = new Object();
53
54 if (fakeState.proceedingInvocation != null) {
55 makeReentrant();
56 }
57 }
58
59 @NonNull
60 Class<?> getRealClass() {
61 return fakeMethod.getRealClass();
62 }
63
64 private void makeReentrant() {
65 proceedingInvocation = new ThreadLocal<>();
66 }
67
68 boolean update() {
69 if (proceedingInvocation != null) {
70 FakeInvocation invocation = proceedingInvocation.get();
71
72 if (invocation != null && invocation.proceeding) {
73 invocation.proceeding = false;
74 return false;
75 }
76 }
77
78 synchronized (invocationCountLock) {
79 invocationCount++;
80 }
81
82 return true;
83 }
84
85 int getTimesInvoked() {
86 synchronized (invocationCountLock) {
87 return invocationCount;
88 }
89 }
90
91 @NonNull
92 Member getRealMethodOrConstructor(@NonNull String fakedClassDesc, @NonNull String fakedMethodName,
93 @NonNull String fakedMethodDesc) {
94 Class<?> fakedClass = ClassLoad.loadFromLoader(THIS_CL, fakedClassDesc.replace('/', '.'));
95 return getRealMethodOrConstructor(fakedClass, fakedMethodName, fakedMethodDesc);
96 }
97
98 @NonNull
99 Member getRealMethodOrConstructor(@NonNull Class<?> fakedClass, @NonNull String fakedMethodName,
100 @NonNull String fakedMethodDesc) {
101 Member member = realMethodOrConstructor;
102
103 if (member == null || !fakedClass.equals(realClass)) {
104 String memberName = "$init".equals(fakedMethodName) ? "<init>" : fakedMethodName;
105
106 RealMethodOrConstructor realMember;
107 try {
108 realMember = new RealMethodOrConstructor(fakedClass, memberName, fakedMethodDesc);
109 } catch (NoSuchMethodException e) {
110 throw new RuntimeException(e);
111 }
112
113 member = realMember.getMember();
114
115 if (!fakeMethod.isAdvice) {
116 realMethodOrConstructor = member;
117 realClass = fakedClass;
118 }
119 }
120
121 return member;
122 }
123
124 boolean shouldProceedIntoRealImplementation(@Nullable Object fake, @NonNull String classDesc) {
125 if (proceedingInvocation != null) {
126 FakeInvocation pendingInvocation = proceedingInvocation.get();
127
128
129 if (pendingInvocation != null && pendingInvocation.isMethodInSuperclass(fake, classDesc)) {
130 return true;
131 }
132 }
133
134 return false;
135 }
136
137 void prepareToProceed(@NonNull FakeInvocation invocation) {
138 if (proceedingInvocation == null) {
139 throw new UnsupportedOperationException("Cannot proceed into abstract/interface method");
140 }
141
142 if (fakeMethod.isForNativeMethod()) {
143 throw new UnsupportedOperationException("Cannot proceed into real implementation of native method");
144 }
145
146 FakeInvocation previousInvocation = proceedingInvocation.get();
147
148 if (previousInvocation != null) {
149 invocation.setPrevious(previousInvocation);
150 }
151
152 proceedingInvocation.set(invocation);
153 }
154
155 void prepareToProceedFromNonRecursiveFake(@NonNull FakeInvocation invocation) {
156 assert proceedingInvocation != null;
157 proceedingInvocation.set(invocation);
158 }
159
160 void clearProceedIndicator() {
161 assert proceedingInvocation != null;
162 FakeInvocation currentInvocation = proceedingInvocation.get();
163 FakeInvocation previousInvocation = (FakeInvocation) currentInvocation.getPrevious();
164 proceedingInvocation.set(previousInvocation);
165 }
166
167 @NonNull
168 Method getFakeMethod(@NonNull Class<?> fakeClass, @NonNull Class<?>[] parameterTypes) {
169 if (actualFakeMethod == null) {
170 actualFakeMethod = MethodReflection.findCompatibleMethod(fakeClass, fakeMethod.name, parameterTypes);
171 }
172
173 return actualFakeMethod;
174 }
175 }