View Javadoc
1   /*
2    * MIT License
3    * Copyright (c) 2006-2025 JMockit developers
4    * See LICENSE file for full license text.
5    */
6   package mockit.internal.capturing;
7   
8   import static mockit.internal.util.GeneratedClasses.isExternallyGeneratedSubclass;
9   import static mockit.internal.util.GeneratedClasses.isGeneratedClass;
10  
11  import edu.umd.cs.findbugs.annotations.NonNull;
12  import edu.umd.cs.findbugs.annotations.Nullable;
13  
14  import java.security.ProtectionDomain;
15  
16  import org.checkerframework.checker.index.qual.NonNegative;
17  
18  final class CapturedType {
19      private static final ProtectionDomain JMOCKIT_DOMAIN = CapturedType.class.getProtectionDomain();
20  
21      @NonNull
22      final Class<?> baseType;
23  
24      CapturedType(@NonNull Class<?> baseType) {
25          this.baseType = baseType;
26      }
27  
28      boolean isToBeCaptured(@NonNull Class<?> aClass) {
29          if (aClass == baseType || aClass.isArray() || !baseType.isAssignableFrom(aClass)
30                  || extendsJMockitBaseType(aClass)) {
31              return false;
32          }
33  
34          return !aClass.isInterface() && !isNotToBeCaptured(aClass.getProtectionDomain(), aClass.getName());
35      }
36  
37      @SuppressWarnings("UnnecessaryFullyQualifiedName")
38      private static boolean extendsJMockitBaseType(@NonNull Class<?> aClass) {
39          return mockit.MockUp.class.isAssignableFrom(aClass) || mockit.Expectations.class.isAssignableFrom(aClass)
40                  || mockit.Verifications.class.isAssignableFrom(aClass)
41                  || mockit.Delegate.class.isAssignableFrom(aClass);
42      }
43  
44      static boolean isNotToBeCaptured(@Nullable ProtectionDomain pd, @NonNull String classNameOrDesc) {
45          return pd == JMOCKIT_DOMAIN || classNameOrDesc.endsWith("Test")
46                  || isNonEligibleInternalJDKClass(classNameOrDesc) || isNonEligibleStandardJavaClass(classNameOrDesc)
47                  || isNonEligibleClassFromIDERuntime(classNameOrDesc)
48                  || isNonEligibleClassFromThirdPartyLibrary(classNameOrDesc) || isGeneratedClass(classNameOrDesc)
49                  || isExternallyGeneratedSubclass(classNameOrDesc);
50      }
51  
52      private static boolean isNonEligibleInternalJDKClass(@NonNull String classNameOrDesc) {
53          return classNameOrDesc.startsWith("jdk/")
54                  || classNameOrDesc.startsWith("sun") && !hasSubPackage(classNameOrDesc, 4, "management")
55                  || classNameOrDesc.startsWith("com") && hasSubPackage(classNameOrDesc, 4, "sun")
56                          && !hasSubPackages(classNameOrDesc, 8, "proxy org");
57      }
58  
59      private static boolean isNonEligibleStandardJavaClass(@NonNull String classNameOrDesc) {
60          return classNameOrDesc.startsWith("java") && !hasSubPackage(classNameOrDesc, 10, "concurrent");
61      }
62  
63      private static boolean isNonEligibleClassFromIDERuntime(@NonNull String classNameOrDesc) {
64          return classNameOrDesc.startsWith("com") && hasSubPackage(classNameOrDesc, 4, "intellij");
65      }
66  
67      private static boolean isNonEligibleClassFromThirdPartyLibrary(@NonNull String classNameOrDesc) {
68          return classNameOrDesc.startsWith("junit") || classNameOrDesc.startsWith("org")
69                  && hasSubPackages(classNameOrDesc, 4, "junit testng hamcrest gradle");
70      }
71  
72      private static boolean hasSubPackage(@NonNull String nameOrDesc, @NonNegative int offset,
73              @NonNull String subPackage) {
74          return nameOrDesc.regionMatches(offset, subPackage, 0, subPackage.length());
75      }
76  
77      private static boolean hasSubPackages(@NonNull String nameOrDesc, @NonNegative int offset,
78              @NonNull String subPackages) {
79          int subPackageStart = 0;
80          int subPackageEnd;
81  
82          do {
83              subPackageEnd = subPackages.indexOf(' ', subPackageStart);
84              int subPackageLength = (subPackageEnd > 0 ? subPackageEnd : subPackages.length()) - subPackageStart;
85  
86              if (nameOrDesc.regionMatches(offset, subPackages, subPackageStart, subPackageLength)) {
87                  return true;
88              }
89  
90              subPackageStart = subPackageEnd + 1;
91          } while (subPackageEnd > 0);
92  
93          return false;
94      }
95  }