View Javadoc
1   /*
2    * MIT License
3    * Copyright (c) 2006-2025 JMockit developers
4    * See LICENSE file for full license text.
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   * Allows the creation of new implementation classes for interfaces and abstract classes.
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  }