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