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;
7   
8   import edu.umd.cs.findbugs.annotations.NonNull;
9   import edu.umd.cs.findbugs.annotations.Nullable;
10  
11  import java.io.File;
12  import java.io.FileInputStream;
13  import java.lang.reflect.InvocationHandler;
14  import java.net.URL;
15  import java.util.Hashtable;
16  import java.util.Vector;
17  import java.util.concurrent.locks.ReentrantLock;
18  import java.util.jar.JarEntry;
19  import java.util.jar.JarFile;
20  import java.util.jar.Manifest;
21  
22  import mockit.internal.expectations.mocking.MockedBridge;
23  import mockit.internal.faking.FakeBridge;
24  import mockit.internal.faking.FakeMethodBridge;
25  import mockit.internal.util.ClassLoad;
26  import mockit.internal.util.StackTrace;
27  
28  import org.checkerframework.checker.index.qual.NonNegative;
29  
30  public abstract class ClassLoadingBridge implements InvocationHandler {
31      private static final Object[] EMPTY_ARGS = {};
32      private static final ReentrantLock LOCK = new ReentrantLock();
33      private static boolean fieldsSet;
34      public static String hostJREClassName;
35  
36      public final String id;
37  
38      /**
39       * The instance is stored in a place directly accessible through the Java SE API, so that it can be recovered from
40       * any class loader.
41       */
42      protected ClassLoadingBridge(@NonNull String id) {
43          this.id = id;
44      }
45  
46      protected static boolean notToBeMocked(@Nullable Object instance, @NonNull String classDesc) {
47          return (instance == null && "java/lang/System".equals(classDesc)
48                  || instance != null && instanceOfClassThatParticipatesInClassLoading(instance.getClass()))
49                  && wasCalledDuringClassLoading();
50      }
51  
52      public static boolean instanceOfClassThatParticipatesInClassLoading(@NonNull Class<?> aClass) {
53          return aClass == File.class || aClass == URL.class || aClass == FileInputStream.class
54                  || aClass == Manifest.class || JarFile.class.isAssignableFrom(aClass)
55                  || JarEntry.class.isAssignableFrom(aClass) || Vector.class.isAssignableFrom(aClass)
56                  || Hashtable.class.isAssignableFrom(aClass);
57      }
58  
59      private static boolean wasCalledDuringClassLoading() {
60          if (LOCK.isHeldByCurrentThread()) {
61              return true;
62          }
63  
64          LOCK.lock();
65  
66          try {
67              StackTrace st = new StackTrace(new Throwable());
68              int n = st.getDepth();
69  
70              for (int i = 3; i < n; i++) {
71                  StackTraceElement ste = st.getElement(i);
72  
73                  if ("ClassLoader.java".equals(ste.getFileName())
74                          && "loadClass getResource loadLibrary".contains(ste.getMethodName())) {
75                      return true;
76                  }
77              }
78  
79              return false;
80          } finally {
81              LOCK.unlock();
82          }
83      }
84  
85      @NonNull
86      protected static Object[] extractArguments(@NonNegative int startingIndex, @NonNull Object[] args) {
87          if (args.length > startingIndex) {
88              Object[] targetMemberArgs = new Object[args.length - startingIndex];
89              System.arraycopy(args, startingIndex, targetMemberArgs, 0, targetMemberArgs.length);
90              return targetMemberArgs;
91          }
92  
93          return EMPTY_ARGS;
94      }
95  
96      @NonNull
97      static String getHostClassName() {
98          if (!fieldsSet) {
99              setBridgeFields();
100             fieldsSet = true;
101         }
102 
103         return hostJREClassName;
104     }
105 
106     private static void setBridgeFields() {
107         Class<?> hostClass = ClassLoad.loadByInternalName(hostJREClassName);
108         setBridgeField(hostClass, MockedBridge.MB);
109         setBridgeField(hostClass, FakeBridge.MB);
110         setBridgeField(hostClass, FakeMethodBridge.MB);
111     }
112 
113     private static void setBridgeField(@NonNull Class<?> hostClass, @NonNull ClassLoadingBridge bridge) {
114         try {
115             hostClass.getDeclaredField(bridge.id).set(null, bridge);
116         } catch (NoSuchFieldException ignore) {
117         } catch (IllegalAccessException e) {
118             throw new RuntimeException(e);
119         }
120     }
121 }