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