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