From 027d79b276a2be3bad227e980fa732f1cca9425c Mon Sep 17 00:00:00 2001
From: Josef Eisl
Date: Tue, 4 Nov 2025 10:02:55 +0100
Subject: [PATCH 1/7] svm: add FeatureImpl#findTypeByName
---
.../src/com/oracle/svm/hosted/FeatureImpl.java | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java
index b346ba36f3ef..b2bbe178b541 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java
@@ -243,6 +243,14 @@ public AnalysisMetaAccess getMetaAccess() {
return bb.getMetaAccess();
}
+ public AnalysisType findTypeByName(String className) {
+ Class> clazz = findClassByName(className);
+ if (clazz == null) {
+ return null;
+ }
+ return getMetaAccess().lookupJavaType(clazz);
+ }
+
public boolean isReachable(Class> clazz) {
return isReachable(getMetaAccess().lookupJavaType(clazz));
}
From 7c8bd29aa731222b50b9162fa75c16b04896de39 Mon Sep 17 00:00:00 2001
From: Josef Eisl
Date: Tue, 4 Nov 2025 13:07:12 +0100
Subject: [PATCH 2/7] svm: add FeatureImpl#findSubtypes
---
.../src/com/oracle/svm/hosted/FeatureImpl.java | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java
index b2bbe178b541..279c5e3a58c0 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java
@@ -251,6 +251,10 @@ public AnalysisType findTypeByName(String className) {
return getMetaAccess().lookupJavaType(clazz);
}
+ public List findSubtypes(AnalysisType baseClass) {
+ return imageClassLoader.findSubclasses(baseClass.getJavaClass(), false).stream().map(getMetaAccess()::lookupJavaType).toList();
+ }
+
public boolean isReachable(Class> clazz) {
return isReachable(getMetaAccess().lookupJavaType(clazz));
}
From e03fe095a8dc460fbb91c2bf1da0600d2002ecc3 Mon Sep 17 00:00:00 2001
From: Josef Eisl
Date: Tue, 4 Nov 2025 10:03:31 +0100
Subject: [PATCH 3/7] svm: add JVMCIReflectionUtil#getConstructors
---
.../src/com/oracle/svm/util/JVMCIReflectionUtil.java | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/JVMCIReflectionUtil.java b/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/JVMCIReflectionUtil.java
index 7a3954e870da..c6d7bfdcb016 100644
--- a/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/JVMCIReflectionUtil.java
+++ b/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/JVMCIReflectionUtil.java
@@ -32,6 +32,7 @@
import jdk.graal.compiler.debug.GraalError;
import jdk.vm.ci.meta.JavaType;
import jdk.vm.ci.meta.MetaAccessProvider;
+import jdk.vm.ci.meta.ModifiersProvider;
import jdk.vm.ci.meta.ResolvedJavaField;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;
@@ -134,6 +135,15 @@ public static ResolvedJavaMethod getDeclaredConstructor(MetaAccessProvider metaA
return getDeclaredConstructor(false, metaAccess, declaringClass, parameterTypes);
}
+ /**
+ * Gets the constructors declared by {@code declaringClass}. Like
+ * {@link Class#getConstructors()}, this only returns public constructors and does not consider
+ * super classes.
+ */
+ public static ResolvedJavaMethod[] getConstructors(ResolvedJavaType declaringClass) {
+ return Arrays.stream(declaringClass.getDeclaredConstructors(false)).filter(ModifiersProvider::isPublic).toArray(ResolvedJavaMethod[]::new);
+ }
+
private static ResolvedJavaMethod findMethod(ResolvedJavaType declaringClass, ResolvedJavaMethod[] methods, String name, ResolvedJavaType... parameterTypes) {
ResolvedJavaMethod res = null;
for (ResolvedJavaMethod m : methods) {
From fcf61bb2bd2776b96bf11b0130b4c7e77b911ff8 Mon Sep 17 00:00:00 2001
From: Josef Eisl
Date: Thu, 6 Nov 2025 15:06:39 +0100
Subject: [PATCH 4/7] svm: add JVMCIReflectionUtil#getResolvedReturnType()
---
.../oracle/svm/util/JVMCIReflectionUtil.java | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/JVMCIReflectionUtil.java b/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/JVMCIReflectionUtil.java
index c6d7bfdcb016..8800517f0305 100644
--- a/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/JVMCIReflectionUtil.java
+++ b/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/JVMCIReflectionUtil.java
@@ -24,6 +24,7 @@
*/
package com.oracle.svm.util;
+import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -158,7 +159,7 @@ private static ResolvedJavaMethod findMethod(ResolvedJavaType declaringClass, Re
if (res == null) {
res = m;
} else {
- throw new GraalError("More than one method with signature %s in %s", res.format("%h(%p)"), declaringClass.toClassName());
+ throw new GraalError("More than one method with signature %s in %s", res.format("%H.%n(%p)"), declaringClass.toClassName());
}
}
return res;
@@ -236,4 +237,18 @@ public static String getPackageName(ResolvedJavaType type) {
int dot = cn.lastIndexOf('.');
return (dot != -1) ? cn.substring(0, dot).intern() : "";
}
+
+ /**
+ * Gets the return type for a {@link ResolvedJavaMethod}. This is the same as calling
+ * {@link Method#getReturnType()} on the underlying method.
+ *
+ * @throws GraalError if the return type is not a {@link ResolvedJavaType}
+ */
+ public static ResolvedJavaType getResolvedReturnType(ResolvedJavaMethod m) {
+ JavaType returnType = m.getSignature().getReturnType(m.getDeclaringClass());
+ if (returnType instanceof ResolvedJavaType resolvedJavaType) {
+ return resolvedJavaType;
+ }
+ throw new GraalError("Method does not have a resolved return type: %s", m.format("%H.%n(%p)"));
+ }
}
From 8a3fc8788815307ae96878511bd9620bc3251fc1 Mon Sep 17 00:00:00 2001
From: Josef Eisl
Date: Thu, 30 Oct 2025 10:10:16 +0100
Subject: [PATCH 5/7] svm: migrate SubstrateGraphBuilderPlugins to
JVMCIReflection
---
.../SubstrateGraphBuilderPlugins.java | 65 +++++++++++--------
1 file changed, 38 insertions(+), 27 deletions(-)
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java
index f8c261280784..60dc46d9b35b 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java
@@ -45,7 +45,6 @@
import org.graalvm.nativeimage.c.function.CodePointer;
import org.graalvm.nativeimage.c.struct.SizeOf;
import org.graalvm.nativeimage.hosted.RuntimeProxyCreation;
-import org.graalvm.nativeimage.hosted.RuntimeReflection;
import org.graalvm.nativeimage.hosted.RuntimeSerialization;
import org.graalvm.word.Pointer;
import org.graalvm.word.PointerBase;
@@ -99,12 +98,14 @@
import com.oracle.svm.hosted.ReachabilityCallbackNode;
import com.oracle.svm.hosted.SharedArenaSupport;
import com.oracle.svm.hosted.code.SubstrateCompilationDirectives;
+import com.oracle.svm.hosted.dynamicaccess.JVMCIRuntimeReflection;
import com.oracle.svm.hosted.dynamicaccessinference.DynamicAccessInferenceLog;
import com.oracle.svm.hosted.dynamicaccessinference.StrictDynamicAccessInferenceFeature;
import com.oracle.svm.hosted.nodes.DeoptProxyNode;
import com.oracle.svm.hosted.nodes.ReadReservedRegister;
import com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor;
import com.oracle.svm.util.AnnotationUtil;
+import com.oracle.svm.util.JVMCIReflectionUtil;
import com.oracle.svm.util.OriginalClassProvider;
import com.oracle.svm.util.ReflectionUtil;
@@ -297,6 +298,13 @@ public static T asConstantObject(GraphBuilderContext b, Class type, Value
return StandardGraphBuilderPlugins.asConstantObject(b, type, node);
}
+ public static ResolvedJavaType asConstantType(GraphBuilderContext b, ValueNode node) {
+ if (node instanceof ConstantNode constantNode && constantNode.getValue() instanceof JavaConstant javaConstant && javaConstant.isNonNull()) {
+ return b.getConstantReflection().asJavaType(javaConstant);
+ }
+ return null;
+ }
+
public static int asConstantIntegerOrMinusOne(ValueNode node) {
if (node instanceof ConstantNode constantNode && constantNode.getValue() instanceof JavaConstant javaConstant) {
return javaConstant.asInt();
@@ -739,18 +747,18 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec
* them for reflection/unsafe access.
*/
private static void interceptUpdaterInvoke(GraphBuilderContext b, ValueNode tclassNode, ValueNode fieldNameNode) {
- Class> tclass = asConstantObject(b, Class.class, tclassNode);
+ ResolvedJavaType type = asConstantType(b, tclassNode);
String fieldName = asConstantObject(b, String.class, fieldNameNode);
- if (tclass != null && fieldName != null) {
+ if (type != null && fieldName != null) {
try {
- Field field = tclass.getDeclaredField(fieldName);
+ ResolvedJavaField field = JVMCIReflectionUtil.getDeclaredField(type, fieldName);
/*
* Register the holder class and the field for reflection. This also registers the
* field for unsafe access.
*/
- RuntimeReflection.register(tclass);
- RuntimeReflection.register(field);
- } catch (NoSuchFieldException e) {
+ JVMCIRuntimeReflection.register(type);
+ JVMCIRuntimeReflection.register(field);
+ } catch (NoSuchFieldError e) {
/*
* Ignore the exception. If the field does not exist, there will be an error at run
* time. That is then the same behavior as on HotSpot. The allocation of the
@@ -791,17 +799,15 @@ private static void registerUnsafePlugins(InvocationPlugins plugins) {
r.register(new RequiredInvocationPlugin("objectFieldOffset", Receiver.class, Class.class, String.class) {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode classNode, ValueNode nameNode) {
- Class> clazz = asConstantObject(b, Class.class, classNode);
+ ResolvedJavaType type = asConstantType(b, classNode);
String fieldName = asConstantObject(b, String.class, nameNode);
- if (clazz != null && fieldName != null) {
- Field targetField;
- try {
- targetField = clazz.getDeclaredField(fieldName);
- } catch (ReflectiveOperationException | LinkageError e) {
- return false;
+ if (type != null && fieldName != null) {
+ ResolvedJavaField targetField = JVMCIReflectionUtil.getDeclaredField(false, type, fieldName);
+ if (targetField != null) {
+ return processFieldOffset(b, receiver, false, targetField);
}
- return processFieldOffset(b, receiver, targetField, false);
}
+ /* A NullPointerException will be thrown at run time for this call. */
return false;
}
});
@@ -819,8 +825,10 @@ private static void registerUnsafePlugins(Registration r, boolean isSunMiscUnsaf
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode fieldNode) {
Field targetField = asConstantObject(b, Field.class, fieldNode);
if (targetField != null) {
- return processFieldOffset(b, receiver, targetField, isSunMiscUnsafe);
+ ResolvedJavaField resolvedJavaField = b.getMetaAccess().lookupJavaField(targetField);
+ return processFieldOffset(b, receiver, isSunMiscUnsafe, resolvedJavaField);
}
+ /* A NullPointerException will be thrown at run time for this call. */
return false;
}
});
@@ -840,8 +848,10 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode fieldNode) {
Field targetField = asConstantObject(b, Field.class, fieldNode);
if (targetField != null) {
- return processFieldOffset(b, receiver, targetField, isSunMiscUnsafe);
+ ResolvedJavaField resolvedJavaField = b.getMetaAccess().lookupJavaField(targetField);
+ return processFieldOffset(b, receiver, isSunMiscUnsafe, resolvedJavaField);
}
+ /* A NullPointerException will be thrown at run time for this call. */
return false;
}
});
@@ -869,8 +879,8 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec
});
}
- private static boolean processFieldOffset(GraphBuilderContext b, Receiver receiver, Field targetField, boolean isSunMiscUnsafe) {
- if (!isValidField(targetField, isSunMiscUnsafe)) {
+ private static boolean processFieldOffset(GraphBuilderContext b, Receiver receiver, boolean isSunMiscUnsafe, ResolvedJavaField resolvedJavaField) {
+ if (!isValidField(resolvedJavaField, isSunMiscUnsafe)) {
return false;
}
@@ -880,15 +890,11 @@ private static boolean processFieldOffset(GraphBuilderContext b, Receiver receiv
* The static analysis registers the field for unsafe access if the node remains in the
* graph until then.
*/
- b.addPush(JavaKind.Long, FieldOffsetNode.create(JavaKind.Long, b.getMetaAccess().lookupJavaField(targetField)));
+ b.addPush(JavaKind.Long, FieldOffsetNode.create(JavaKind.Long, resolvedJavaField));
return true;
}
- private static boolean isValidField(Field targetField, boolean isSunMiscUnsafe) {
- if (targetField == null) {
- /* A NullPointerException will be thrown at run time for this call. */
- return false;
- }
+ private static boolean isValidField(ResolvedJavaField targetField, boolean isSunMiscUnsafe) {
/*
* sun.misc.Unsafe performs a few more checks than jdk.internal.misc.Unsafe to explicitly
* disallow hidden classes and records.
@@ -897,13 +903,18 @@ private static boolean isValidField(Field targetField, boolean isSunMiscUnsafe)
}
private static boolean processStaticFieldBase(GraphBuilderContext b, Receiver receiver, Field targetField, boolean isSunMiscUnsafe) {
- if (!isValidField(targetField, isSunMiscUnsafe)) {
+ if (targetField == null) {
+ /* A NullPointerException will be thrown at run time for this call. */
+ return false;
+ }
+ ResolvedJavaField resolvedJavaField = b.getMetaAccess().lookupJavaField(targetField);
+ if (!isValidField(resolvedJavaField, isSunMiscUnsafe)) {
return false;
}
/* Emits a null-check for the otherwise unused receiver. */
receiver.get(true);
- b.addPush(JavaKind.Object, StaticFieldsSupport.createStaticFieldBaseNode(b.getMetaAccess().lookupJavaField(targetField)));
+ b.addPush(JavaKind.Object, StaticFieldsSupport.createStaticFieldBaseNode(resolvedJavaField));
return true;
}
From d2f2ae6a8e24bec2076be6d5e47e1109e8d11028 Mon Sep 17 00:00:00 2001
From: Josef Eisl
Date: Thu, 30 Oct 2025 12:04:26 +0100
Subject: [PATCH 6/7] svm: migrate Jmx*Feature to JVMCI reflection
---
.../svm/hosted/jdk/JmxClientFeature.java | 39 +++++-----
.../svm/hosted/jdk/JmxCommonFeature.java | 76 +++++++++++++++----
.../svm/hosted/jdk/JmxServerFeature.java | 20 ++---
3 files changed, 93 insertions(+), 42 deletions(-)
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JmxClientFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JmxClientFeature.java
index ffb96409d6ca..0dfa077f57eb 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JmxClientFeature.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JmxClientFeature.java
@@ -25,15 +25,17 @@
*/
package com.oracle.svm.hosted.jdk;
-import org.graalvm.nativeimage.hosted.RuntimeJNIAccess;
-import org.graalvm.nativeimage.hosted.RuntimeReflection;
-
+import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
+import com.oracle.graal.pointsto.meta.AnalysisType;
import com.oracle.svm.core.VMInspectionOptions;
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
import com.oracle.svm.core.feature.InternalFeature;
import com.oracle.svm.core.jdk.JNIRegistrationUtil;
import com.oracle.svm.core.util.VMError;
-import com.oracle.svm.util.ReflectionUtil;
+import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl;
+import com.oracle.svm.hosted.dynamicaccess.JVMCIRuntimeJNIAccess;
+import com.oracle.svm.hosted.dynamicaccess.JVMCIRuntimeReflection;
+import com.oracle.svm.util.JVMCIReflectionUtil;
@AutomaticallyRegisteredFeature
public class JmxClientFeature extends JNIRegistrationUtil implements InternalFeature {
@@ -45,16 +47,19 @@ public boolean isInConfiguration(IsInConfigurationAccess access) {
@Override
public void beforeAnalysis(BeforeAnalysisAccess access) {
try {
- configureJNI();
- configureReflection(access);
+ BeforeAnalysisAccessImpl accessImpl = (BeforeAnalysisAccessImpl) access;
+ configureJNI(accessImpl);
+ configureReflection(accessImpl);
} catch (Exception e) {
throw VMError.shouldNotReachHere("ManagementClientFeature configuration failed: " + e);
}
}
- private static void configureJNI() {
- RuntimeJNIAccess.register(Boolean.class);
- RuntimeJNIAccess.register(ReflectionUtil.lookupMethod(Boolean.class, "getBoolean", String.class));
+ private static void configureJNI(BeforeAnalysisAccessImpl access) {
+ AnalysisMetaAccess metaAccess = access.getMetaAccess();
+ AnalysisType type = metaAccess.lookupJavaType(Boolean.class);
+ JVMCIRuntimeJNIAccess.register(type);
+ JVMCIRuntimeJNIAccess.register(JVMCIReflectionUtil.getDeclaredMethod(metaAccess, type, "getBoolean", String.class));
}
/**
@@ -69,14 +74,14 @@ private static void configureJNI() {
* {@code sun.rmi.server.UnicastRef#getRefClass(ObjectOutput)}.
*
*/
- private static void configureReflection(BeforeAnalysisAccess access) {
- RuntimeReflection.register(access.findClassByName("com.sun.jndi.url.rmi.rmiURLContextFactory"));
- RuntimeReflection.register(access.findClassByName("sun.rmi.server.UnicastRef"));
+ private static void configureReflection(BeforeAnalysisAccessImpl access) {
+ JVMCIRuntimeReflection.register(access.findTypeByName("com.sun.jndi.url.rmi.rmiURLContextFactory"));
+ JVMCIRuntimeReflection.register(access.findTypeByName("sun.rmi.server.UnicastRef"));
- RuntimeReflection.register(access.findClassByName("com.sun.jmx.remote.protocol.rmi.ClientProvider"));
- RuntimeReflection.register(access.findClassByName("com.sun.jndi.url.rmi.rmiURLContextFactory").getConstructors());
- RuntimeReflection.register(access.findClassByName("sun.rmi.server.UnicastRef").getConstructors());
- RuntimeReflection.register(access.findClassByName("sun.rmi.server.UnicastRef2").getConstructors());
- RuntimeReflection.register(access.findClassByName("com.sun.jmx.remote.protocol.rmi.ClientProvider").getConstructors());
+ JVMCIRuntimeReflection.register(access.findTypeByName("com.sun.jmx.remote.protocol.rmi.ClientProvider"));
+ JVMCIRuntimeReflection.register(JVMCIReflectionUtil.getConstructors(access.findTypeByName("com.sun.jndi.url.rmi.rmiURLContextFactory")));
+ JVMCIRuntimeReflection.register(JVMCIReflectionUtil.getConstructors(access.findTypeByName("sun.rmi.server.UnicastRef")));
+ JVMCIRuntimeReflection.register(JVMCIReflectionUtil.getConstructors(access.findTypeByName("sun.rmi.server.UnicastRef2")));
+ JVMCIRuntimeReflection.register(JVMCIReflectionUtil.getConstructors(access.findTypeByName("com.sun.jmx.remote.protocol.rmi.ClientProvider")));
}
}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JmxCommonFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JmxCommonFeature.java
index 9ce2e3505e37..e3d455af573c 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JmxCommonFeature.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JmxCommonFeature.java
@@ -30,15 +30,20 @@
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.dynamicaccess.AccessCondition;
-import org.graalvm.nativeimage.hosted.RuntimeJNIAccess;
-import org.graalvm.nativeimage.hosted.RuntimeReflection;
import org.graalvm.nativeimage.hosted.RuntimeSerialization;
+import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
import com.oracle.svm.core.VMInspectionOptions;
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
import com.oracle.svm.core.feature.InternalFeature;
+import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl;
+import com.oracle.svm.hosted.dynamicaccess.JVMCIRuntimeJNIAccess;
+import com.oracle.svm.hosted.dynamicaccess.JVMCIRuntimeReflection;
import com.oracle.svm.hosted.reflect.proxy.ProxyRegistry;
-import com.oracle.svm.util.ReflectionUtil;
+import com.oracle.svm.util.JVMCIReflectionUtil;
+
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+import jdk.vm.ci.meta.ResolvedJavaType;
@AutomaticallyRegisteredFeature
public class JmxCommonFeature implements InternalFeature {
@@ -125,10 +130,11 @@ public void afterRegistration(AfterRegistrationAccess access) {
@Override
public void beforeAnalysis(BeforeAnalysisAccess access) {
- configureJNI();
- configureSerialization(access);
- configureReflection(access);
- configureProxy(access);
+ BeforeAnalysisAccessImpl accessImpl = (BeforeAnalysisAccessImpl) access;
+ configureJNI(accessImpl);
+ configureSerialization(accessImpl);
+ configureReflection(accessImpl);
+ configureProxy(accessImpl);
}
/**
@@ -156,7 +162,7 @@ public void beforeAnalysis(BeforeAnalysisAccess access) {
*
*
*/
- private static void configureProxy(BeforeAnalysisAccess access) {
+ private static void configureProxy(BeforeAnalysisAccessImpl access) {
ProxyRegistry proxyRegistry = ImageSingletons.lookup(ProxyRegistry.class);
proxyRegistry.registerProxy(AccessCondition.unconditional(), false, access.findClassByName("com.sun.management.GarbageCollectorMXBean"),
access.findClassByName("javax.management.NotificationEmitter"));
@@ -182,9 +188,11 @@ private static void configureProxy(BeforeAnalysisAccess access) {
access.findClassByName("javax.management.NotificationEmitter"));
}
- private static void configureJNI() {
- RuntimeJNIAccess.register(Arrays.class);
- RuntimeJNIAccess.register(ReflectionUtil.lookupMethod(Arrays.class, "asList", Object[].class));
+ private static void configureJNI(BeforeAnalysisAccessImpl access) {
+ AnalysisMetaAccess metaAccess = access.getMetaAccess();
+ ResolvedJavaType type = metaAccess.lookupJavaType(Arrays.class);
+ JVMCIRuntimeJNIAccess.register(type);
+ JVMCIRuntimeJNIAccess.register(JVMCIReflectionUtil.getDeclaredMethod(metaAccess, type, "asList", Object[].class));
}
/**
@@ -202,7 +210,7 @@ private static void configureJNI() {
* the remote JMX infrastructure (See {@code sun.management.MappedMXBeanType},
* {@code com.sun.jmx.mbeanserver.MXBeanMapping#makeOpenClass(Type, javax.management.openmbean.OpenType)})
*/
- private static void configureSerialization(BeforeAnalysisAccess access) {
+ private static void configureSerialization(BeforeAnalysisAccessImpl access) {
String[] classes = {
"[B", "com.oracle.svm.core.jdk.UnsupportedFeatureError",
"java.io.IOException", "java.lang.Boolean", "java.lang.ClassCastException", "java.lang.Error",
@@ -281,7 +289,7 @@ private static void configureSerialization(BeforeAnalysisAccess access) {
* {@code javax.management.remote.rmi.RMIConnectionImpl_Stub}.
*
*/
- private static void configureReflection(BeforeAnalysisAccess access) {
+ private static void configureReflection(BeforeAnalysisAccessImpl access) {
String[] classes = {
"com.sun.management.internal.OperatingSystemImpl",
"javax.management.remote.rmi.RMIConnectionImpl_Stub",
@@ -311,13 +319,49 @@ private static void configureReflection(BeforeAnalysisAccess access) {
};
for (String clazz : classes) {
- RuntimeReflection.register(access.findClassByName(clazz));
+ JVMCIRuntimeReflection.register(access.findTypeByName(clazz));
}
for (String clazz : methods) {
- RuntimeReflection.register(access.findClassByName(clazz).getMethods());
+ registerMethods(access.findTypeByName(clazz));
}
for (String clazz : constructors) {
- RuntimeReflection.register(access.findClassByName(clazz).getConstructors());
+ JVMCIRuntimeReflection.register(JVMCIReflectionUtil.getConstructors(access.findTypeByName(clazz)));
+ }
+ }
+
+ /**
+ * Registers all public methods from the {@code declaringClass} and its super classes and
+ * interfaces for runtime reflection.
+ *
+ * Note: this code originally used {@link Class#getMethods()}, which has slightly different
+ * semantics. For methods with the same signature, it only returns the most specific one. This
+ * code registers all of them. This trades-off registering more methods than really needed for a
+ * much simpler algorithm. Should at some point {@link JVMCIReflectionUtil} or
+ * {@link ResolvedJavaType} get an implementation that behaves as {@link Class#getMethods()}, we
+ * might want to use it.
+ */
+ private static void registerMethods(ResolvedJavaType declaringClass) {
+ if ("java.lang.Object".equals(declaringClass.toClassName())) {
+ /*
+ * Don't register java.lang.Object methods since those are not reflectively needed by
+ * JMX. This a deviation from Class#getMethods().
+ */
+ return;
+ }
+ // Start by fetching public declared methods...
+ for (ResolvedJavaMethod m : declaringClass.getDeclaredMethods(false)) {
+ if (m.isPublic()) {
+ JVMCIRuntimeReflection.register(m);
+ }
+ }
+ // ...then recur over superclass methods...
+ ResolvedJavaType sc = declaringClass.getSuperclass();
+ if (sc != null) {
+ registerMethods(sc);
+ }
+ // ...and finally over direct superinterfaces.
+ for (ResolvedJavaType intf : declaringClass.getInterfaces()) {
+ registerMethods(intf);
}
}
}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JmxServerFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JmxServerFeature.java
index ddbd6a33fe0c..29a7d70e908c 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JmxServerFeature.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JmxServerFeature.java
@@ -35,7 +35,6 @@
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.dynamicaccess.AccessCondition;
-import org.graalvm.nativeimage.hosted.RuntimeReflection;
import com.oracle.svm.configure.ResourcesRegistry;
import com.oracle.svm.core.VMInspectionOptions;
@@ -46,8 +45,12 @@
import com.oracle.svm.core.jdk.RuntimeSupport;
import com.oracle.svm.core.jdk.management.ManagementAgentStartupHook;
import com.oracle.svm.core.jdk.management.ManagementSupport;
-import com.oracle.svm.hosted.reflect.proxy.ProxyRegistry;
import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl;
+import com.oracle.svm.hosted.dynamicaccess.JVMCIRuntimeReflection;
+import com.oracle.svm.hosted.reflect.proxy.ProxyRegistry;
+import com.oracle.svm.util.JVMCIReflectionUtil;
+
+import jdk.vm.ci.meta.ResolvedJavaType;
@AutomaticallyRegisteredFeature
public class JmxServerFeature implements InternalFeature {
@@ -71,7 +74,7 @@ private static void handleNativeLibraries(BeforeAnalysisAccess access) {
public void beforeAnalysis(BeforeAnalysisAccess access) {
handleNativeLibraries(access);
registerJMXAgentResources();
- configureReflection(access);
+ configureReflection((BeforeAnalysisAccessImpl) access);
configureProxy(access);
RuntimeSupport.getRuntimeSupport().addStartupHook(new ManagementAgentStartupHook());
}
@@ -107,7 +110,7 @@ private static void configureProxy(BeforeAnalysisAccess access) {
*
*
*/
- private static void configureReflection(BeforeAnalysisAccess access) {
+ private static void configureReflection(BeforeAnalysisAccessImpl access) {
Set platformManagedObjects = ManagementSupport.getSingleton().getPlatformManagedObjects();
for (PlatformManagedObject p : platformManagedObjects) {
// The platformManagedObjects list contains some PlatformManagedObjectSupplier objects
@@ -115,12 +118,11 @@ private static void configureReflection(BeforeAnalysisAccess access) {
if (p instanceof ManagementSupport.PlatformManagedObjectSupplier) {
continue;
}
- Class> clazz = p.getClass();
- RuntimeReflection.register(clazz);
+ JVMCIRuntimeReflection.register(access.getMetaAccess().lookupJavaType(p.getClass()));
}
- Class> serviceProviderClass = access.findClassByName("com.sun.jmx.remote.protocol.rmi.ServerProvider");
- RuntimeReflection.register(serviceProviderClass);
- RuntimeReflection.register(serviceProviderClass.getConstructors());
+ ResolvedJavaType serviceProviderClass = access.findTypeByName("com.sun.jmx.remote.protocol.rmi.ServerProvider");
+ JVMCIRuntimeReflection.register(serviceProviderClass);
+ JVMCIRuntimeReflection.register(JVMCIReflectionUtil.getConstructors(serviceProviderClass));
}
}
From 9ed4eaf9e0f39e7a4b7b4bef054199e394f69688 Mon Sep 17 00:00:00 2001
From: Josef Eisl
Date: Tue, 4 Nov 2025 13:07:08 +0100
Subject: [PATCH 7/7] svm: migrate ScalaFeature to JVMCI reflection
---
.../svm/polyglot/scala/ScalaFeature.java | 41 ++++++++++---------
1 file changed, 22 insertions(+), 19 deletions(-)
diff --git a/substratevm/src/com.oracle.svm.polyglot/src/com/oracle/svm/polyglot/scala/ScalaFeature.java b/substratevm/src/com.oracle.svm.polyglot/src/com/oracle/svm/polyglot/scala/ScalaFeature.java
index 450d4c4de5ce..e7c272816c08 100644
--- a/substratevm/src/com.oracle.svm.polyglot/src/com/oracle/svm/polyglot/scala/ScalaFeature.java
+++ b/substratevm/src/com.oracle.svm.polyglot/src/com/oracle/svm/polyglot/scala/ScalaFeature.java
@@ -24,23 +24,25 @@
*/
package com.oracle.svm.polyglot.scala;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.function.BooleanSupplier;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.hosted.RuntimeClassInitialization;
-import org.graalvm.nativeimage.hosted.RuntimeReflection;
+import com.oracle.graal.pointsto.meta.AnalysisType;
import com.oracle.svm.core.ParsingReason;
import com.oracle.svm.core.feature.InternalFeature;
import com.oracle.svm.core.util.UserError;
import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl;
+import com.oracle.svm.hosted.dynamicaccess.JVMCIRuntimeReflection;
+import com.oracle.svm.util.JVMCIReflectionUtil;
import com.oracle.svm.util.ModuleSupport;
import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins;
import jdk.graal.compiler.phases.util.Providers;
+import jdk.vm.ci.meta.ResolvedJavaField;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
public class ScalaFeature implements InternalFeature {
@@ -76,8 +78,8 @@ public void registerGraphBuilderPlugins(Providers providers, Plugins plugins, Pa
plugins.appendNodePlugin(new ScalaAnalysisPlugin());
}
- private static boolean isValDef(Field[] fields, Method m) {
- return Arrays.stream(fields).anyMatch(fd -> fd.getName().equals(m.getName()) && fd.getType().equals(m.getReturnType()));
+ private static boolean isValDef(ResolvedJavaField[] fields, ResolvedJavaMethod m) {
+ return Arrays.stream(fields).anyMatch(fd -> fd.getName().equals(m.getName()) && fd.getType().equals(JVMCIReflectionUtil.getResolvedReturnType(m)));
}
/**
@@ -87,27 +89,28 @@ private static boolean isValDef(Field[] fields, Method m) {
private static void initializeScalaEnumerations(BeforeAnalysisAccess beforeAnalysisAccess) {
BeforeAnalysisAccessImpl access = (BeforeAnalysisAccessImpl) beforeAnalysisAccess;
- Class> scalaEnum = access.findClassByName("scala.Enumeration");
+ AnalysisType scalaEnum = access.findTypeByName("scala.Enumeration");
UserError.guarantee(scalaEnum != null, "%s", UNSUPPORTED_SCALA_VERSION);
- Class> scalaEnumVal = access.findClassByName("scala.Enumeration$Val");
+ AnalysisType scalaEnumVal = access.findTypeByName("scala.Enumeration$Val");
UserError.guarantee(scalaEnumVal != null, "%s", UNSUPPORTED_SCALA_VERSION);
- Class> valueClass = access.findClassByName("scala.Enumeration$Value");
+ AnalysisType valueClass = access.findTypeByName("scala.Enumeration$Value");
UserError.guarantee(valueClass != null, "%s", UNSUPPORTED_SCALA_VERSION);
- access.findSubclasses(scalaEnum).forEach(enumClass -> {
+ access.findSubtypes(scalaEnum).forEach(enumClass -> {
/* this is based on implementation of scala.Enumeration.populateNamesMap */
- RuntimeReflection.registerAllDeclaredFields(enumClass);
+ JVMCIRuntimeReflection.registerAllDeclaredFields(enumClass);
// all method relevant for Enums
- Method[] relevantMethods = Arrays.stream(enumClass.getDeclaredMethods())
- .filter(m -> m.getParameterTypes().length == 0 &&
- m.getDeclaringClass() != scalaEnum &&
- valueClass.isAssignableFrom(m.getReturnType()) &&
- isValDef(enumClass.getDeclaredFields(), m))
- .toArray(Method[]::new);
- RuntimeReflection.register(relevantMethods);
+ ResolvedJavaMethod[] relevantMethods = Arrays.stream(enumClass.getDeclaredMethods(false))
+ .filter(m -> m.getParameters().length == 0 &&
+ !scalaEnum.equals(m.getDeclaringClass()) &&
+ valueClass.isAssignableFrom(JVMCIReflectionUtil.getResolvedReturnType(m)) &&
+ (isValDef(enumClass.getInstanceFields(false), m) ||
+ isValDef(enumClass.getStaticFields(), m)))
+ .toArray(ResolvedJavaMethod[]::new);
+ JVMCIRuntimeReflection.register(relevantMethods);
try {
- RuntimeReflection.register(scalaEnumVal.getDeclaredMethod("id"));
- } catch (NoSuchMethodException e) {
+ JVMCIRuntimeReflection.register(JVMCIReflectionUtil.getDeclaredMethod(access.getMetaAccess(), scalaEnumVal, "id"));
+ } catch (NoSuchMethodError e) {
throw UserError.abort("%s", UNSUPPORTED_SCALA_VERSION);
}
});