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.faking;
7   
8   import edu.umd.cs.findbugs.annotations.NonNull;
9   import edu.umd.cs.findbugs.annotations.Nullable;
10  
11  import java.lang.instrument.ClassDefinition;
12  import java.lang.reflect.Proxy;
13  import java.lang.reflect.Type;
14  
15  import mockit.MockUp;
16  import mockit.asm.classes.ClassReader;
17  import mockit.internal.BaseClassModifier;
18  import mockit.internal.ClassFile;
19  import mockit.internal.startup.Startup;
20  import mockit.internal.state.CachedClassfiles;
21  import mockit.internal.state.TestRun;
22  
23  public final class FakeClassSetup {
24      @NonNull
25      final Class<?> realClass;
26      @Nullable
27      private ClassReader rcReader;
28      @NonNull
29      private final FakeMethods fakeMethods;
30      @NonNull
31      final MockUp<?> fake;
32      private final boolean forStartupFake;
33  
34      public FakeClassSetup(@NonNull Class<?> realClass, @NonNull Class<?> classToFake, @Nullable Type fakedType,
35              @NonNull MockUp<?> fake) {
36          this(realClass, classToFake, fakedType, fake, null);
37      }
38  
39      FakeClassSetup(@NonNull Class<?> realClass, @Nullable Type fakedType, @NonNull MockUp<?> fake,
40              @Nullable byte[] realClassCode) {
41          this(realClass, realClass, fakedType, fake, realClassCode);
42      }
43  
44      FakeClassSetup(@NonNull Class<?> realClass, @NonNull Class<?> classToFake, @Nullable Type fakedType,
45              @NonNull MockUp<?> fake, @Nullable byte[] realClassCode) {
46          this.realClass = classToFake;
47          this.fake = fake;
48          forStartupFake = Startup.initializing;
49          rcReader = realClassCode == null ? null : new ClassReader(realClassCode);
50          fakeMethods = new FakeMethods(realClass, fakedType);
51          collectFakeMethods();
52          registerFakeClassAndItsStates();
53      }
54  
55      private void collectFakeMethods() {
56          Class<?> fakeClass = fake.getClass();
57          new FakeMethodCollector(fakeMethods).collectFakeMethods(fakeClass);
58      }
59  
60      private void registerFakeClassAndItsStates() {
61          fakeMethods.registerFakeStates(fake, forStartupFake);
62  
63          FakeClasses fakeClasses = TestRun.getFakeClasses();
64  
65          if (forStartupFake) {
66              fakeClasses.addFake(fakeMethods.getFakeClassInternalName(), fake);
67          } else {
68              fakeClasses.addFake(fake);
69          }
70      }
71  
72      void redefineMethodsInGeneratedClass() {
73          byte[] modifiedClassFile = modifyRealClass(realClass);
74  
75          if (modifiedClassFile != null) {
76              applyClassModifications(realClass, modifiedClassFile);
77          }
78      }
79  
80      public void redefineMethods() {
81          @Nullable
82          Class<?> classToModify = realClass;
83  
84          while (classToModify != null && fakeMethods.hasUnusedFakes()) {
85              byte[] modifiedClassFile = modifyRealClass(classToModify);
86  
87              if (modifiedClassFile != null) {
88                  applyClassModifications(classToModify, modifiedClassFile);
89              }
90  
91              Class<?> superClass = classToModify.getSuperclass();
92              classToModify = superClass == Object.class || superClass == Proxy.class ? null : superClass;
93              rcReader = null;
94          }
95  
96      }
97  
98      @Nullable
99      private byte[] modifyRealClass(@NonNull Class<?> classToModify) {
100         if (rcReader == null) {
101             rcReader = ClassFile.createReaderFromLastRedefinitionIfAny(classToModify);
102         }
103 
104         FakedClassModifier modifier = new FakedClassModifier(rcReader, classToModify, fake, fakeMethods);
105         rcReader.accept(modifier);
106 
107         return modifier.wasModified() ? modifier.toByteArray() : null;
108     }
109 
110     @NonNull
111     BaseClassModifier createClassModifier(@NonNull ClassReader cr) {
112         return new FakedClassModifier(cr, realClass, fake, fakeMethods);
113     }
114 
115     void applyClassModifications(@NonNull Class<?> classToModify, @NonNull byte[] modifiedClassFile) {
116         ClassDefinition classDef = new ClassDefinition(classToModify, modifiedClassFile);
117         Startup.redefineMethods(classDef);
118 
119         if (forStartupFake) {
120             CachedClassfiles.addClassfile(classToModify, modifiedClassFile);
121         } else {
122             String fakeClassDesc = fakeMethods.getFakeClassInternalName();
123             TestRun.mockFixture().addRedefinedClass(fakeClassDesc, classDef);
124         }
125     }
126 }