1
2
3
4
5
6 package mockit.internal.state;
7
8 import static java.lang.reflect.Modifier.isAbstract;
9
10 import static mockit.internal.util.GeneratedClasses.getMockedClass;
11 import static mockit.internal.util.GeneratedClasses.getMockedClassOrInterfaceType;
12 import static mockit.internal.util.GeneratedClasses.isGeneratedImplementationClass;
13 import static mockit.internal.util.Utilities.getClassType;
14
15 import edu.umd.cs.findbugs.annotations.NonNull;
16 import edu.umd.cs.findbugs.annotations.Nullable;
17
18 import java.lang.instrument.ClassDefinition;
19 import java.lang.reflect.InvocationTargetException;
20 import java.lang.reflect.Method;
21 import java.lang.reflect.Type;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.IdentityHashMap;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Map.Entry;
31 import java.util.Set;
32 import java.util.concurrent.ConcurrentHashMap;
33
34 import mockit.internal.ClassFile;
35 import mockit.internal.ClassIdentification;
36 import mockit.internal.capturing.CaptureTransformer;
37 import mockit.internal.expectations.mocking.CaptureOfNewInstances;
38 import mockit.internal.expectations.mocking.InstanceFactory;
39 import mockit.internal.startup.Startup;
40 import mockit.internal.util.ClassLoad;
41
42
43
44
45 public final class MockFixture {
46
47
48
49
50
51
52
53
54 @NonNull
55 private final Map<ClassIdentification, byte[]> transformedClasses;
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72 @NonNull
73 private final Map<Class<?>, byte[]> redefinedClasses;
74
75
76
77
78
79
80
81
82
83 @NonNull
84 private final Set<String> redefinedClassesWithNativeMethods;
85
86
87
88
89
90
91
92
93
94 @NonNull
95 private final Map<Class<?>, String> realClassesToFakeClasses;
96
97
98
99
100
101
102
103
104
105
106 @NonNull
107 private final List<Class<?>> mockedClasses;
108
109
110
111
112
113
114
115
116
117 @NonNull
118 private final Map<Type, InstanceFactory> mockedTypesAndInstances;
119
120
121
122
123
124
125
126
127
128
129 @NonNull
130 private final List<CaptureTransformer<?>> captureTransformers;
131
132 MockFixture() {
133 transformedClasses = new HashMap<>(2);
134 redefinedClasses = new ConcurrentHashMap<>(8);
135 redefinedClassesWithNativeMethods = new HashSet<>();
136 realClassesToFakeClasses = new IdentityHashMap<>(8);
137 mockedClasses = new ArrayList<>();
138 mockedTypesAndInstances = new IdentityHashMap<>();
139 captureTransformers = new ArrayList<>();
140 }
141
142
143
144 public void addTransformedClass(@NonNull ClassIdentification classId, @NonNull byte[] pretransformClassfile) {
145 transformedClasses.put(classId, pretransformClassfile);
146 }
147
148
149
150 public void addRedefinedClass(@NonNull ClassDefinition newClassDefinition) {
151 redefinedClasses.put(newClassDefinition.getDefinitionClass(), newClassDefinition.getDefinitionClassFile());
152 }
153
154 public void registerMockedClass(@NonNull Class<?> mockedType) {
155 if (!mockedClasses.contains(mockedType)) {
156 mockedType = getMockedClassOrInterfaceType(mockedType);
157 mockedClasses.add(mockedType);
158 }
159 }
160
161
162
163 public void redefineClasses(@NonNull ClassDefinition... definitions) {
164 Startup.redefineMethods(definitions);
165
166 for (ClassDefinition def : definitions) {
167 addRedefinedClass(def);
168 }
169 }
170
171 public void redefineMethods(@NonNull Map<Class<?>, byte[]> modifiedClassfiles) {
172 ClassDefinition[] classDefs = new ClassDefinition[modifiedClassfiles.size()];
173 int i = 0;
174
175 for (Entry<Class<?>, byte[]> classAndBytecode : modifiedClassfiles.entrySet()) {
176 Class<?> modifiedClass = classAndBytecode.getKey();
177 byte[] modifiedClassfile = classAndBytecode.getValue();
178
179 ClassDefinition classDef = new ClassDefinition(modifiedClass, modifiedClassfile);
180 classDefs[i] = classDef;
181 i++;
182
183 addRedefinedClass(classDef);
184 }
185
186 Startup.redefineMethods(classDefs);
187 }
188
189 public boolean isStillMocked(@Nullable Object instance, @NonNull String classDesc) {
190 Class<?> targetClass;
191
192 if (instance == null) {
193 targetClass = ClassLoad.loadByInternalName(classDesc);
194 return isClassAssignableTo(targetClass);
195 }
196
197 targetClass = instance.getClass();
198 return mockedTypesAndInstances.containsKey(targetClass) || isInstanceOfMockedClass(instance);
199 }
200
201 private boolean isClassAssignableTo(@NonNull Class<?> toClass) {
202 for (Class<?> mockedClass : mockedClasses) {
203 if (toClass == mockedClass || toClass.isAssignableFrom(mockedClass)) {
204 return true;
205 }
206 }
207
208 return false;
209 }
210
211 public boolean isInstanceOfMockedClass(@NonNull Object mockedInstance) {
212 Class<?> mockedClass = getMockedClassOrInterfaceType(mockedInstance.getClass());
213 Class<?> mockedSuperclass = mockedClass.getSuperclass();
214
215 if (mockedSuperclass != null && mockedSuperclass.isEnum()) {
216 return mockedClasses.contains(mockedSuperclass);
217 }
218
219 return mockedClasses.contains(mockedClass) || isCaptured(mockedClass);
220 }
221
222 public void registerInstanceFactoryForMockedType(@NonNull Class<?> mockedType,
223 @NonNull InstanceFactory mockedInstanceFactory) {
224 registerMockedClass(mockedType);
225 mockedTypesAndInstances.put(mockedType, mockedInstanceFactory);
226 }
227
228 @Nullable
229 public InstanceFactory findInstanceFactory(@NonNull Type mockedType) {
230 InstanceFactory instanceFactory = mockedTypesAndInstances.get(mockedType);
231
232 if (instanceFactory != null) {
233 return instanceFactory;
234 }
235
236 Class<?> mockedClass = getClassType(mockedType);
237
238 instanceFactory = mockedTypesAndInstances.get(mockedClass);
239
240 if (instanceFactory != null) {
241 return instanceFactory;
242 }
243
244 boolean abstractType = mockedClass.isInterface() || isAbstract(mockedClass.getModifiers());
245
246 for (Entry<Type, InstanceFactory> entry : mockedTypesAndInstances.entrySet()) {
247 Type registeredMockedType = entry.getKey();
248 Class<?> registeredMockedClass = getClassType(registeredMockedType);
249
250 if (abstractType) {
251 registeredMockedClass = getMockedClassOrInterfaceType(registeredMockedClass);
252 }
253
254 if (mockedClass.isAssignableFrom(registeredMockedClass)) {
255 instanceFactory = entry.getValue();
256 break;
257 }
258 }
259
260 return instanceFactory;
261 }
262
263
264
265 public void addRedefinedClass(@NonNull String fakeClassInternalName, @NonNull ClassDefinition classDef) {
266 @NonNull
267 Class<?> redefinedClass = classDef.getDefinitionClass();
268 String previousNames = realClassesToFakeClasses.put(redefinedClass, fakeClassInternalName);
269
270 if (previousNames != null) {
271 realClassesToFakeClasses.put(redefinedClass, previousNames + ' ' + fakeClassInternalName);
272 }
273
274 addRedefinedClass(classDef);
275 }
276
277
278
279 void restoreTransformedClasses(@NonNull Set<ClassIdentification> previousTransformedClasses) {
280 if (!transformedClasses.isEmpty()) {
281 Set<ClassIdentification> classesToRestore;
282
283 if (previousTransformedClasses.isEmpty()) {
284 classesToRestore = transformedClasses.keySet();
285 } else {
286 classesToRestore = getTransformedClasses();
287 classesToRestore.removeAll(previousTransformedClasses);
288 }
289
290 if (!classesToRestore.isEmpty()) {
291 restoreAndRemoveTransformedClasses(classesToRestore);
292 }
293 }
294 }
295
296 @NonNull
297 Set<ClassIdentification> getTransformedClasses() {
298 return transformedClasses.isEmpty() ? Collections.emptySet() : new HashSet<>(transformedClasses.keySet());
299 }
300
301 @NonNull
302 Map<Class<?>, byte[]> getRedefinedClasses() {
303 return redefinedClasses.isEmpty() ? Collections.emptyMap() : new HashMap<>(redefinedClasses);
304 }
305
306 private void restoreAndRemoveTransformedClasses(@NonNull Set<ClassIdentification> classesToRestore) {
307 for (ClassIdentification transformedClassId : classesToRestore) {
308 byte[] definitionToRestore = transformedClasses.get(transformedClassId);
309 Startup.redefineMethods(transformedClassId, definitionToRestore);
310 }
311
312 transformedClasses.keySet().removeAll(classesToRestore);
313 }
314
315 void restoreRedefinedClasses(@NonNull Map<?, byte[]> previousDefinitions) {
316 if (redefinedClasses.isEmpty()) {
317 return;
318 }
319
320 Iterator<Entry<Class<?>, byte[]>> itr = redefinedClasses.entrySet().iterator();
321
322 while (itr.hasNext()) {
323 Entry<Class<?>, byte[]> entry = itr.next();
324 Class<?> redefinedClass = entry.getKey();
325 byte[] currentDefinition = entry.getValue();
326 byte[] previousDefinition = previousDefinitions.get(redefinedClass);
327
328 if (previousDefinition == null) {
329 restoreDefinition(redefinedClass);
330 itr.remove();
331 } else if (currentDefinition != previousDefinition) {
332 Startup.redefineMethods(redefinedClass, previousDefinition);
333 entry.setValue(previousDefinition);
334 }
335 }
336 }
337
338 private void restoreDefinition(@NonNull Class<?> redefinedClass) {
339 if (!isGeneratedImplementationClass(redefinedClass)) {
340 byte[] previousDefinition = ClassFile.getClassFile(redefinedClass);
341 Startup.redefineMethods(redefinedClass, previousDefinition);
342 }
343 if (redefinedClassesWithNativeMethods.contains(redefinedClass.getName())) {
344 reregisterNativeMethodsForRestoredClass(redefinedClass);
345 }
346
347 removeMockedClass(redefinedClass);
348 discardStateForCorrespondingFakeClassIfAny(redefinedClass);
349 }
350
351 private void removeMockedClass(@NonNull Class<?> mockedClass) {
352 mockedTypesAndInstances.remove(mockedClass);
353 mockedClasses.remove(mockedClass);
354 }
355
356 private void discardStateForCorrespondingFakeClassIfAny(@NonNull Class<?> redefinedClass) {
357 String mockClassesInternalNames = realClassesToFakeClasses.remove(redefinedClass);
358 TestRun.getFakeStates().removeClassState(redefinedClass, mockClassesInternalNames);
359 }
360
361 void removeMockedClasses(@NonNull List<Class<?>> previousMockedClasses) {
362 int currentMockedClassCount = mockedClasses.size();
363
364 if (currentMockedClassCount > 0) {
365 int previousMockedClassCount = previousMockedClasses.size();
366
367 if (previousMockedClassCount == 0) {
368 mockedClasses.clear();
369 mockedTypesAndInstances.clear();
370 } else if (previousMockedClassCount < currentMockedClassCount) {
371 mockedClasses.retainAll(previousMockedClasses);
372 mockedTypesAndInstances.keySet().retainAll(previousMockedClasses);
373 }
374 }
375 }
376
377
378
379 @Nullable
380 public byte[] getRedefinedClassfile(@NonNull Class<?> redefinedClass) {
381 return redefinedClasses.get(redefinedClass);
382 }
383
384 public boolean containsRedefinedClass(@NonNull Class<?> redefinedClass) {
385 return redefinedClasses.containsKey(redefinedClass);
386 }
387
388 @NonNull
389 public List<Class<?>> getMockedClasses() {
390 return mockedClasses.isEmpty() ? Collections.emptyList() : new ArrayList<>(mockedClasses);
391 }
392
393
394
395 public void addCaptureTransformer(@NonNull CaptureTransformer<?> transformer) {
396 captureTransformers.add(transformer);
397 }
398
399
400
401 int getCaptureTransformerCount() {
402 return captureTransformers.size();
403 }
404
405 void removeCaptureTransformers(int previousTransformerCount) {
406 int currentTransformerCount = captureTransformers.size();
407
408 for (int i = currentTransformerCount - 1; i >= previousTransformerCount; i--) {
409 CaptureTransformer<?> transformer = captureTransformers.get(i);
410 transformer.deactivate();
411 Startup.instrumentation().removeTransformer(transformer);
412 captureTransformers.remove(i);
413 }
414 }
415
416
417
418 public boolean isCaptured(@NonNull Object mockedInstance) {
419 if (!captureTransformers.isEmpty()) {
420 Class<?> mockedClass = getMockedClass(mockedInstance);
421 return isCaptured(mockedClass);
422 }
423
424 return false;
425 }
426
427 private boolean isCaptured(@NonNull Class<?> mockedClass) {
428 for (CaptureTransformer<?> captureTransformer : captureTransformers) {
429 CaptureOfNewInstances capture = captureTransformer.getCaptureOfImplementationsIfApplicable(mockedClass);
430
431 if (capture != null) {
432 return true;
433 }
434 }
435
436 return false;
437 }
438
439 public boolean areCapturedClasses(@NonNull Class<?> mockedClass1, @NonNull Class<?> mockedClass2) {
440 for (CaptureTransformer<?> captureTransformer : captureTransformers) {
441 if (captureTransformer.areCapturedClasses(mockedClass1, mockedClass2)) {
442 return true;
443 }
444 }
445
446 return false;
447 }
448
449 private static void reregisterNativeMethodsForRestoredClass(@NonNull Class<?> realClass) {
450 Method registerNatives = null;
451
452 try {
453 registerNatives = realClass.getDeclaredMethod("registerNatives");
454 } catch (NoSuchMethodException ignore) {
455 try {
456 registerNatives = realClass.getDeclaredMethod("initIDs");
457 } catch (NoSuchMethodException ignored) {
458 }
459 }
460
461 if (registerNatives != null) {
462 try {
463 registerNatives.setAccessible(true);
464 registerNatives.invoke(null);
465 } catch (IllegalAccessException | InvocationTargetException ignore) {
466 }
467 }
468
469
470
471 }
472
473 public void addRedefinedClassWithNativeMethods(@NonNull String redefinedClassInternalName) {
474 redefinedClassesWithNativeMethods.add(redefinedClassInternalName.replace('/', '.'));
475 }
476 }