1
2
3
4
5
6 package mockit.internal.state;
7
8 import edu.umd.cs.findbugs.annotations.NonNull;
9 import edu.umd.cs.findbugs.annotations.Nullable;
10
11 import java.lang.instrument.ClassFileTransformer;
12 import java.security.ProtectionDomain;
13 import java.util.HashMap;
14 import java.util.Map;
15 import java.util.WeakHashMap;
16
17 import mockit.internal.startup.Startup;
18
19
20
21
22
23
24
25
26
27
28
29 public final class CachedClassfiles implements ClassFileTransformer {
30 @NonNull
31 public static final CachedClassfiles INSTANCE = new CachedClassfiles();
32
33 @NonNull
34 private final Map<ClassLoader, Map<String, byte[]>> classLoadersAndClassfiles;
35 @Nullable
36 private Class<?> classBeingCached;
37
38 private CachedClassfiles() {
39 classLoadersAndClassfiles = new WeakHashMap<>(2);
40 }
41
42 @Nullable
43 @Override
44 public byte[] transform(@Nullable ClassLoader loader, String classDesc,
45 @Nullable Class<?> classBeingRedefinedOrRetransformed, @Nullable ProtectionDomain protectionDomain,
46 @NonNull byte[] classfileBuffer) {
47
48 if (classDesc != null && classBeingRedefinedOrRetransformed != null
49 && classBeingRedefinedOrRetransformed == classBeingCached) {
50 addClassfile(loader, classDesc, classfileBuffer);
51 classBeingCached = null;
52 }
53
54 return null;
55 }
56
57 private void addClassfile(@Nullable ClassLoader loader, @NonNull String classDesc, @NonNull byte[] classfile) {
58 Map<String, byte[]> classfiles = getClassfiles(loader);
59 classfiles.put(classDesc, classfile);
60 }
61
62 @NonNull
63 private Map<String, byte[]> getClassfiles(@Nullable ClassLoader loader) {
64 return classLoadersAndClassfiles.computeIfAbsent(loader, k -> new HashMap<>(100));
65 }
66
67 @Nullable
68 private byte[] findClassfile(@NonNull Class<?> aClass) {
69 String className = aClass.getName();
70
71
72 int p = className.indexOf('/');
73 if (p > 0) {
74 className = className.substring(0, p);
75 }
76
77 Map<String, byte[]> classfiles = getClassfiles(aClass.getClassLoader());
78 return classfiles.get(className.replace('.', '/'));
79 }
80
81 @Nullable
82 public static synchronized byte[] getClassfile(@NonNull String classDesc) {
83 return INSTANCE.findClassfile(classDesc);
84 }
85
86 @Nullable
87 private byte[] findClassfile(@NonNull String classDesc) {
88 byte[] classfile = null;
89
90 for (Map<String, byte[]> classfiles : classLoadersAndClassfiles.values()) {
91 classfile = classfiles.get(classDesc);
92
93 if (classfile != null) {
94 return classfile;
95 }
96 }
97
98 Class<?> desiredClass = Startup.getClassIfLoaded(classDesc);
99
100 if (desiredClass != null) {
101 classBeingCached = desiredClass;
102 Startup.retransformClass(desiredClass);
103 ClassLoader classLoader = desiredClass.getClassLoader();
104 classfile = INSTANCE.findClassfile(classLoader, classDesc);
105 }
106
107 return classfile;
108 }
109
110 @Nullable
111 private synchronized byte[] findClassfile(@Nullable ClassLoader loader, @NonNull String classDesc) {
112 Map<String, byte[]> classfiles = getClassfiles(loader);
113 return classfiles.get(classDesc);
114 }
115
116 @Nullable
117 public static synchronized byte[] getClassfile(@NonNull Class<?> aClass) {
118 byte[] cached = INSTANCE.findClassfile(aClass);
119 if (cached != null) {
120 return cached;
121 }
122
123 INSTANCE.classBeingCached = aClass;
124 Startup.retransformClass(aClass);
125 return INSTANCE.findClassfile(aClass);
126 }
127
128 @Nullable
129 public static byte[] getClassfile(@Nullable ClassLoader loader, @NonNull String internalClassName) {
130 return INSTANCE.findClassfile(loader, internalClassName);
131 }
132
133 public static void addClassfile(@NonNull Class<?> aClass, @NonNull byte[] classfile) {
134 INSTANCE.addClassfile(aClass.getClassLoader(), aClass.getName().replace('.', '/'), classfile);
135 }
136 }