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