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