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