1
2
3
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 }