1
2
3
4
5 package mockit.internal.classGeneration;
6
7 import edu.umd.cs.findbugs.annotations.NonNull;
8 import edu.umd.cs.findbugs.annotations.Nullable;
9
10 import java.lang.reflect.Type;
11
12 import mockit.asm.classes.ClassReader;
13 import mockit.asm.classes.ClassVisitor;
14 import mockit.internal.ClassFile;
15 import mockit.internal.util.ClassLoad;
16 import mockit.internal.util.GeneratedClasses;
17 import mockit.internal.util.Utilities;
18
19
20
21
22 public abstract class ImplementationClass<T> {
23 @NonNull
24 protected final Class<?> sourceClass;
25 @NonNull
26 protected String generatedClassName;
27 @Nullable
28 private byte[] generatedBytecode;
29
30 protected ImplementationClass(@NonNull Type mockedType) {
31 this(Utilities.getClassType(mockedType));
32 }
33
34 protected ImplementationClass(@NonNull Class<?> mockedClass) {
35 this(mockedClass, GeneratedClasses.getNameForGeneratedClass(mockedClass, null));
36 }
37
38 protected ImplementationClass(@NonNull Class<?> sourceClass, @NonNull String desiredClassName) {
39 this.sourceClass = sourceClass;
40 generatedClassName = desiredClassName;
41 }
42
43 @NonNull
44 public final Class<T> generateClass() {
45 ClassReader classReader = ClassFile.createReaderOrGetFromCache(sourceClass);
46
47 ClassVisitor modifier = createMethodBodyGenerator(classReader);
48 classReader.accept(modifier);
49
50 return defineNewClass(modifier);
51 }
52
53 @NonNull
54 protected abstract ClassVisitor createMethodBodyGenerator(@NonNull ClassReader cr);
55
56 @NonNull
57 private Class<T> defineNewClass(@NonNull ClassVisitor modifier) {
58 final ClassLoader parentLoader = ClassLoad.getClassLoaderWithAccess(sourceClass);
59 final byte[] modifiedClassfile = modifier.toByteArray();
60
61 try {
62 @SuppressWarnings("unchecked")
63 Class<T> generatedClass = (Class<T>) new ClassLoader(parentLoader) {
64 @Override
65 protected Class<?> findClass(String name) throws ClassNotFoundException {
66 if (!name.equals(generatedClassName)) {
67 return parentLoader.loadClass(name);
68 }
69
70 return defineClass(name, modifiedClassfile, 0, modifiedClassfile.length);
71 }
72 }.findClass(generatedClassName);
73 generatedBytecode = modifiedClassfile;
74
75 return generatedClass;
76 } catch (ClassNotFoundException e) {
77 throw new RuntimeException("Unable to define class: " + generatedClassName, e);
78 }
79 }
80
81 @Nullable
82 public final byte[] getGeneratedBytecode() {
83 return generatedBytecode;
84 }
85 }