1
2
3
4
5
6 package mockit.internal.expectations.transformation;
7
8 import edu.umd.cs.findbugs.annotations.NonNull;
9 import edu.umd.cs.findbugs.annotations.Nullable;
10
11 import java.lang.instrument.ClassFileTransformer;
12 import java.security.ProtectionDomain;
13
14 import mockit.asm.classes.ClassReader;
15 import mockit.asm.classes.ClassVisitor;
16 import mockit.asm.classes.ClassWriter;
17 import mockit.asm.classes.WrappingClassVisitor;
18 import mockit.asm.methods.MethodVisitor;
19 import mockit.asm.methods.MethodWriter;
20 import mockit.internal.util.ClassNaming;
21 import mockit.internal.util.VisitInterruptedException;
22
23 public final class ExpectationsTransformer implements ClassFileTransformer {
24 private static final String BASE_CLASSES = "mockit/Expectations mockit/Verifications mockit/VerificationsInOrder mockit/FullVerifications";
25
26 @Nullable
27 @Override
28 public byte[] transform(@Nullable ClassLoader loader, @NonNull String className,
29 @Nullable Class<?> classBeingRedefined, @Nullable ProtectionDomain protectionDomain,
30 @NonNull byte[] classfileBuffer) {
31 if (classBeingRedefined == null && protectionDomain != null && className != null) {
32 boolean anonymousClass = ClassNaming.isAnonymousClass(className);
33
34 if (anonymousClass && !isJMockitClass(className) && !className.startsWith("org/junit/")) {
35 ClassReader cr = new ClassReader(classfileBuffer);
36 String superClassName = cr.getSuperName();
37
38 if (!BASE_CLASSES.contains(superClassName)) {
39 return null;
40 }
41
42 return modifyInvocationsSubclass(cr, className);
43 }
44 }
45
46 return null;
47 }
48
49 private static boolean isJMockitClass(@NonNull String classDesc) {
50 return classDesc.startsWith("mockit/") && (classDesc.startsWith("mockit/internal/")
51 || classDesc.startsWith("mockit/coverage/") || classDesc.startsWith("mockit/integration/"));
52 }
53
54 @Nullable
55 private static byte[] modifyInvocationsSubclass(@NonNull ClassReader cr, @NonNull final String classDesc) {
56 ClassWriter cw = new ClassWriter(cr);
57
58 ClassVisitor modifier = new WrappingClassVisitor(cw) {
59 @Override
60 public MethodVisitor visitMethod(int access, @NonNull String name, @NonNull String desc,
61 @Nullable String signature, @Nullable String[] exceptions) {
62 MethodWriter mw = cw.visitMethod(access, name, desc, signature, exceptions);
63
64 if (!"<init>".equals(name)) {
65 return mw;
66 }
67
68 return new InvocationBlockModifier(mw, classDesc);
69 }
70 };
71
72 try {
73 cr.accept(modifier);
74 return modifier.toByteArray();
75 } catch (VisitInterruptedException ignore) {
76 } catch (Throwable e) {
77 e.printStackTrace();
78 }
79
80 return null;
81 }
82 }