|
28 | 28 | import java.lang.invoke.MethodHandle;
|
29 | 29 | import java.lang.invoke.MethodType;
|
30 | 30 | import java.lang.invoke.VarHandle;
|
| 31 | +import java.lang.ref.SoftReference; |
31 | 32 | import java.lang.reflect.Field;
|
32 | 33 | import java.lang.reflect.InvocationTargetException;
|
33 | 34 | import java.lang.reflect.Member;
|
34 | 35 | import java.lang.reflect.Method;
|
| 36 | +import java.util.Map; |
35 | 37 | import java.util.Optional;
|
36 | 38 | import java.util.concurrent.ConcurrentHashMap;
|
37 | 39 | import java.util.function.Supplier;
|
@@ -234,6 +236,52 @@ public Object transform(Object receiver, Object originalValue) {
|
234 | 236 | }
|
235 | 237 | });
|
236 | 238 |
|
| 239 | + if (JavaVersionUtil.JAVA_SPEC >= 24) { |
| 240 | + /* |
| 241 | + * StringConcatFactory$InlineHiddenClassStrategy was added in JDK 24, as well as its |
| 242 | + * CACHE field, so there is no need to filter for previous JDK versions. |
| 243 | + */ |
| 244 | + Class<?> referencedKeyMapClazz = ReflectionUtil.lookupClass("jdk.internal.util.ReferencedKeyMap"); |
| 245 | + Method createMethod = ReflectionUtil.lookupMethod(referencedKeyMapClazz, "create", boolean.class, Supplier.class); |
| 246 | + Method concurrentHashMapSupplierMethod = ReflectionUtil.lookupMethod(referencedKeyMapClazz, "concurrentHashMapSupplier"); |
| 247 | + Class<?> methodHandlePair = ReflectionUtil.lookupClass("java.lang.invoke.StringConcatFactory$InlineHiddenClassStrategy$MethodHandlePair"); |
| 248 | + Method constructorGetter = ReflectionUtil.lookupMethod(methodHandlePair, "constructor"); |
| 249 | + Method concatenatorGetter = ReflectionUtil.lookupMethod(methodHandlePair, "concatenator"); |
| 250 | + |
| 251 | + /* |
| 252 | + * StringConcatFactory$InlineHiddenClassStrategy.CACHE is a cache like |
| 253 | + * SpeciesData.transformHelpers, so it needs a similar transformation for the same |
| 254 | + * reasons. |
| 255 | + */ |
| 256 | + access.registerFieldValueTransformer( |
| 257 | + ReflectionUtil.lookupField(ReflectionUtil.lookupClass("java.lang.invoke.StringConcatFactory$InlineHiddenClassStrategy"), "CACHE"), |
| 258 | + new FieldValueTransformerWithAvailability() { |
| 259 | + @Override |
| 260 | + public boolean isAvailable() { |
| 261 | + return BuildPhaseProvider.isHostedUniverseBuilt(); |
| 262 | + } |
| 263 | + |
| 264 | + @Override |
| 265 | + @SuppressWarnings("unchecked") |
| 266 | + public Object transform(Object receiver, Object originalValue) { |
| 267 | + Map<Object, SoftReference<Object>> cache = (Map<Object, SoftReference<Object>>) originalValue; |
| 268 | + Map<Object, Object> result = ReflectionUtil.invokeMethod(createMethod, null, true, ReflectionUtil.invokeMethod(concurrentHashMapSupplierMethod, null)); |
| 269 | + |
| 270 | + for (var entry : cache.entrySet()) { |
| 271 | + SoftReference<Object> value = entry.getValue(); |
| 272 | + Object object = value.get(); |
| 273 | + MethodHandle constructor = ReflectionUtil.invokeMethod(constructorGetter, object); |
| 274 | + MethodHandle concatenator = ReflectionUtil.invokeMethod(concatenatorGetter, object); |
| 275 | + if (constructor != null && concatenator != null && heapScanner.isObjectReachable(constructor) && heapScanner.isObjectReachable(concatenator)) { |
| 276 | + result.put(entry.getKey(), value); |
| 277 | + } |
| 278 | + } |
| 279 | + |
| 280 | + return result; |
| 281 | + } |
| 282 | + }); |
| 283 | + } |
| 284 | + |
237 | 285 | /*
|
238 | 286 | * Retrieve all six basic types from the java.lang.invoke.LambdaForm$BasicType class (void,
|
239 | 287 | * int, long, float, double, Object) and invoke the
|
|
0 commit comments