|
26 | 26 |
|
27 | 27 | import java.lang.reflect.InvocationHandler; |
28 | 28 | import java.util.Arrays; |
29 | | -import java.util.Map; |
30 | | -import java.util.concurrent.ConcurrentHashMap; |
31 | 29 | import java.util.regex.Pattern; |
32 | 30 |
|
| 31 | +import org.graalvm.collections.EconomicMap; |
| 32 | +import org.graalvm.collections.EconomicSet; |
| 33 | +import org.graalvm.collections.Equivalence; |
33 | 34 | import org.graalvm.compiler.debug.GraalError; |
34 | 35 | import org.graalvm.nativeimage.Platform; |
35 | 36 | import org.graalvm.nativeimage.Platforms; |
36 | 37 | import org.graalvm.nativeimage.hosted.RuntimeClassInitialization; |
37 | 38 | import org.graalvm.nativeimage.hosted.RuntimeReflection; |
38 | 39 |
|
| 40 | +import com.oracle.svm.core.SubstrateUtil; |
39 | 41 | import com.oracle.svm.core.configure.ConfigurationFiles; |
40 | 42 | import com.oracle.svm.core.hub.DynamicHub; |
41 | 43 | import com.oracle.svm.core.hub.PredefinedClassesSupport; |
42 | 44 | import com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry; |
43 | 45 | import com.oracle.svm.core.option.SubstrateOptionsParser; |
| 46 | +import com.oracle.svm.core.util.ImageHeapMap; |
44 | 47 | import com.oracle.svm.core.util.VMError; |
45 | 48 | import com.oracle.svm.util.ClassUtil; |
46 | 49 | import com.oracle.svm.util.LogUtils; |
@@ -82,71 +85,78 @@ public String toString() { |
82 | 85 | } |
83 | 86 | } |
84 | 87 |
|
85 | | - private final Map<ProxyCacheKey, Object> proxyCache; |
| 88 | + private final EconomicSet<Class<?>> hostedProxyClasses = EconomicSet.create(Equivalence.IDENTITY); |
| 89 | + private final EconomicMap<ProxyCacheKey, Object> proxyCache = ImageHeapMap.create(); |
86 | 90 |
|
| 91 | + @Platforms(Platform.HOSTED_ONLY.class) |
87 | 92 | public DynamicProxySupport() { |
88 | | - this.proxyCache = new ConcurrentHashMap<>(); |
89 | 93 | } |
90 | 94 |
|
91 | | - @Platforms(Platform.HOSTED_ONLY.class) |
92 | 95 | @Override |
93 | | - public void addProxyClass(Class<?>... interfaces) { |
| 96 | + @Platforms(Platform.HOSTED_ONLY.class) |
| 97 | + public synchronized void addProxyClass(Class<?>... interfaces) { |
94 | 98 | /* |
95 | 99 | * Make a defensive copy of the interfaces array to protect against the caller modifying the |
96 | 100 | * array. |
97 | 101 | */ |
98 | | - final Class<?>[] intfs = interfaces.clone(); |
| 102 | + Class<?>[] intfs = interfaces.clone(); |
99 | 103 | ProxyCacheKey key = new ProxyCacheKey(intfs); |
100 | 104 |
|
101 | | - proxyCache.computeIfAbsent(key, k -> { |
102 | | - try { |
103 | | - Class<?> clazz = createProxyClassFromImplementedInterfaces(intfs); |
104 | | - |
105 | | - boolean isPredefinedProxy = Arrays.stream(interfaces).anyMatch(PredefinedClassesSupport::isPredefined); |
| 105 | + if (!proxyCache.containsKey(key)) { |
| 106 | + proxyCache.put(key, createProxyClass(intfs)); |
| 107 | + } |
| 108 | + } |
106 | 109 |
|
107 | | - if (isPredefinedProxy) { |
108 | | - /* |
109 | | - * Treat the proxy as a predefined class so that we can set its class loader to |
110 | | - * the loader passed at runtime. If one of the interfaces is a predefined class, |
111 | | - * this can be required so that the classes can actually see each other |
112 | | - * according to the runtime class loader hierarchy. |
113 | | - */ |
114 | | - PredefinedClassesSupport.registerClass(clazz); |
115 | | - RuntimeClassInitialization.initializeAtRunTime(clazz); |
116 | | - } |
| 110 | + @Platforms(Platform.HOSTED_ONLY.class) |
| 111 | + private Object createProxyClass(Class<?>[] interfaces) { |
| 112 | + try { |
| 113 | + Class<?> clazz = createProxyClassFromImplementedInterfaces(interfaces); |
117 | 114 |
|
| 115 | + boolean isPredefinedProxy = Arrays.stream(interfaces).anyMatch(PredefinedClassesSupport::isPredefined); |
| 116 | + if (isPredefinedProxy) { |
118 | 117 | /* |
119 | | - * The constructor of the generated dynamic proxy class that takes a |
120 | | - * `java.lang.reflect.InvocationHandler` argument, i.e., the one reflectively |
121 | | - * invoked by `java.lang.reflect.Proxy.newProxyInstance(ClassLoader, Class<?>[], |
122 | | - * InvocationHandler)`, is registered for reflection so that dynamic proxy instances |
123 | | - * can be allocated at run time. |
| 118 | + * Treat the proxy as a predefined class so that we can set its class loader to the |
| 119 | + * loader passed at runtime. If one of the interfaces is a predefined class, this |
| 120 | + * can be required so that the classes can actually see each other according to the |
| 121 | + * runtime class loader hierarchy. |
124 | 122 | */ |
125 | | - RuntimeReflection.register(ReflectionUtil.lookupConstructor(clazz, InvocationHandler.class)); |
| 123 | + PredefinedClassesSupport.registerClass(clazz); |
| 124 | + RuntimeClassInitialization.initializeAtRunTime(clazz); |
| 125 | + } |
126 | 126 |
|
127 | | - /* |
128 | | - * The proxy class reflectively looks up the methods of the interfaces it implements |
129 | | - * to pass a Method object to InvocationHandler. |
130 | | - */ |
131 | | - for (Class<?> intf : intfs) { |
132 | | - RuntimeReflection.register(intf.getMethods()); |
133 | | - } |
134 | | - |
135 | | - return clazz; |
136 | | - } catch (Throwable t) { |
137 | | - LogUtils.warning("Could not create a proxy class from list of interfaces: %s. Reason: %s", Arrays.toString(interfaces), t.getMessage()); |
138 | | - return t; |
| 127 | + /* |
| 128 | + * The constructor of the generated dynamic proxy class that takes a |
| 129 | + * `java.lang.reflect.InvocationHandler` argument, i.e., the one reflectively invoked by |
| 130 | + * `java.lang.reflect.Proxy.newProxyInstance(ClassLoader, Class<?>[], |
| 131 | + * InvocationHandler)`, is registered for reflection so that dynamic proxy instances can |
| 132 | + * be allocated at run time. |
| 133 | + */ |
| 134 | + RuntimeReflection.register(ReflectionUtil.lookupConstructor(clazz, InvocationHandler.class)); |
| 135 | + |
| 136 | + /* |
| 137 | + * The proxy class reflectively looks up the methods of the interfaces it implements to |
| 138 | + * pass a Method object to InvocationHandler. |
| 139 | + */ |
| 140 | + for (Class<?> intf : interfaces) { |
| 141 | + RuntimeReflection.register(intf.getMethods()); |
139 | 142 | } |
140 | | - }); |
| 143 | + |
| 144 | + hostedProxyClasses.add(clazz); |
| 145 | + return clazz; |
| 146 | + } catch (Throwable t) { |
| 147 | + LogUtils.warning("Could not create a proxy class from list of interfaces: %s. Reason: %s", Arrays.toString(interfaces), t.getMessage()); |
| 148 | + return t; |
| 149 | + } |
141 | 150 | } |
142 | 151 |
|
143 | 152 | @Override |
| 153 | + @Platforms(Platform.HOSTED_ONLY.class) |
144 | 154 | public Class<?> createProxyClassForSerialization(Class<?>... interfaces) { |
145 | 155 | final Class<?>[] intfs = interfaces.clone(); |
146 | | - |
147 | 156 | return createProxyClassFromImplementedInterfaces(intfs); |
148 | 157 | } |
149 | 158 |
|
| 159 | + @Platforms(Platform.HOSTED_ONLY.class) |
150 | 160 | private static Class<?> createProxyClassFromImplementedInterfaces(Class<?>[] interfaces) { |
151 | 161 | return getJdkProxyClass(getCommonClassLoaderOrFail(null, interfaces), interfaces); |
152 | 162 | } |
@@ -228,7 +238,15 @@ private static void describeLoaderChain(StringBuilder b, ClassLoader loader) { |
228 | 238 |
|
229 | 239 | @Override |
230 | 240 | public boolean isProxyClass(Class<?> clazz) { |
231 | | - return proxyCache.containsValue(clazz); |
| 241 | + if (SubstrateUtil.HOSTED) { |
| 242 | + return isHostedProxyClass(clazz); |
| 243 | + } |
| 244 | + return DynamicHub.fromClass(clazz).isProxyClass(); |
| 245 | + } |
| 246 | + |
| 247 | + @Platforms(Platform.HOSTED_ONLY.class) |
| 248 | + private synchronized boolean isHostedProxyClass(Class<?> clazz) { |
| 249 | + return hostedProxyClasses.contains(clazz); |
232 | 250 | } |
233 | 251 |
|
234 | 252 | @SuppressWarnings("deprecation") |
|
0 commit comments