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.util;
7   
8   import edu.umd.cs.findbugs.annotations.NonNull;
9   import edu.umd.cs.findbugs.annotations.Nullable;
10  
11  import java.util.Map;
12  import java.util.concurrent.ConcurrentHashMap;
13  
14  import mockit.internal.state.TestRun;
15  
16  public final class ClassLoad {
17      public static final String OBJECT = "java/lang/Object";
18  
19      private static final ClassLoader THIS_CL = ClassLoad.class.getClassLoader();
20      private static final Map<String, Class<?>> LOADED_CLASSES = new ConcurrentHashMap<>();
21      private static final Map<String, String> SUPER_CLASSES = new ConcurrentHashMap<>();
22  
23      private ClassLoad() {
24      }
25  
26      public static void registerLoadedClass(@NonNull Class<?> aClass) {
27          LOADED_CLASSES.put(aClass.getName(), aClass);
28      }
29  
30      @NonNull
31      public static <T> Class<T> loadByInternalName(@NonNull String internalClassName) {
32          return loadClass(internalClassName.replace('/', '.'));
33      }
34  
35      @NonNull
36      public static <T> Class<T> loadClass(@NonNull String className) {
37          @Nullable
38          Class<?> loadedClass = LOADED_CLASSES.get(className);
39  
40          if (loadedClass == null) {
41              try {
42                  loadedClass = loadClassFromAClassLoader(className);
43              } catch (LinkageError e) {
44                  e.printStackTrace();
45                  throw e;
46              }
47          }
48  
49          // noinspection unchecked
50          return (Class<T>) loadedClass;
51      }
52  
53      @NonNull
54      private static Class<?> loadClassFromAClassLoader(@NonNull String className) {
55          Class<?> loadedClass = loadClass(null, className);
56  
57          if (loadedClass == null) {
58              if (className.startsWith("mockit.")) {
59                  loadedClass = loadClass(THIS_CL, className);
60              }
61  
62              if (loadedClass == null) {
63                  Class<?> testClass = TestRun.getCurrentTestClass();
64                  loadedClass = testClass == null ? null : loadClass(testClass.getClassLoader(), className);
65  
66                  if (loadedClass == null) {
67                      ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
68                      loadedClass = loadClass(contextCL, className);
69  
70                      if (loadedClass == null) {
71                          throw new IllegalArgumentException("No class with name \"" + className + "\" found");
72                      }
73                  }
74              }
75          }
76  
77          return loadedClass;
78      }
79  
80      @NonNull
81      public static <T> Class<T> loadClassAtStartup(@NonNull String className) {
82          ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
83          Class<?> loadedClass;
84  
85          try {
86              loadedClass = loadClass(contextCL, className);
87  
88              if (loadedClass == null) {
89                  loadedClass = loadClass(THIS_CL, className);
90  
91                  if (loadedClass == null) {
92                      throw new IllegalArgumentException("No class with name \"" + className + "\" found");
93                  }
94              }
95          } catch (LinkageError e) {
96              e.printStackTrace();
97              throw e;
98          }
99  
100         // noinspection unchecked
101         return (Class<T>) loadedClass;
102     }
103 
104     @Nullable
105     public static Class<?> loadClass(@Nullable ClassLoader loader, @NonNull String className) {
106         try {
107             return Class.forName(className, false, loader);
108         } catch (ClassNotFoundException ignore) {
109             return null;
110         }
111     }
112 
113     @NonNull
114     public static <T> Class<T> loadFromLoader(@Nullable ClassLoader loader, @NonNull String className) {
115         try {
116             // noinspection unchecked
117             return (Class<T>) Class.forName(className, false, loader);
118         } catch (ClassNotFoundException ignore) {
119             throw new IllegalArgumentException("No class with name \"" + className + "\" found");
120         }
121     }
122 
123     @Nullable
124     public static <T> Class<? extends T> searchTypeInClasspath(@NonNull String typeName) {
125         return searchTypeInClasspath(typeName, false);
126     }
127 
128     @Nullable
129     public static <T> Class<? extends T> searchTypeInClasspath(@NonNull String typeName, boolean initializeType) {
130         // noinspection OverlyBroadCatchBlock
131         try {
132             // noinspection unchecked
133             return (Class<? extends T>) Class.forName(typeName, initializeType, THIS_CL);
134         } catch (Throwable ignore) {
135             return null;
136         }
137     }
138 
139     public static void addSuperClass(@NonNull String classInternalName, @NonNull String superClassInternalName) {
140         SUPER_CLASSES.put(classInternalName.intern(), superClassInternalName.intern());
141     }
142 
143     @NonNull
144     public static String getSuperClass(@NonNull String classInternalName) {
145         String classDesc = classInternalName.intern();
146         String superName = SUPER_CLASSES.computeIfAbsent(classDesc, key -> {
147             Class<?> theClass = loadByInternalName(key);
148             Class<?> superClass = theClass.getSuperclass();
149             return superClass != null ? superClass.getName().replace('.', '/').intern() : null;
150         });
151         return superName == null ? OBJECT : superName;
152     }
153 
154     @Nullable
155     public static String whichIsSuperClass(@NonNull String internalClassName1, @NonNull String internalClassName2) {
156         String class1 = actualSuperClass(internalClassName1, internalClassName2);
157 
158         if (class1 != null) {
159             return class1;
160         }
161 
162         return actualSuperClass(internalClassName2, internalClassName1);
163     }
164 
165     @Nullable
166     private static String actualSuperClass(@NonNull String candidateSuperClass, @NonNull String candidateSubclass) {
167         String subclass = candidateSubclass;
168 
169         while (true) {
170             String superClass = getSuperClass(subclass);
171 
172             if (superClass.equals(OBJECT)) {
173                 return null;
174             }
175 
176             if (superClass.equals(candidateSuperClass)) {
177                 return candidateSuperClass;
178             }
179 
180             subclass = superClass;
181         }
182     }
183 
184     public static boolean isClassLoaderWithNoDirectAccess(@Nullable ClassLoader classLoader) {
185         return classLoader == null || classLoader != THIS_CL && classLoader.getParent() != THIS_CL;
186     }
187 
188     public static ClassLoader getClassLoaderWithAccess(@NonNull Class<?> classToBeAccessed) {
189         ClassLoader cl = classToBeAccessed.getClassLoader();
190         return isClassLoaderWithNoDirectAccess(cl) ? THIS_CL : cl;
191     }
192 }