From d9ff6fb1330387cd77fcee6668b48066bbe73eb5 Mon Sep 17 00:00:00 2001 From: Loic Ottet Date: Tue, 7 Jan 2025 09:55:50 +0100 Subject: [PATCH] Keep existing speciesData objects in cache at run-time --- .../methodhandles/MethodHandleFeature.java | 83 ++++++++++--------- 1 file changed, 43 insertions(+), 40 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleFeature.java index 060b7703240f..083f88a14c80 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleFeature.java @@ -33,8 +33,9 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Member; import java.lang.reflect.Method; +import java.util.HashSet; import java.util.Map; -import java.util.Optional; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; @@ -42,7 +43,6 @@ import com.oracle.graal.pointsto.heap.ImageHeapScanner; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; -import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.svm.core.BuildPhaseProvider; import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; @@ -107,6 +107,8 @@ public class MethodHandleFeature implements InternalFeature { private MethodHandleInvokerRenamingSubstitutionProcessor substitutionProcessor; + private Set heapSpeciesData = new HashSet<>(); + @Override public void duringSetup(DuringSetupAccess access) { Class memberNameClass = ReflectionUtil.lookupClass("java.lang.invoke.MemberName"); @@ -149,6 +151,8 @@ public void duringSetup(DuringSetupAccess access) { accessImpl.registerObjectReachableCallback(memberNameClass, (a1, member, reason) -> registerHeapMemberName((Member) member)); accessImpl.registerObjectReachableCallback(MethodType.class, (a1, methodType, reason) -> registerHeapMethodType(methodType)); + Class speciesDataClass = ReflectionUtil.lookupClass("java.lang.invoke.BoundMethodHandle$SpeciesData"); + accessImpl.registerObjectReachableCallback(speciesDataClass, (a1, speciesData, reason) -> registerHeapSpeciesData(speciesData)); } @Override @@ -167,9 +171,6 @@ public void beforeAnalysis(BeforeAnalysisAccess a) { AnalysisMetaAccess metaAccess = access.getMetaAccess(); ImageHeapScanner heapScanner = access.getUniverse().getHeapScanner(); - // GR-60093: currently, Species_L is needed at runtime but not seen by the analysis - access.registerAsInHeap(ReflectionUtil.lookupClass("java.lang.invoke.BoundMethodHandle$Species_L")); - access.registerFieldValueTransformer( ReflectionUtil.lookupField(ReflectionUtil.lookupClass("java.lang.invoke.ClassSpecializer"), "cache"), new FieldValueTransformerWithAvailability() { @@ -194,18 +195,14 @@ public Object transform(Object receiver, Object originalValue) { ConcurrentHashMap originalMap = (ConcurrentHashMap) originalValue; ConcurrentHashMap filteredMap = new ConcurrentHashMap<>(); originalMap.forEach((key, speciesData) -> { - if (isSpeciesTypeInstantiated(speciesData)) { + if (heapSpeciesData.contains(speciesData)) { filteredMap.put(key, speciesData); } }); + /* No uses of heapSpeciesData should be needed after this point. */ + heapSpeciesData = null; return filteredMap; } - - private boolean isSpeciesTypeInstantiated(Object speciesData) { - Class speciesClass = ReflectionUtil.readField(SPECIES_DATA_CLASS, "speciesCode", speciesData); - Optional analysisType = metaAccess.optionalLookupJavaType(speciesClass); - return analysisType.isPresent() && analysisType.get().isInstantiated(); - } }); access.registerFieldValueTransformer( ReflectionUtil.lookupField(ReflectionUtil.lookupClass("java.lang.invoke.DirectMethodHandle"), "ACCESSOR_FORMS"), @@ -214,36 +211,37 @@ private boolean isSpeciesTypeInstantiated(Object speciesData) { ReflectionUtil.lookupField(ReflectionUtil.lookupClass("java.lang.invoke.MethodType"), "internTable"), (receiver, originalValue) -> runtimeMethodTypeInternTable); + FieldValueTransformerWithAvailability methodHandleArrayTransformer = new FieldValueTransformerWithAvailability() { + @Override + public boolean isAvailable() { + return BuildPhaseProvider.isHostedUniverseBuilt(); + } + + @Override + @SuppressWarnings("unchecked") + public Object transform(Object receiver, Object originalValue) { + MethodHandle[] originalArray = (MethodHandle[]) originalValue; + MethodHandle[] filteredArray = new MethodHandle[originalArray.length]; + for (int i = 0; i < originalArray.length; i++) { + MethodHandle handle = originalArray[i]; + if (handle != null && heapScanner.isObjectReachable(handle)) { + filteredArray[i] = handle; + } + } + return filteredArray; + } + }; + /* - * SpeciesData.transformHelpers is a lazily initialized cache of MethodHandle objects. We do - * not want to make a MethodHandle reachable just because the image builder initialized the - * cache, so we filter out unreachable objects. This also solves the problem when late image - * heap re-scanning after static analysis would see a method handle that was not yet cached - * during static analysis, in which case image building would fail because new types would - * be made reachable after analysis. + * SpeciesData.transformHelpers and MethodHandleImpl.ARRAYS are lazily initialized caches of + * MethodHandle objects. We do not want to make a MethodHandle reachable just because the + * image builder initialized a cache, so we filter out unreachable objects. This also solves + * the problem when late image heap re-scanning after static analysis would see a method + * handle that was not yet cached during static analysis, in which case image building would + * fail because new types would be made reachable after analysis. */ - access.registerFieldValueTransformer( - ReflectionUtil.lookupField(ReflectionUtil.lookupClass("java.lang.invoke.ClassSpecializer$SpeciesData"), "transformHelpers"), - new FieldValueTransformerWithAvailability() { - @Override - public boolean isAvailable() { - return BuildPhaseProvider.isHostedUniverseBuilt(); - } - - @Override - @SuppressWarnings("unchecked") - public Object transform(Object receiver, Object originalValue) { - MethodHandle[] originalArray = (MethodHandle[]) originalValue; - MethodHandle[] filteredArray = new MethodHandle[originalArray.length]; - for (int i = 0; i < originalArray.length; i++) { - MethodHandle handle = originalArray[i]; - if (handle != null && heapScanner.isObjectReachable(handle)) { - filteredArray[i] = handle; - } - } - return filteredArray; - } - }); + access.registerFieldValueTransformer(ReflectionUtil.lookupField(ReflectionUtil.lookupClass("java.lang.invoke.ClassSpecializer$SpeciesData"), "transformHelpers"), methodHandleArrayTransformer); + access.registerFieldValueTransformer(ReflectionUtil.lookupField(ReflectionUtil.lookupClass("java.lang.invoke.MethodHandleImpl"), "ARRAYS"), methodHandleArrayTransformer); if (JavaVersionUtil.JAVA_SPEC >= 24) { /* @@ -417,6 +415,11 @@ public void registerHeapMemberName(Member memberName) { } } + public void registerHeapSpeciesData(Object speciesData) { + VMError.guarantee(heapSpeciesData != null, "The collected SpeciesData objects have already been processed."); + heapSpeciesData.add(speciesData); + } + @Override public void duringAnalysis(DuringAnalysisAccess a) { DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a;