Skip to content

Commit 7765eb4

Browse files
committed
[GR-50962] Add object reachability hooks.
PullRequest: graal/16397
2 parents 86d5ec0 + ba32dfe commit 7765eb4

File tree

8 files changed

+111
-24
lines changed

8 files changed

+111
-24
lines changed

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,10 @@ protected void onObjectReachable(ImageHeapConstant imageHeapConstant, ScanReason
515515
AnalysisType objectType = metaAccess.lookupJavaType(imageHeapConstant);
516516
imageHeap.addReachableObject(objectType, imageHeapConstant);
517517

518+
AnalysisType type = imageHeapConstant.getType(metaAccess);
519+
Object object = bb.getSnippetReflectionProvider().asObject(Object.class, imageHeapConstant);
520+
type.notifyObjectReachable(universe.getConcurrentAnalysisAccess(), object);
521+
518522
markTypeInstantiated(objectType, reason);
519523
if (imageHeapConstant instanceof ImageHeapObjectArray imageHeapArray) {
520524
AnalysisType arrayType = imageHeapArray.getType(metaAccess);

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisField.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import com.oracle.graal.pointsto.infrastructure.OriginalFieldProvider;
4141
import com.oracle.graal.pointsto.infrastructure.WrappedJavaField;
4242
import com.oracle.graal.pointsto.typestate.TypeState;
43+
import com.oracle.graal.pointsto.util.AnalysisError;
4344
import com.oracle.graal.pointsto.util.AnalysisFuture;
4445
import com.oracle.graal.pointsto.util.AtomicUtils;
4546
import com.oracle.graal.pointsto.util.ConcurrentLightHashSet;
@@ -476,7 +477,7 @@ public void setPosition(int newPosition) {
476477
}
477478

478479
public int getPosition() {
479-
assert position != -1 : this;
480+
AnalysisError.guarantee(position != -1, "Unknown position for field %s", this);
480481
return position;
481482
}
482483

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import java.util.concurrent.ConcurrentMap;
3838
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
3939
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
40+
import java.util.function.BiConsumer;
4041
import java.util.function.Consumer;
4142
import java.util.function.Function;
4243

@@ -96,6 +97,9 @@ public abstract class AnalysisType extends AnalysisElement implements WrappedJav
9697
private static final AtomicReferenceFieldUpdater<AnalysisType, Object> instantiatedNotificationsUpdater = AtomicReferenceFieldUpdater
9798
.newUpdater(AnalysisType.class, Object.class, "typeInstantiatedNotifications");
9899

100+
private static final AtomicReferenceFieldUpdater<AnalysisType, Object> objectReachableCallbacksUpdater = AtomicReferenceFieldUpdater
101+
.newUpdater(AnalysisType.class, Object.class, "objectReachableCallbacks");
102+
99103
private static final AtomicReferenceFieldUpdater<AnalysisType, Object> isAllocatedUpdater = AtomicReferenceFieldUpdater
100104
.newUpdater(AnalysisType.class, Object.class, "isAllocated");
101105

@@ -219,6 +223,11 @@ public enum UsageKind {
219223
*/
220224
@SuppressWarnings("unused") private volatile Object typeInstantiatedNotifications;
221225

226+
/**
227+
* Contains callbacks that are executed when an object of this type is marked as reachable.
228+
*/
229+
@SuppressWarnings("unused") private volatile Object objectReachableCallbacks;
230+
222231
@SuppressWarnings("this-escape")
223232
public AnalysisType(AnalysisUniverse universe, ResolvedJavaType javaType, JavaKind storageKind, AnalysisType objectType, AnalysisType cloneableType) {
224233
this.universe = universe;
@@ -614,6 +623,21 @@ public Set<MethodOverrideReachableNotification> getOverrideReachabilityNotificat
614623
return ConcurrentLightHashMap.getOrDefault(this, overrideReachableNotificationsUpdater, method, Collections.emptySet());
615624
}
616625

626+
public <T> void registerObjectReachableCallback(BiConsumer<DuringAnalysisAccess, T> callback) {
627+
ConcurrentLightHashSet.addElement(this, objectReachableCallbacksUpdater, callback);
628+
/* Register the callback with already discovered subtypes too. */
629+
for (AnalysisType subType : subTypes) {
630+
/* Subtypes include this type itself. */
631+
if (!subType.equals(this)) {
632+
subType.registerObjectReachableCallback(callback);
633+
}
634+
}
635+
}
636+
637+
public <T> void notifyObjectReachable(DuringAnalysisAccess access, T object) {
638+
ConcurrentLightHashSet.forEach(this, objectReachableCallbacksUpdater, (BiConsumer<DuringAnalysisAccess, T> c) -> c.accept(access, object));
639+
}
640+
617641
public void registerInstantiatedCallback(Consumer<DuringAnalysisAccess> callback) {
618642
if (this.isInstantiated()) {
619643
/* If the type is already instantiated just trigger the callback. */
@@ -1013,6 +1037,11 @@ public Set<AnalysisType> getSubTypes() {
10131037

10141038
private void addSubType(AnalysisType subType) {
10151039
boolean result = this.subTypes.add(subType);
1040+
/* Register the object reachability callbacks with the newly discovered subtype. */
1041+
if (!subType.equals(this)) {
1042+
/* Subtypes include this type itself. */
1043+
ConcurrentLightHashSet.forEach(this, objectReachableCallbacksUpdater, (BiConsumer<DuringAnalysisAccess, Object> callback) -> subType.registerObjectReachableCallback(callback));
1044+
}
10161045
assert result : "Tried to add a " + subType + " which is already registered";
10171046
}
10181047

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateAccessor.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
import com.oracle.svm.core.hub.DynamicHub;
3434
import com.oracle.svm.core.jdk.InternalVMMethod;
3535

36+
import jdk.vm.ci.meta.ResolvedJavaMethod;
37+
3638
@InternalVMMethod
3739
public abstract class SubstrateAccessor {
3840
/**
@@ -43,6 +45,14 @@ public abstract class SubstrateAccessor {
4345
@Platforms(Platform.HOSTED_ONLY.class) //
4446
final Executable member;
4547

48+
/**
49+
* The actual target method. For @{@link SubstrateConstructorAccessor} this is a factory method.
50+
* For {@link SubstrateMethodAccessor} it can be the member itself or an adapter for caller
51+
* sensitive methods.
52+
*/
53+
@Platforms(Platform.HOSTED_ONLY.class) //
54+
final ResolvedJavaMethod targetMethod;
55+
4656
/**
4757
* The first-level function that is invoked. It expands the boxed Object[] signature to the
4858
* expanded real signature.
@@ -62,14 +72,24 @@ public abstract class SubstrateAccessor {
6272
final DynamicHub initializeBeforeInvoke;
6373

6474
@Platforms(Platform.HOSTED_ONLY.class)
65-
SubstrateAccessor(Executable member, CFunctionPointer expandSignature, CFunctionPointer directTarget, DynamicHub initializeBeforeInvoke) {
75+
SubstrateAccessor(Executable member, CFunctionPointer expandSignature, CFunctionPointer directTarget, ResolvedJavaMethod targetMethod, DynamicHub initializeBeforeInvoke) {
6676
this.member = member;
6777
this.expandSignature = expandSignature;
6878
this.directTarget = directTarget;
6979
this.initializeBeforeInvoke = initializeBeforeInvoke;
80+
this.targetMethod = targetMethod;
7081
}
7182

7283
public Executable getMember() {
7384
return member;
7485
}
86+
87+
public CFunctionPointer getExpandSignature() {
88+
return expandSignature;
89+
}
90+
91+
@Platforms(Platform.HOSTED_ONLY.class)
92+
public ResolvedJavaMethod getTargetMethod() {
93+
return targetMethod;
94+
}
7595
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateConstructorAccessor.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,13 @@
3434
import com.oracle.svm.core.reflect.ReflectionAccessorHolder.MethodInvokeFunctionPointer;
3535

3636
import jdk.internal.reflect.ConstructorAccessor;
37+
import jdk.vm.ci.meta.ResolvedJavaMethod;
3738

3839
@InternalVMMethod
3940
public final class SubstrateConstructorAccessor extends SubstrateAccessor implements ConstructorAccessor {
4041

41-
public SubstrateConstructorAccessor(Executable member, CFunctionPointer expandSignature, CFunctionPointer directTarget, DynamicHub initializeBeforeInvoke) {
42-
super(member, expandSignature, directTarget, initializeBeforeInvoke);
42+
public SubstrateConstructorAccessor(Executable member, CFunctionPointer expandSignature, CFunctionPointer directTarget, ResolvedJavaMethod targetMethod, DynamicHub initializeBeforeInvoke) {
43+
super(member, expandSignature, directTarget, targetMethod, initializeBeforeInvoke);
4344
}
4445

4546
@Override

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateMethodAccessor.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@
2626

2727
import java.lang.reflect.Executable;
2828

29-
import jdk.graal.compiler.nodes.NamedLocationIdentity;
30-
import jdk.graal.compiler.word.BarrieredAccess;
3129
import org.graalvm.nativeimage.Platform;
3230
import org.graalvm.nativeimage.Platforms;
3331
import org.graalvm.nativeimage.c.function.CFunctionPointer;
@@ -39,7 +37,10 @@
3937
import com.oracle.svm.core.reflect.ReflectionAccessorHolder.MethodInvokeFunctionPointerForCallerSensitiveAdapter;
4038
import com.oracle.svm.core.util.VMError;
4139

40+
import jdk.graal.compiler.nodes.NamedLocationIdentity;
41+
import jdk.graal.compiler.word.BarrieredAccess;
4242
import jdk.internal.reflect.MethodAccessor;
43+
import jdk.vm.ci.meta.ResolvedJavaMethod;
4344

4445
interface MethodAccessorJDK19 {
4546
Object invoke(Object obj, Object[] args, Class<?> caller);
@@ -61,9 +62,9 @@ public final class SubstrateMethodAccessor extends SubstrateAccessor implements
6162
private final boolean callerSensitiveAdapter;
6263

6364
@Platforms(Platform.HOSTED_ONLY.class)
64-
public SubstrateMethodAccessor(Executable member, Class<?> receiverType, CFunctionPointer expandSignature, CFunctionPointer directTarget, int vtableOffset, DynamicHub initializeBeforeInvoke,
65-
boolean callerSensitiveAdapter) {
66-
super(member, expandSignature, directTarget, initializeBeforeInvoke);
65+
public SubstrateMethodAccessor(Executable member, Class<?> receiverType, CFunctionPointer expandSignature, CFunctionPointer directTarget, ResolvedJavaMethod targetMethod, int vtableOffset,
66+
DynamicHub initializeBeforeInvoke, boolean callerSensitiveAdapter) {
67+
super(member, expandSignature, directTarget, targetMethod, initializeBeforeInvoke);
6768
this.receiverType = receiverType;
6869
this.vtableOffset = vtableOffset;
6970
this.callerSensitiveAdapter = callerSensitiveAdapter;

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,6 @@
4646
import java.util.stream.Collectors;
4747

4848
import org.graalvm.collections.Pair;
49-
import jdk.graal.compiler.debug.DebugContext;
50-
import jdk.graal.compiler.phases.util.Providers;
5149
import org.graalvm.nativeimage.AnnotationAccess;
5250
import org.graalvm.nativeimage.hosted.Feature;
5351
import org.graalvm.nativeimage.hosted.Feature.DuringAnalysisAccess;
@@ -89,6 +87,8 @@
8987
import com.oracle.svm.util.ReflectionUtil;
9088
import com.oracle.svm.util.UnsafePartitionKind;
9189

90+
import jdk.graal.compiler.debug.DebugContext;
91+
import jdk.graal.compiler.phases.util.Providers;
9292
import jdk.vm.ci.meta.MetaAccessProvider;
9393

9494
@SuppressWarnings("deprecation")
@@ -290,6 +290,16 @@ public void registerObjectReplacer(Function<Object, Object> replacer) {
290290
getUniverse().registerObjectReplacer(replacer);
291291
}
292292

293+
/**
294+
* Register a callback that is executed when an object of the specified type or any of its
295+
* subtypes is marked as reachable.
296+
*
297+
* @since 24.0
298+
*/
299+
public <T> void registerObjectReachableCallback(Class<T> clazz, BiConsumer<DuringAnalysisAccess, T> callback) {
300+
getMetaAccess().lookupJavaType(clazz).registerObjectReachableCallback(callback);
301+
}
302+
293303
public void registerSubstitutionProcessor(SubstitutionProcessor substitution) {
294304
getUniverse().registerFeatureSubstitution(substitution);
295305
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
import com.oracle.svm.hosted.FallbackFeature;
7171
import com.oracle.svm.hosted.FeatureImpl;
7272
import com.oracle.svm.hosted.FeatureImpl.BeforeCompilationAccessImpl;
73+
import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl;
7374
import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl;
7475
import com.oracle.svm.hosted.ImageClassLoader;
7576
import com.oracle.svm.hosted.analysis.Inflation;
@@ -163,6 +164,7 @@ public SubstrateAccessor getOrCreateAccessor(Executable member) {
163164
private SubstrateAccessor createAccessor(Executable member) {
164165
MethodPointer expandSignature;
165166
MethodPointer directTarget = null;
167+
AnalysisMethod targetMethod = null;
166168
DynamicHub initializeBeforeInvoke = null;
167169
if (member instanceof Method) {
168170
int vtableOffset = SubstrateMethodAccessor.STATICALLY_BOUND;
@@ -171,7 +173,7 @@ private SubstrateAccessor createAccessor(Executable member) {
171173

172174
if (member.getDeclaringClass() == MethodHandle.class && (member.getName().equals("invoke") || member.getName().equals("invokeExact"))) {
173175
/* Method handles must not be invoked via reflection. */
174-
expandSignature = register(analysisAccess.getMetaAccess().lookupJavaMethod(methodHandleInvokeErrorMethod), "Registered in " + ReflectionFeature.class);
176+
expandSignature = asMethodPointer(analysisAccess.getMetaAccess().lookupJavaMethod(methodHandleInvokeErrorMethod));
175177
} else {
176178
Method target = (Method) member;
177179
try {
@@ -184,18 +186,17 @@ private SubstrateAccessor createAccessor(Executable member) {
184186
throw VMError.shouldNotReachHere(ex);
185187
}
186188
expandSignature = createExpandSignatureMethod(target, callerSensitiveAdapter);
187-
AnalysisMethod targetMethod = analysisAccess.getMetaAccess().lookupJavaMethod(target);
189+
targetMethod = analysisAccess.getMetaAccess().lookupJavaMethod(target);
188190
/*
189191
* The SubstrateMethodAccessor is also used for the implementation of MethodHandle
190192
* that are created to do an invokespecial. So non-abstract instance methods have
191193
* both a directTarget and a vtableOffset.
192194
*/
193195
if (!targetMethod.isAbstract()) {
194-
directTarget = register(targetMethod, "Reflection target, registered in " + ReflectionFeature.class);
196+
directTarget = asMethodPointer(targetMethod);
195197
}
196198
if (!targetMethod.canBeStaticallyBound()) {
197199
vtableOffset = SubstrateMethodAccessor.OFFSET_NOT_YET_COMPUTED;
198-
analysisAccess.registerAsRoot(targetMethod, false, "Accessor method for reflection, registered in " + ReflectionFeature.class);
199200
}
200201
VMError.guarantee(directTarget != null || vtableOffset != SubstrateMethodAccessor.STATICALLY_BOUND, "Must have either a directTarget or a vtableOffset");
201202
if (!targetMethod.isStatic()) {
@@ -205,7 +206,7 @@ private SubstrateAccessor createAccessor(Executable member) {
205206
initializeBeforeInvoke = analysisAccess.getHostVM().dynamicHub(targetMethod.getDeclaringClass());
206207
}
207208
}
208-
return new SubstrateMethodAccessor(member, receiverType, expandSignature, directTarget, vtableOffset, initializeBeforeInvoke, callerSensitiveAdapter);
209+
return new SubstrateMethodAccessor(member, receiverType, expandSignature, directTarget, targetMethod, vtableOffset, initializeBeforeInvoke, callerSensitiveAdapter);
209210

210211
} else {
211212
Class<?> holder = member.getDeclaringClass();
@@ -216,31 +217,30 @@ private SubstrateAccessor createAccessor(Executable member) {
216217
* an interface, array, or primitive type, but we are defensive and throw the
217218
* exception in that case too.
218219
*/
219-
expandSignature = register(analysisAccess.getMetaAccess().lookupJavaMethod(newInstanceErrorMethod), "Registered in " + ReflectionFeature.class);
220+
expandSignature = asMethodPointer(analysisAccess.getMetaAccess().lookupJavaMethod(newInstanceErrorMethod));
220221
} else {
221222
expandSignature = createExpandSignatureMethod(member, false);
222223
AnalysisMethod constructor = analysisAccess.getMetaAccess().lookupJavaMethod(member);
223-
AnalysisMethod factoryMethod = FactoryMethodSupport.singleton().lookup(analysisAccess.getMetaAccess(), constructor, false);
224-
directTarget = register(factoryMethod, "Factory method, registered in " + ReflectionFeature.class);
224+
targetMethod = FactoryMethodSupport.singleton().lookup(analysisAccess.getMetaAccess(), constructor, false);
225+
directTarget = asMethodPointer(targetMethod);
225226
if (!constructor.getDeclaringClass().isInitialized()) {
226227
initializeBeforeInvoke = analysisAccess.getHostVM().dynamicHub(constructor.getDeclaringClass());
227228
}
228229
}
229-
return new SubstrateConstructorAccessor(member, expandSignature, directTarget, initializeBeforeInvoke);
230+
return new SubstrateConstructorAccessor(member, expandSignature, directTarget, targetMethod, initializeBeforeInvoke);
230231
}
231232
}
232233

233234
private MethodPointer createExpandSignatureMethod(Executable member, boolean callerSensitiveAdapter) {
234235
return expandSignatureMethods.computeIfAbsent(new SignatureKey(member, callerSensitiveAdapter), signatureKey -> {
235236
ResolvedJavaMethod prototype = analysisAccess.getMetaAccess().lookupJavaMethod(callerSensitiveAdapter ? invokePrototypeForCallerSensitiveAdapter : invokePrototype).getWrapped();
236-
return register(new ReflectionExpandSignatureMethod("invoke_" + signatureKey.uniqueShortName(), prototype, signatureKey.isStatic, signatureKey.argTypes, signatureKey.returnKind,
237-
signatureKey.callerSensitiveAdapter), "Registered in " + ReflectionFeature.class);
237+
return asMethodPointer(new ReflectionExpandSignatureMethod("invoke_" + signatureKey.uniqueShortName(), prototype, signatureKey.isStatic, signatureKey.argTypes, signatureKey.returnKind,
238+
signatureKey.callerSensitiveAdapter));
238239
});
239240
}
240241

241-
private MethodPointer register(ResolvedJavaMethod method, String reason) {
242+
private MethodPointer asMethodPointer(ResolvedJavaMethod method) {
242243
AnalysisMethod aMethod = method instanceof AnalysisMethod ? (AnalysisMethod) method : analysisAccess.getUniverse().lookup(method);
243-
analysisAccess.registerAsRoot(aMethod, true, reason);
244244
return new MethodPointer(aMethod);
245245
}
246246

@@ -273,6 +273,27 @@ public void duringSetup(DuringSetupAccess a) {
273273
for (Class<?> primitiveClass : PRIMITIVE_CLASSES) {
274274
ClassForNameSupport.registerNegativeQuery(primitiveClass.getName());
275275
}
276+
277+
access.registerObjectReachableCallback(SubstrateAccessor.class, ReflectionFeature::onAccessorReachable);
278+
}
279+
280+
private static void onAccessorReachable(DuringAnalysisAccess a, SubstrateAccessor accessor) {
281+
DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a;
282+
283+
String reason = "Registered in " + ReflectionFeature.class;
284+
ResolvedJavaMethod expandSignatureMethod = ((MethodPointer) accessor.getExpandSignature()).getMethod();
285+
access.registerAsRoot((AnalysisMethod) expandSignatureMethod, true, reason);
286+
287+
ResolvedJavaMethod targetMethod = accessor.getTargetMethod();
288+
if (targetMethod != null) {
289+
if (!targetMethod.isAbstract()) {
290+
access.registerAsRoot((AnalysisMethod) targetMethod, true, reason);
291+
}
292+
/* If the accessor can be used for a virtual call, register virtual root method. */
293+
if (accessor instanceof SubstrateMethodAccessor mAccessor && mAccessor.getVTableOffset() != SubstrateMethodAccessor.STATICALLY_BOUND) {
294+
access.registerAsRoot((AnalysisMethod) targetMethod, false, reason);
295+
}
296+
}
276297
}
277298

278299
@Override

0 commit comments

Comments
 (0)