Skip to content

Commit 4b171c8

Browse files
committed
[GR-60093] Keep existing speciesData objects in cache at run-time
PullRequest: graal/19731
2 parents e28f8e4 + d9ff6fb commit 4b171c8

File tree

1 file changed

+43
-40
lines changed

1 file changed

+43
-40
lines changed

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

+43-40
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,16 @@
3333
import java.lang.reflect.InvocationTargetException;
3434
import java.lang.reflect.Member;
3535
import java.lang.reflect.Method;
36+
import java.util.HashSet;
3637
import java.util.Map;
37-
import java.util.Optional;
38+
import java.util.Set;
3839
import java.util.concurrent.ConcurrentHashMap;
3940
import java.util.function.Supplier;
4041

4142
import org.graalvm.nativeimage.hosted.RuntimeReflection;
4243

4344
import com.oracle.graal.pointsto.heap.ImageHeapScanner;
4445
import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
45-
import com.oracle.graal.pointsto.meta.AnalysisType;
4646
import com.oracle.svm.core.BuildPhaseProvider;
4747
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
4848
import com.oracle.svm.core.feature.InternalFeature;
@@ -107,6 +107,8 @@ public class MethodHandleFeature implements InternalFeature {
107107

108108
private MethodHandleInvokerRenamingSubstitutionProcessor substitutionProcessor;
109109

110+
private Set<Object> heapSpeciesData = new HashSet<>();
111+
110112
@Override
111113
public void duringSetup(DuringSetupAccess access) {
112114
Class<?> memberNameClass = ReflectionUtil.lookupClass("java.lang.invoke.MemberName");
@@ -149,6 +151,8 @@ public void duringSetup(DuringSetupAccess access) {
149151

150152
accessImpl.registerObjectReachableCallback(memberNameClass, (a1, member, reason) -> registerHeapMemberName((Member) member));
151153
accessImpl.registerObjectReachableCallback(MethodType.class, (a1, methodType, reason) -> registerHeapMethodType(methodType));
154+
Class<?> speciesDataClass = ReflectionUtil.lookupClass("java.lang.invoke.BoundMethodHandle$SpeciesData");
155+
accessImpl.registerObjectReachableCallback(speciesDataClass, (a1, speciesData, reason) -> registerHeapSpeciesData(speciesData));
152156
}
153157

154158
@Override
@@ -167,9 +171,6 @@ public void beforeAnalysis(BeforeAnalysisAccess a) {
167171
AnalysisMetaAccess metaAccess = access.getMetaAccess();
168172
ImageHeapScanner heapScanner = access.getUniverse().getHeapScanner();
169173

170-
// GR-60093: currently, Species_L is needed at runtime but not seen by the analysis
171-
access.registerAsInHeap(ReflectionUtil.lookupClass("java.lang.invoke.BoundMethodHandle$Species_L"));
172-
173174
access.registerFieldValueTransformer(
174175
ReflectionUtil.lookupField(ReflectionUtil.lookupClass("java.lang.invoke.ClassSpecializer"), "cache"),
175176
new FieldValueTransformerWithAvailability() {
@@ -194,18 +195,14 @@ public Object transform(Object receiver, Object originalValue) {
194195
ConcurrentHashMap<Object, Object> originalMap = (ConcurrentHashMap<Object, Object>) originalValue;
195196
ConcurrentHashMap<Object, Object> filteredMap = new ConcurrentHashMap<>();
196197
originalMap.forEach((key, speciesData) -> {
197-
if (isSpeciesTypeInstantiated(speciesData)) {
198+
if (heapSpeciesData.contains(speciesData)) {
198199
filteredMap.put(key, speciesData);
199200
}
200201
});
202+
/* No uses of heapSpeciesData should be needed after this point. */
203+
heapSpeciesData = null;
201204
return filteredMap;
202205
}
203-
204-
private boolean isSpeciesTypeInstantiated(Object speciesData) {
205-
Class<?> speciesClass = ReflectionUtil.readField(SPECIES_DATA_CLASS, "speciesCode", speciesData);
206-
Optional<AnalysisType> analysisType = metaAccess.optionalLookupJavaType(speciesClass);
207-
return analysisType.isPresent() && analysisType.get().isInstantiated();
208-
}
209206
});
210207
access.registerFieldValueTransformer(
211208
ReflectionUtil.lookupField(ReflectionUtil.lookupClass("java.lang.invoke.DirectMethodHandle"), "ACCESSOR_FORMS"),
@@ -214,36 +211,37 @@ private boolean isSpeciesTypeInstantiated(Object speciesData) {
214211
ReflectionUtil.lookupField(ReflectionUtil.lookupClass("java.lang.invoke.MethodType"), "internTable"),
215212
(receiver, originalValue) -> runtimeMethodTypeInternTable);
216213

214+
FieldValueTransformerWithAvailability methodHandleArrayTransformer = new FieldValueTransformerWithAvailability() {
215+
@Override
216+
public boolean isAvailable() {
217+
return BuildPhaseProvider.isHostedUniverseBuilt();
218+
}
219+
220+
@Override
221+
@SuppressWarnings("unchecked")
222+
public Object transform(Object receiver, Object originalValue) {
223+
MethodHandle[] originalArray = (MethodHandle[]) originalValue;
224+
MethodHandle[] filteredArray = new MethodHandle[originalArray.length];
225+
for (int i = 0; i < originalArray.length; i++) {
226+
MethodHandle handle = originalArray[i];
227+
if (handle != null && heapScanner.isObjectReachable(handle)) {
228+
filteredArray[i] = handle;
229+
}
230+
}
231+
return filteredArray;
232+
}
233+
};
234+
217235
/*
218-
* SpeciesData.transformHelpers is a lazily initialized cache of MethodHandle objects. We do
219-
* not want to make a MethodHandle reachable just because the image builder initialized the
220-
* cache, so we filter out unreachable objects. This also solves the problem when late image
221-
* heap re-scanning after static analysis would see a method handle that was not yet cached
222-
* during static analysis, in which case image building would fail because new types would
223-
* be made reachable after analysis.
236+
* SpeciesData.transformHelpers and MethodHandleImpl.ARRAYS are lazily initialized caches of
237+
* MethodHandle objects. We do not want to make a MethodHandle reachable just because the
238+
* image builder initialized a cache, so we filter out unreachable objects. This also solves
239+
* the problem when late image heap re-scanning after static analysis would see a method
240+
* handle that was not yet cached during static analysis, in which case image building would
241+
* fail because new types would be made reachable after analysis.
224242
*/
225-
access.registerFieldValueTransformer(
226-
ReflectionUtil.lookupField(ReflectionUtil.lookupClass("java.lang.invoke.ClassSpecializer$SpeciesData"), "transformHelpers"),
227-
new FieldValueTransformerWithAvailability() {
228-
@Override
229-
public boolean isAvailable() {
230-
return BuildPhaseProvider.isHostedUniverseBuilt();
231-
}
232-
233-
@Override
234-
@SuppressWarnings("unchecked")
235-
public Object transform(Object receiver, Object originalValue) {
236-
MethodHandle[] originalArray = (MethodHandle[]) originalValue;
237-
MethodHandle[] filteredArray = new MethodHandle[originalArray.length];
238-
for (int i = 0; i < originalArray.length; i++) {
239-
MethodHandle handle = originalArray[i];
240-
if (handle != null && heapScanner.isObjectReachable(handle)) {
241-
filteredArray[i] = handle;
242-
}
243-
}
244-
return filteredArray;
245-
}
246-
});
243+
access.registerFieldValueTransformer(ReflectionUtil.lookupField(ReflectionUtil.lookupClass("java.lang.invoke.ClassSpecializer$SpeciesData"), "transformHelpers"), methodHandleArrayTransformer);
244+
access.registerFieldValueTransformer(ReflectionUtil.lookupField(ReflectionUtil.lookupClass("java.lang.invoke.MethodHandleImpl"), "ARRAYS"), methodHandleArrayTransformer);
247245

248246
if (JavaVersionUtil.JAVA_SPEC >= 24) {
249247
/*
@@ -417,6 +415,11 @@ public void registerHeapMemberName(Member memberName) {
417415
}
418416
}
419417

418+
public void registerHeapSpeciesData(Object speciesData) {
419+
VMError.guarantee(heapSpeciesData != null, "The collected SpeciesData objects have already been processed.");
420+
heapSpeciesData.add(speciesData);
421+
}
422+
420423
@Override
421424
public void duringAnalysis(DuringAnalysisAccess a) {
422425
DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a;

0 commit comments

Comments
 (0)