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.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  }