Skip to content

Commit 7e8431b

Browse files
committed
Use image heap scanner to find method handle objects that are actually reachable.
1 parent 84a53a1 commit 7e8431b

File tree

3 files changed

+50
-43
lines changed

3 files changed

+50
-43
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VarHandleFeature.java

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
package com.oracle.svm.core.jdk;
2626

2727
import java.lang.invoke.MethodHandle;
28+
import java.lang.invoke.VarHandle;
2829
import java.lang.reflect.Field;
2930
import java.lang.reflect.Modifier;
3031
import java.util.HashMap;
@@ -76,11 +77,10 @@
7677
* unsafe accessed so that our static analysis is correct, and 2) recompute the field offsets from
7778
* the hosted offsets to the runtime offsets. Luckily, we have all information to reconstruct the
7879
* original {@link Field} (see {@link #findVarHandleField}). The registration for unsafe access
79-
* happens in an object replacer: the method {@link #processVarHandle} is called for every object
80-
* (and therefore every VarHandle) that is reachable in the image heap. The field offset
81-
* recomputations are registered for all classes manually (a bit of code duplication on our side),
82-
* but all recomputations use the same custom field value recomputation handler:
83-
* {@link VarHandleFieldOffsetComputer}.
80+
* happens in {@link #processReachableHandle} which is called for every relevant object once it
81+
* becomes reachable and so part of the image heap. The field offset recomputations are registered
82+
* for all classes manually (a bit of code duplication on our side), but all recomputations use the
83+
* same custom field value recomputation handler: {@link VarHandleFieldOffsetComputer}.
8484
*
8585
* For static fields, also the base of the Unsafe access needs to be changed to the static field
8686
* holder arrays defined in {@link StaticFieldsSupport}. We cannot do a recomputation to the actual
@@ -187,11 +187,6 @@ Field findVarHandleField(Object varHandle) {
187187
throw VMError.shouldNotReachHere("Could not find field referenced in VarHandle: " + type + ", offset = " + originalFieldOffset + ", isStatic = " + info.isStatic);
188188
}
189189

190-
@Override
191-
public void duringSetup(DuringSetupAccess access) {
192-
access.registerObjectReplacer(this::processVarHandle);
193-
}
194-
195190
@Override
196191
public void beforeAnalysis(BeforeAnalysisAccess access) {
197192
markAsUnsafeAccessed = access::registerAsUnsafeAccessed;
@@ -202,13 +197,21 @@ public void afterAnalysis(AfterAnalysisAccess access) {
202197
markAsUnsafeAccessed = null;
203198
}
204199

200+
public void registerHeapVarHandle(VarHandle varHandle) {
201+
processReachableHandle(varHandle);
202+
}
203+
204+
public void registerHeapMethodHandle(MethodHandle directMethodHandle) {
205+
processReachableHandle(directMethodHandle);
206+
}
207+
205208
/**
206209
* Register all fields accessed by a VarHandle for an instance field or a static field as unsafe
207210
* accessed, which is necessary for correctness of the static analysis. We want to process every
208-
* VarHandle only once, therefore we mark all VarHandle that were already processed in in
211+
* VarHandle only once, therefore we mark all VarHandle that were already processed in
209212
* {@link #processedVarHandles}.
210213
*/
211-
private Object processVarHandle(Object obj) {
214+
private Object processReachableHandle(Object obj) {
212215
VarHandleInfo info = infos.get(obj.getClass());
213216
if (info != null && processedVarHandles.putIfAbsent(obj, true) == null) {
214217
VMError.guarantee(markAsUnsafeAccessed != null, "New VarHandle found after static analysis");

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,12 @@
2424
*/
2525
package com.oracle.svm.hosted.heap;
2626

27+
import java.lang.invoke.MethodHandle;
28+
import java.lang.invoke.MethodType;
29+
import java.lang.invoke.VarHandle;
2730
import java.lang.reflect.Executable;
2831
import java.lang.reflect.Field;
32+
import java.lang.reflect.Member;
2933
import java.util.function.Consumer;
3034

3135
import org.graalvm.collections.EconomicMap;
@@ -44,11 +48,13 @@
4448
import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
4549
import com.oracle.svm.core.BuildPhaseProvider;
4650
import com.oracle.svm.core.hub.DynamicHub;
51+
import com.oracle.svm.core.jdk.VarHandleFeature;
4752
import com.oracle.svm.core.util.VMError;
4853
import com.oracle.svm.hosted.ImageClassLoader;
4954
import com.oracle.svm.hosted.ameta.AnalysisConstantReflectionProvider;
5055
import com.oracle.svm.hosted.ameta.ReadableJavaField;
5156
import com.oracle.svm.hosted.meta.HostedMetaAccess;
57+
import com.oracle.svm.hosted.methodhandles.MethodHandleFeature;
5258
import com.oracle.svm.hosted.reflect.ReflectionHostedSupport;
5359
import com.oracle.svm.util.ReflectionUtil;
5460

@@ -63,6 +69,10 @@ public class SVMImageHeapScanner extends ImageHeapScanner {
6369
private final Field economicMapImplEntriesField;
6470
private final Field economicMapImplHashArrayField;
6571
private final ReflectionHostedSupport reflectionSupport;
72+
private final Class<?> memberNameClass;
73+
private final MethodHandleFeature methodHandleSupport;
74+
private final Class<?> directMethodHandleClass;
75+
private final VarHandleFeature varHandleSupport;
6676

6777
@SuppressWarnings("this-escape")
6878
public SVMImageHeapScanner(BigBang bb, ImageHeap imageHeap, ImageClassLoader loader, AnalysisMetaAccess metaAccess,
@@ -74,6 +84,10 @@ public SVMImageHeapScanner(BigBang bb, ImageHeap imageHeap, ImageClassLoader loa
7484
economicMapImplHashArrayField = ReflectionUtil.lookupField(economicMapImpl, "hashArray");
7585
ImageSingletons.add(ImageHeapScanner.class, this);
7686
reflectionSupport = ImageSingletons.lookup(ReflectionHostedSupport.class);
87+
memberNameClass = getClass("java.lang.invoke.MemberName");
88+
methodHandleSupport = ImageSingletons.lookup(MethodHandleFeature.class);
89+
directMethodHandleClass = getClass("java.lang.invoke.DirectMethodHandle");
90+
varHandleSupport = ImageSingletons.lookup(VarHandleFeature.class);
7791
}
7892

7993
public static ImageHeapScanner instance() {
@@ -149,6 +163,14 @@ protected void onObjectReachable(ImageHeapConstant imageHeapConstant, ScanReason
149163
reflectionSupport.registerHeapReflectionExecutable(executable, reason);
150164
} else if (object instanceof DynamicHub hub) {
151165
reflectionSupport.registerHeapDynamicHub(hub, reason);
166+
} else if (object instanceof VarHandle varHandle) {
167+
varHandleSupport.registerHeapVarHandle(varHandle);
168+
} else if (directMethodHandleClass.isInstance(object)) {
169+
varHandleSupport.registerHeapMethodHandle((MethodHandle) object);
170+
} else if (object instanceof MethodType methodType) {
171+
methodHandleSupport.registerHeapMethodType(methodType);
172+
} else if (memberNameClass.isInstance(object)) {
173+
methodHandleSupport.registerHeapMemberName((Member) object);
152174
}
153175
}
154176
}

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

Lines changed: 13 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import java.lang.reflect.Array;
3131
import java.lang.reflect.Field;
3232
import java.lang.reflect.InvocationTargetException;
33+
import java.lang.reflect.Member;
3334
import java.lang.reflect.Method;
3435
import java.util.Iterator;
3536
import java.util.Optional;
@@ -39,7 +40,6 @@
3940

4041
import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
4142
import com.oracle.graal.pointsto.meta.AnalysisType;
42-
import com.oracle.svm.core.BuildPhaseProvider;
4343
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
4444
import com.oracle.svm.core.feature.InternalFeature;
4545
import com.oracle.svm.core.fieldvaluetransformer.FieldValueTransformerWithAvailability;
@@ -81,9 +81,6 @@
8181
@SuppressWarnings("unused")
8282
public class MethodHandleFeature implements InternalFeature {
8383

84-
private Class<?> memberNameClass;
85-
private Method memberNameGetDeclaringClass;
86-
private Method memberNameGetName;
8784
private Method memberNameIsMethod;
8885
private Method memberNameIsConstructor;
8986
private Method memberNameIsField;
@@ -106,9 +103,7 @@ public class MethodHandleFeature implements InternalFeature {
106103

107104
@Override
108105
public void duringSetup(DuringSetupAccess access) {
109-
memberNameClass = access.findClassByName("java.lang.invoke.MemberName");
110-
memberNameGetDeclaringClass = ReflectionUtil.lookupMethod(memberNameClass, "getDeclaringClass");
111-
memberNameGetName = ReflectionUtil.lookupMethod(memberNameClass, "getName");
106+
Class<?> memberNameClass = access.findClassByName("java.lang.invoke.MemberName");
112107
memberNameIsMethod = ReflectionUtil.lookupMethod(memberNameClass, "isMethod");
113108
memberNameIsConstructor = ReflectionUtil.lookupMethod(memberNameClass, "isConstructor");
114109
memberNameIsField = ReflectionUtil.lookupMethod(memberNameClass, "isField");
@@ -126,8 +121,6 @@ public void duringSetup(DuringSetupAccess access) {
126121
Class<?> concurrentWeakInternSetClass = access.findClassByName("java.lang.invoke.MethodType$ConcurrentWeakInternSet");
127122
runtimeMethodTypeInternTable = ReflectionUtil.newInstance(concurrentWeakInternSetClass);
128123
concurrentWeakInternSetAdd = ReflectionUtil.lookupMethod(concurrentWeakInternSetClass, "add", Object.class);
129-
130-
access.registerObjectReplacer(this::registerSeenObject);
131124
}
132125

133126
@Override
@@ -313,39 +306,28 @@ private static void registerVarHandleMethodsForReflection(FeatureAccess access,
313306
}
314307
}
315308

316-
private Object registerSeenObject(Object obj) {
317-
if (!BuildPhaseProvider.isAnalysisFinished()) {
318-
if (obj instanceof MethodType mt) {
319-
registerMethodType(mt);
320-
} else if (memberNameClass.isInstance(obj)) {
321-
/*
322-
* We used to register only MemberName instances which are reachable from a
323-
* MethodHandle, but optimizations can eliminate a MethodHandle object in code which
324-
* we might never see otherwise and leave a MemberName object behind which is still
325-
* used for a call. Therefore, we register all MemberName instances in the image
326-
* heap, which should only be reachable via MethodHandle objects, in any case.
327-
*/
328-
registerMemberName(obj);
329-
}
330-
}
331-
return obj;
332-
}
333-
334-
private void registerMethodType(MethodType methodType) {
309+
public void registerHeapMethodType(MethodType methodType) {
335310
try {
336311
concurrentWeakInternSetAdd.invoke(runtimeMethodTypeInternTable, methodType);
337312
} catch (ReflectiveOperationException e) {
338313
throw VMError.shouldNotReachHere(e);
339314
}
340315
}
341316

342-
private void registerMemberName(Object memberName) {
317+
public void registerHeapMemberName(Member memberName) {
318+
/*
319+
* We used to register only MemberName instances which are reachable from MethodHandle
320+
* objects, but code optimizations can eliminate a MethodHandle object which might never
321+
* become reachable otherwise and leave a MemberName object behind which is still used for a
322+
* call or field access. Therefore, we register all MemberName instances in the image heap,
323+
* which should only come into existence via MethodHandle objects, in any case.
324+
*/
343325
try {
344-
Class<?> declaringClass = (Class<?>) memberNameGetDeclaringClass.invoke(memberName);
326+
Class<?> declaringClass = memberName.getDeclaringClass();
345327
boolean isMethod = (boolean) memberNameIsMethod.invoke(memberName);
346328
boolean isConstructor = (boolean) memberNameIsConstructor.invoke(memberName);
347329
boolean isField = (boolean) memberNameIsField.invoke(memberName);
348-
String name = (isMethod || isField) ? (String) memberNameGetName.invoke(memberName) : null;
330+
String name = (isMethod || isField) ? memberName.getName() : null;
349331
Class<?>[] paramTypes = null;
350332
if (isMethod || isConstructor) {
351333
MethodType methodType = (MethodType) memberNameGetMethodType.invoke(memberName);

0 commit comments

Comments
 (0)