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.reflection;
7   
8   import edu.umd.cs.findbugs.annotations.NonNull;
9   import edu.umd.cs.findbugs.annotations.Nullable;
10  
11  import java.lang.reflect.ParameterizedType;
12  import java.lang.reflect.Proxy;
13  import java.lang.reflect.Type;
14  import java.lang.reflect.TypeVariable;
15  import java.util.ArrayList;
16  import java.util.List;
17  
18  /**
19   * This marker interface exists only to guarantee that JMockit can get the bytecode definition of each Proxy class it
20   * creates through <code>java.lang.reflect.Proxy</code>. If such a class is created before JMockit is initialized, its
21   * bytecode won't be stored in JMockit's cache. And since the JDK uses an internal cache for proxy classes, it won't
22   * create a new one, therefore not going through the ProxyRegistrationTransformer. So, by always implementing this
23   * additional interface, we can guarantee a new proxy class will be created when JMockit first requests it for a given
24   * interface.
25   */
26  public interface EmptyProxy {
27      final class Impl {
28          private Impl() {
29          }
30  
31          @NonNull
32          public static <E> E newEmptyProxy(@Nullable ClassLoader loader, @NonNull Type... interfacesToBeProxied) {
33              List<Class<?>> interfaces = new ArrayList<>();
34  
35              for (Type type : interfacesToBeProxied) {
36                  addInterface(interfaces, type);
37              }
38  
39              if (loader == null) {
40                  // noinspection AssignmentToMethodParameter
41                  loader = interfaces.get(0).getClassLoader();
42              }
43  
44              if (loader == EmptyProxy.class.getClassLoader()) {
45                  interfaces.add(EmptyProxy.class);
46              }
47  
48              Class<?>[] interfacesArray = interfaces.toArray(new Class<?>[interfaces.size()]);
49  
50              // noinspection unchecked
51              return (E) Proxy.newProxyInstance(loader, interfacesArray, MockInvocationHandler.INSTANCE);
52          }
53  
54          private static void addInterface(@NonNull List<Class<?>> interfaces, @NonNull Type type) {
55              if (type instanceof Class<?>) {
56                  interfaces.add((Class<?>) type);
57              } else if (type instanceof ParameterizedType) {
58                  ParameterizedType paramType = (ParameterizedType) type;
59                  interfaces.add((Class<?>) paramType.getRawType());
60              } else if (type instanceof TypeVariable) {
61                  TypeVariable<?> typeVar = (TypeVariable<?>) type;
62                  addBoundInterfaces(interfaces, typeVar.getBounds());
63              }
64          }
65  
66          private static void addBoundInterfaces(@NonNull List<Class<?>> interfaces, @NonNull Type[] bounds) {
67              for (Type bound : bounds) {
68                  addInterface(interfaces, bound);
69              }
70          }
71      }
72  }