Skip to content

Commit 47da7f5

Browse files
committed
[GR-59809] Filter StringConcatFactory.CACHE to only include reachable types in the image
PullRequest: graal/19315
2 parents be67923 + 307d68a commit 47da7f5

File tree

1 file changed

+48
-0
lines changed

1 file changed

+48
-0
lines changed

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleFeature.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,12 @@
2828
import java.lang.invoke.MethodHandle;
2929
import java.lang.invoke.MethodType;
3030
import java.lang.invoke.VarHandle;
31+
import java.lang.ref.SoftReference;
3132
import java.lang.reflect.Field;
3233
import java.lang.reflect.InvocationTargetException;
3334
import java.lang.reflect.Member;
3435
import java.lang.reflect.Method;
36+
import java.util.Map;
3537
import java.util.Optional;
3638
import java.util.concurrent.ConcurrentHashMap;
3739
import java.util.function.Supplier;
@@ -240,6 +242,52 @@ public Object transform(Object receiver, Object originalValue) {
240242
}
241243
});
242244

245+
if (JavaVersionUtil.JAVA_SPEC >= 24) {
246+
/*
247+
* StringConcatFactory$InlineHiddenClassStrategy was added in JDK 24, as well as its
248+
* CACHE field, so there is no need to filter for previous JDK versions.
249+
*/
250+
Class<?> referencedKeyMapClazz = ReflectionUtil.lookupClass("jdk.internal.util.ReferencedKeyMap");
251+
Method createMethod = ReflectionUtil.lookupMethod(referencedKeyMapClazz, "create", boolean.class, Supplier.class);
252+
Method concurrentHashMapSupplierMethod = ReflectionUtil.lookupMethod(referencedKeyMapClazz, "concurrentHashMapSupplier");
253+
Class<?> methodHandlePair = ReflectionUtil.lookupClass("java.lang.invoke.StringConcatFactory$InlineHiddenClassStrategy$MethodHandlePair");
254+
Method constructorGetter = ReflectionUtil.lookupMethod(methodHandlePair, "constructor");
255+
Method concatenatorGetter = ReflectionUtil.lookupMethod(methodHandlePair, "concatenator");
256+
257+
/*
258+
* StringConcatFactory$InlineHiddenClassStrategy.CACHE is a cache like
259+
* SpeciesData.transformHelpers, so it needs a similar transformation for the same
260+
* reasons.
261+
*/
262+
access.registerFieldValueTransformer(
263+
ReflectionUtil.lookupField(ReflectionUtil.lookupClass("java.lang.invoke.StringConcatFactory$InlineHiddenClassStrategy"), "CACHE"),
264+
new FieldValueTransformerWithAvailability() {
265+
@Override
266+
public boolean isAvailable() {
267+
return BuildPhaseProvider.isHostedUniverseBuilt();
268+
}
269+
270+
@Override
271+
@SuppressWarnings("unchecked")
272+
public Object transform(Object receiver, Object originalValue) {
273+
Map<Object, SoftReference<Object>> cache = (Map<Object, SoftReference<Object>>) originalValue;
274+
Map<Object, Object> result = ReflectionUtil.invokeMethod(createMethod, null, true, ReflectionUtil.invokeMethod(concurrentHashMapSupplierMethod, null));
275+
276+
for (var entry : cache.entrySet()) {
277+
SoftReference<Object> value = entry.getValue();
278+
Object object = value.get();
279+
MethodHandle constructor = ReflectionUtil.invokeMethod(constructorGetter, object);
280+
MethodHandle concatenator = ReflectionUtil.invokeMethod(concatenatorGetter, object);
281+
if (constructor != null && concatenator != null && heapScanner.isObjectReachable(constructor) && heapScanner.isObjectReachable(concatenator)) {
282+
result.put(entry.getKey(), value);
283+
}
284+
}
285+
286+
return result;
287+
}
288+
});
289+
}
290+
243291
/*
244292
* Retrieve all six basic types from the java.lang.invoke.LambdaForm$BasicType class (void,
245293
* int, long, float, double, Object) and invoke the

0 commit comments

Comments
 (0)