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