Skip to content

Commit 307d68a

Browse files
committed
Filter StringConcatFactory.CACHE to only include reachable types in the image
1 parent c31b058 commit 307d68a

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;
@@ -234,6 +236,52 @@ public Object transform(Object receiver, Object originalValue) {
234236
}
235237
});
236238

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+
237285
/*
238286
* Retrieve all six basic types from the java.lang.invoke.LambdaForm$BasicType class (void,
239287
* int, long, float, double, Object) and invoke the

0 commit comments

Comments
 (0)