View Javadoc
1   /*
2    * Copyright (c) 2006 JMockit developers
3    * This file is subject to the terms of the MIT license (see LICENSE.txt).
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   * Allows the creation of new implementation classes for interfaces and abstract classes.
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  }